본문 바로가기

세상 바라보는 시선 👁/Growth Hacking

[Python] Flask를 이용하여 서비스 운영


repl.it

from flask import Flask  # 대소문자 주의
from flask import render_template #html을 가져와주기 위해서 부름, Flask import 뒤에 ','로도 연결할 수 있음
from flask import request #query문을 가져오기 위해서 import
from flask import redirect #다른 페이지로 넘기기 위해 
from scrapper import get_jobs #이전에 만들어둔 곳에서 함수를 가져옴
from exporter import save_to_file csv 파일로 서버에 저장시키기
from flask import send_file #파일로 다운로드 하게 하기
app = Flask("Fantastic Scarpper")

#init 할 때 db를 생성해두는 것
db = {}


# (1)  Dynamic URL
# / 로 접속하면 function 을 작동하도록
# @decorator는 바로 아래 있는 함수를 찾게 된다.
# "/"로 접속 요청이 들어오면 아래 function!!(function만!)을 시행하면 되는 구나 라고 생각함.
@app.route("/")
def home():
  return  render_template("potato.html")
  #1 templates 폴더에 potato.html 를 FLASK가 알아서 찾아냄

@app.route("/report")
def report():
  # print(request.args) #요청된 자료에서 argument들을 Dictionary로 출력해낸다
  keyword = request.args.get("search") #"search"에 해당되는 potato.html에 있는 input query 
  if keyword: #keyword가 있다면
    keyword = keyword.lower()
    existingJobs= db.get(keyword)
    if existingJobs : #db에 keyword가 있는지 확인
      jobs = existingJobs
    else: 
      jobs = get_jobs(keyword) #scrapper.py 에서 가져온 함수
      db[keyword] = jobs #검색 결과를 db에 저장해두어 빠르게 함
  else:  #keyword 입력이 안되면( 검색어 없이 들어오면) home 으로 redirect
    return redirect("/") 

  return render_template("report.html", searchingBy = keyword, resultsNumber=len(jobs), results = jobs) # html 파일에 {{}}을 이용하면, html파일에서 인식 가능 

# ※ html을 가져오는 것은 reder_template로 가져오고, 사용할 데이터는 {{}} 로서 미리 정해둔다.

@app.route("/export")
def export():
  try:
    keyword = request.args.get('search')
    if not keyword:
      raise Exception() #except로 보냄
    keyword = keyword.lower()
    jobs = db.get(keyword)
    if not jobs:
      raise Exception() 
    save_to_file(jobs)
    return send_file("jobs.csv")
  except:
    return redirect("/")

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


@app.route("/<username>")
def potato(username):
  return f"Hello {username}. How are you doing?"


app.run(host="0.0.0.0")  #웹사이트 구축, local에서 작동한다면, host를 "0.0.0.0"은 작동하지 않는다.

potato.html - Templates 폴더에 넣어야 함

<!DOCTYPE html>
<html>
  <head>
    <title> Job searcher, Web scrapper</title>

  </head>
  <body>
    <h1> Indeed 직업 검색기</h1>
    <form action = "/report" method ="get" >  <!-- report?word= blahblach 로 기록됨-->
      <input placeholder="직업을 입력하세요" name ="search" required/> 
      <button> 검색</button>
      
    </form>
  </body>
</html>
      

report.html - Templates 폴더에 넣어야 함

html 파일에 변수를 {{}}로 감싸서 rendering 할 수 있다. 

<!DOCTYPE html>
<html>
  <head>
    <title> Job searcher, Web scrapper</title>
    <style>
        section{
            display:grid;
            gap : 20px;
            grid-template-columns: repeat(4,1fr);
            border : 1px solid green;
        }
    </style>
  </head>
  <body>
    <h1> 검색 결과</h1>
      <div> 
        당신이 검색한 것은 {{searchingBy}}
      </div>
      <div> 검색 수 : {{resultsNumber}} </div>
      <div> <a href ="/export?search={{searchingBy}}" targe="_blank" >Export </a></div>

      <section>
        <h4>Title</h4>
        <h4>Company</h4>
        <h4>Location</h4>
        <h4>Link</h4>
     
        {%for job in results %} 
          <span>{{job.title}}</span>
          <span>{{job.company}}</span>
          <span>{{job.location}}</span>
          <a href="{{job.link}}" target="_blank">Link</a>
        {% endfor %} 
  </body>
</html>
      

scrapper.py

import requests
# 좌측 메뉴에서 packge에 들어가서 request를 설치한다. : pip install requests
from bs4 import BeautifulSoup #beautiful Soup <- S 대문자여야 하낟
# Beautifulsoup4 로 다운로드 한다. html scrapper package : pip install bs4 , from bs4 import BeautifulSoup
################################
def extract_indeed_pages(url):
  url_result = requests.get(url)

  # print(indeed_result) # 출력 : <Response[200]>
  # print(indeed_result.text) # 출력 모든게 다나옴

  soup = BeautifulSoup(url_result.text,"html.parser")
  #class 
  pagination = soup.find("div", {"class":"pagination"})

  links = pagination.find_all('a') #links는 list로 만들어진다.

  pages = [] #span list를 담을 그릇 

  for link in links[:-1] : #[:-1] 뒤에 마지막 것 제외하고 출력하기 
    pages.append(int(link.find("span").string)) #.string을 이용해서 text만 가져옴, 가져온 text를 int로 바꿔줌
    
  # 가장 마지막 숫자를 찾는다.
  max_page = pages[-1]
  return max_page


# TITLE  가져오고 각각 REQUEST 보냄
def extract_indeed_jobs(last_page,url,limit):
  jobs = []
  for page in range(1):
    print(f"스크랩해온 페이지는 {page+1}/{last_page}입니다")
    result = requests.get(f"{url}&start={page*limit}") #변수끼리 계산할 것이 있으면 계산할 것 하고 {}로 닫아준다
    print(result.status_code) #반응하는지 확인, 200이 20개 나와줘야함.
    soup = BeautifulSoup(result.text,"html.parser")
    # 제목 division을 찾아온다
    results = soup.find_all("div", {"class":"jobsearch-SerpJobCard"})
    
    # 각 페이지에서 List 로 생성된 results를 for 구문으로 돌린다.
    for result in results : 

      #title을 추출함
      title = result.find("div", {"class":"title"}).find("a")["title"] #list에서 title value 에 배정된 값을 가져와
      print(title)
      #company 이름을 추출함
      company = result.find("span",{"class":"company"})
      if company is not None:
        company_anchor = company.find("a")
            # ※참고※  만약 span이 2개로 나온다면 | company, anchor = html.find("h3").find_all("span",recursive = False) | 와 같이 각각을 한번에 담을 수도 있다
        if company_anchor is not None: #anchor가 있으면 anchor 내용을 가져온다.
          company = company_anchor.string
        else:
          company = company.string  
          print("☆no anchor",company)
         # 여백이 너무 많아서 .strip()을 이용하여 지워줌
      else:
        company = "none"
      company = company.strip()
      #Location 추가 함
      location = result.find("div",{"class":"recJobLoc"})["data-rc-loc"] #data-rc-loc에 배정된 내용을 가져온다.

      #job_id 추가함  : 다른 페이지 링크로 연결됨
      job_id = result["data-jk"]

      # 전체 다 가지는 배열 만듦
      
      job = {
        'title':title, 
        'company':company, 
        'location':location, 
        'link': f"https://www.indeed.com/viewjob?jk={job_id}"
        } #job list에 하나씩 쌓아줌. 
      print(job)  
      jobs.append(job) 
  return jobs
###################################

def get_jobs(keyword):
  limit = 50
  url = f"https://www.indeed.com/jobs?q={keyword}&limit={limit}"
  max_indeed_page = extract_indeed_pages(url) #URL을 다른 함수로 보내줘야함, 그래서 url로 바꿔줌 이전엔 전역에서 지정해줬으나, 함수이므로, 각각 넣어줘야함.
  jobs = extract_indeed_jobs(max_indeed_page,url,limit)
  return jobs

exporter.py

import csv
def save_to_file(jobs):
  file = open("jobs.csv", mode="w")
  writer = csv.writer(file)
  writer.writerow(["title","company","location","link"]) #상단 1행
  for job in jobs:
    writer.writerow(list(job.values()))
  return
♡를 눌러주시면 블로그를 작성하는데 큰 힘이 됩니다♪
로그인이 필요없어요.

이 블로그 인기글