Flask

Python으로 웹사이트를 만들 수 있게 해주는 micro-framework
공식문서 / 한글화된 공식문서

url에 따른 구조

폴더 구조

venv 폴더와 requirements.txt 파일은 가상환경 사용하면서 생성함


완성 코드

main.py

from flask import Flask, render_template, request, redirect, send_file
from scrapper import get_jobs, save_to_file

app = Flask("SuperScrapper")

db = {}

# 데코레이터를 통해, 해당 루트로 접근하면 함수가 자동으로 실행됨
# 데코레이터 아래에는 함수만! 변수는 X
@app.route('/')
def home():
    return render_template('home.html')

@app.route('/contact')
def contact():
    return 'Contact me!'

# Dynamic URL
@app.route('/<username>')
def greeting(username):
    return f'Hello {username}, how are you?'

# Query Arguments
@app.route('/report')
def report():
    word = request.args.get('word')
    if word:
        word = word.lower()
        from_db = db.get(word)
        if from_db:
            jobs = from_db
        else:
            jobs = get_jobs(word)
            db[word] = jobs
    else:
        return redirect('/')
    return render_template('report.html', resultsCount=len(jobs), word=word, jobs=jobs)

@app.route('/export')
def export():
    try:
        word = request.args.get('word')
        if not word:
            # exception(error)이 발생하면 try 블록 중단하고 except 블록 실행
            raise Exception()
        word = word.lower()
        jobs = db.get(word)
        if not jobs:
            raise Exception()
        save_to_file(jobs)
        return send_file('jobs.csv')
    except:
        return redirect('/')

app.run()

scrapper.py

import requests    # url로 get 요청 보내서 html 받아오기
from bs4 import BeautifulSoup    # 응답으로 받은 html에서 필요한 데이터 추출하기

import csv

# -----------------------------------------------------
# Part 1. Scrapping

def extract_jobkorea_pages(url):
    response = requests.get(url)

    # 마지막 페이지 번호가 안 보이기 때문에 전체 공고 수를 이용
    soup = BeautifulSoup(response.text, 'html.parser')
    dev_tot = soup.find('strong', {'class': 'dev_tot'}).string
    counts = ''

    for item in dev_tot:
        if '0' <= item <= '9':
            counts += item

    counts = int(counts)
    # 한 페이지에 보여지는 공고 수 20을 이용하여 마지막 페이지 번호 구하기
    max_page = counts // 20 + 1 if counts % 20 != 0 else counts // 20
    return max_page


def extract_jobkorea_jobs(max_page, url):
    jobs = []

    for page in range(1, max_page + 1):
        print(f'Scrapping page {page}')
        response = requests.get(f'{url}&Page_No={page}')
        soup = BeautifulSoup(response.text, 'html.parser')
        posts = soup.find_all('div', {'class': 'post'})
        for post in posts:
            # 한 페이지의 posts 중 공고 post를 다 가져온 뒤 공고가 아닌 post를 만나면 종료
            try:
                title = post.find('a', {'class': 'title'})['title'].strip()
                company = post.find('a', {'class': 'name'})['title'].strip()
                location = post.find('span', {'class': 'loc long'}).string.strip()
                link = post.find('a', {'class': 'title'})['href'].strip()
                # 잡코리아 링크의 일부분이 아닌 전체 링크를 가지고 있을 경우 대비
                if link[:4] != 'http':
                    link = "https://www.jobkorea.co.kr" + link
                job = {'title': title, 'company': company, 'location': location, 'link': link}
                jobs.append(job)
            except:
                break

    return jobs

# --------------------------------------------------------
# Part 2. Saving to CSV file
# Comma Seperated Value, column은 ,로 row는 new line으로 구분
# excel 프로그램이 있어야 하는 xls와 달리 다양한 환경에서 사용할 수 있음

def save_to_file(jobs):
    file = open('jobs.csv', mode="w")    # 쓰기 전용으로 파일 열기
    writer = csv.writer(file)
    writer.writerow(['title', 'company', 'location', 'link'])
    for job in jobs:
        writer.writerow(list(job.values()))
    return


# --------------------------------------------------------
# Part 3. Run

def get_jobs(word):
    JOBKOREA_URL = f"https://www.jobkorea.co.kr/Search/?stext={word}&tabType=recruit"
    last_jobkorea_page = extract_jobkorea_pages(JOBKOREA_URL)
    jobkorea_jobs = extract_jobkorea_jobs(last_jobkorea_page, JOBKOREA_URL)
    return jobkorea_jobs

templates/home.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Job Search</title>
</head>
<body>
  <h1>Job Search</h1>
  <form action="/report" method="get">
    <input type="text" placeholder="Which job do you want to search?" required name="word">
    <button>Search</button>
  </form>
</body>
</html>

templates/report.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Job Search</title>
  <style>
    section {
      display: grid;
      gap: 20px;
      grid-template-columns: repeat(4, 1fr);
    }
  </style>
</head>
<body>
  <h1>Search Results</h1>
  <h3>Found {{resultsCount}} results for {{word}}</h3>
  <a href="/export?word={{word}}">Export to CSV</a>
  <section>
    <h4>Title</h4>
    <h4>Company</h4>
    <h4>Location</h4>
    <h4>Link</h4>
    {% for job in jobs %}
      <span>{{job.title}}</span>
      <span>{{job.company}}</span>
      <span>{{job.location}}</span>
      <a href="{{job.link}}" target="_blank">Apply</a>
    {% endfor %}
  </section>
</body>
</html>

결과물

/

/report?word=Vue

/export?word=Vue


jobs.csv
246.1 kB

※ 강의 링크: https://nomadcoders.co/python-for-beginners

'Clone Coding > Python으로 웹 스크래퍼 만들기' 카테고리의 다른 글

#3 Get Ready for Django(More about Python)  (0) 2022.07.04
#2 Building a Web Scrapper  (0) 2022.07.04
#1 Python Theory  (0) 2022.07.04

+ Recent posts