์•ˆ๋…•ํ•˜์„ธ์š”:) ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํŒŒ์ด์ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ ํ”Œ๋ผ์Šคํฌ์—์„œ ๋™์ž‘ํ•˜๋Š” ๋„ค์ด๋ฒ„ ์ง€๋„ ํฌ๋กค๋ง ์„œ๋ฒ„ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ง€๋‚œ ์†Œ๊ฐœ(1ํƒ„) ํฌ์ŠคํŒ…์—์„œ ๋ง์”€๋“œ๋ฆฐ ๊ฒƒ์ฒ˜๋Ÿผ, ํฌ๋กค๋ง ์ง„ํ–‰์‹œ ๊ณ ๋ คํ•ด๋ณด๋ฉด ์ข‹์€ ๋•๋ชฉ๊ณผ ํฌ๋กค๋Ÿฌ ์‹œ์ ์—์„œ ๋ณธ ๋„ค์ด๋ฒ„ ์ง€๋„์˜ ํŠน์ง•์„ ์†Œ๊ฐœํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.



1. ํฌ๋กค๋ง ์„œ๋ฒ„ํ™˜๊ฒฝ ๊ตฌ์ถ•

(์ด ํฌ์ŠคํŒ… ์‹œ๋ฆฌ์ฆˆ๋Š” mac os๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.)

๋จผ์ €, ํŒŒ์ด์ฌ์ด ์„ค์น˜๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ํŒŒ์ด์ฌ์„ ์„ค์น˜ํ•ด์ค๋‹ˆ๋‹ค. (์นจ๊ณ  ๋งํฌ: https://programmers.co.kr/learn/courses/2/lessons/48) ํŒŒ์ด์ฌ ๋ฒ„์ „ํ™•์ธ(3.4์ด์ƒ ํ•„์š”)

python --version


  • ํŒŒ์ด์ฌ ๊ฐ€์ƒํ™˜๊ฒฝ ๊ตฌ์ถ• ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์—์„œ๋งŒ ๊ท€์†๋˜๊ฒŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•˜๊ธฐ ์œ„ํ•ด venv๊ฐ€์ƒํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ด ์ค๋‹ˆ๋‹ค.
pip install virtualenv //์„ค์น˜
virtualenv ํด๋”์ด๋ฆ„ //์ƒ์„ฑ
source ํด๋”์ด๋ฆ„/bin/activate //์ ‘์†


  • ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ํฌ๋กค๋ง์— ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ๋Š” ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋ช‡๊ฐ€์ง€ ์—†์–ด ์ง์ ‘ pip install๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์„ค์น˜๋ฅผ ์ง„ํ–‰ํ•˜์ง€๋งŒ db์—ฐ๊ฒฐ, ์Šค์ผ€์ค„๋Ÿฌ ์„ค์ • ๋“ฑ์ด ์ถ”๊ฐ€๋œ๋‹ค๋ฉด ์„ค์น˜ํ•ด์•ผํ•  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋งŽ์•„์ง‘๋‹ˆ๋‹ค. ์ด๋•Œ๋Š” requirements.txtํŒŒ์ผ์— ์„ค์น˜ํ•  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ช…์‹œํ•ด๋‘๊ณ  ํ•œ ๋ฒˆ์— ์„ค์น˜ํ•˜์—ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ๋ฐ ๋ฒ„์ „ ๊ด€๋ฆฌ๋ฅผ ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
pip install flask beautifulsoup4 selenium webdriver-manager


์„ค๋ช…

  • flask: ํŒŒ์ด์ฌ ์›น ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ํฌ๋กค๋ง ์„œ๋ฒ„ ๊ตฌ์ถ• ํ‹€
  • beautifulsoup4: html parser๋กœ, ์›นํŽ˜์ด์ง€ ํฌ๋กค๋ง์‹œ, htmlํƒœ๊ทธ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ์ถ”์ถœํ•˜๊ธฐ ์œ„ํ•จ
  • selenium: ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํฌ๋กค๋งํ•˜๊ธฐ ์œ„ํ•จ, ๋งˆ์šฐ์Šค, ํ‚ค๋ณด๋“œ ๋“ฑ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋ง์„ ์‰ฝ๊ฒŒ ์œ„ํ•จ
  • webdriver-manager: ํฌ๋กค๋ง์„ ์‹œ๋„ํ•  ์›น ๋ธŒ๋ผ์šฐ์ €์ธ ํฌ๋กฌ์„ ์ปจํŠธ๋กคํ•˜๊ธฐ ์œ„ํ•จ


ํ”Œ๋ผ์Šคํฌ ์„œ๋ฒ„ ์‹คํ–‰

app.py

import argparse
from flask import Flask

# ์‹คํ–‰ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •
parser = argparse.ArgumentParser()
parser.add_argument('--env',required=False, default='development')
args = parser.parse_args()

# flask app ์ƒ์„ฑ
app = Flask(__name__)

if __name__ == '__main__':
  app.run(host='0.0.0.0', port='3060', debug=True, use_reloader=True)

In terminal

python app.py


2. ํฌ๋กค๋ง์‹œ ์•Œ์•„๋‘๋ฉด ์ข‹์€ 3๊ฐ€์ง€ ์š”์†Œ

  • ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์ƒํ™ฉ์— ๋Œ€ํ•œ ์ „์ฒ˜๋ฆฌ ์ง„ํ–‰
  • sleep์˜ ํ•„์š”์„ฑ
  • Iframe๊ฐ„ ์ด๋™

์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์ƒํ™ฉ์— ๋Œ€ํ•œ ์ „์ฒ˜๋ฆฌ ์ง„ํ–‰

์›นํŽ˜์ด์ง€ ํฌ๋กค๋ง์„ ํ•˜๋‹ค๋ณด๋ฉด ์ƒ๊ฐ์ง€๋„ ๋ชปํ•œ ๋ถ€๋ถ„์—์„œ ๋™์ž‘์ด ๋ฉˆ์ถ”๊ฑฐ๋‚˜ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ๊ฐ•์ œ์ข…๋ฃŒ๋˜๋Š” ์ผ์ด ์ข…์ข… ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋งŽ์€ ์ด์œ ๊ฐ€ ์žˆ์ง€๋งŒ, ํฌ๊ฒŒ 3๊ฐ€์ง€ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  1. ul > li ๋‚ด๋ถ€์—์„œ ์„œ๋กœ๋‹ค๋ฅธ ๊ฐ€๋ณ€์  ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ
  2. ์›น์‚ฌ์ดํŠธ ๋‚ด๋ถ€ html ํƒœ๊ทธ์˜ ํ˜•์‹์ด๋‚˜ ์ด๋ฆ„์ด ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ
  3. ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์ด ๋ถˆ์•ˆ์ •ํ•˜์—ฌ ๋กœ๋”ฉ์‹œ๊ฐ„์ด ๊ธธ์–ด์งˆ๋•Œ๋‚˜ ๋Š์ผฐ์„ ๋•Œ ์œ„ ์ƒํ™ฉ์—์„œ ์ผ๋‹จ ํฌ๋กค๋ง์ด ๊ณ„์† ์ง„ํ–‰๋˜์–ด ์ž‘์„ฑํ•ด๋‘” ๋ฐ˜๋ณต๋ฌธ์„ ์ผ๋‹จ ๋๊นŒ์ง€ ๋Œ๋ฆฌ๊ฑฐ๋‚˜ ๋‹ค์Œ ํ•ญ๋ชฉ์œผ๋กœ ๋„˜์–ด๊ฐ€๋Š” ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํฌ๋กค๋ง์ฝ”๋“œ๋Š” ์ฃผ๊ธฐ์ ์œผ๋กœ ๋Œ๋ ค๋ณด๋ฉฐ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์‹คํŒจ ์ผ€์ด์Šค๋ฅผ ์ฐพ์•„๋‚ด๊ณ , ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•ฉ๋‹ˆ๋‹ค.

sleep์˜ ํ•„์š”์„ฑ

์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ํฌ๋กค๋ง ์„œ๋ฒ„๋ฅผ ์‹คํ–‰์‹œ์ผฐ์„ ๋•Œ, ์‹ค์ œ ์ฝ”๋“œ ์‹คํ–‰ ์†๋„์™€ ์›น ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ ์ „์†ก ์‹œ๊ฐ„ ์ฐจ์ด๋กœ ์ธํ•ด ๋Ÿฐํƒ€์ž„์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š”, ๋ธŒ๋ผ์šฐ์ € ๋ Œ๋”๋ง ์—”์ง„์— ์˜ํ•œ html ํŒŒ์‹ฑ์ด ์ด๋ฃจ์–ด ์ง€๊ธฐ ์ „์—, ์ด๋ฏธ beautifulsoup์—์„œ html์„ parserํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ํŒŒ์ด์ฌ ๋‚ด๋ถ€ํ•จ์ˆ˜์ธ time์˜ ๋ชจ๋“ˆ ์ค‘ ์ฝ”๋“œ ์‹คํ–‰ ์ง€์—ฐ ๋ชจ๋“ˆ์€ sleep()์„ ํ†ตํ•ด ์‹ค์ œ ์ฝ”๋“œ ์‹คํ–‰์†๋„๋ฅผ ์›น์„œ๋ฒ„์™€ ๋งž์ถ”๋Š” ๊ฒƒ์ด ํ•ด๊ฒฐ์ฑ… ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ด๋•Œ, time.sleep()ํ•จ์ˆ˜๋กœ ์ง€์—ฐํ•  ์‹œ๊ฐ„์€ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ณด๋ฉฐ ์ ์ ˆํ•œ ์‹œ๊ฐ„์—์„œ ์กฐ๊ธˆ ๋„๋„ํ•œ ์‹œ๊ฐ„(+1~3์ดˆ)์„ ์„ค์ •ํ•ด ๋†“๋Š” ๊ฒƒ์„ ์ถ”์ฒœ ๋“œ๋ฆฝ๋‹ˆ๋‹ค.


Iframe๊ฐ„ ์ด๋™

Iframe์€ HTML inline Frame ์š”์†Œ๋กœ, ํ•˜๋‚˜์˜ ์‚ฌ์ดํŠธ์— 2๊ฐœ ์ด์ƒ์˜ ํŽ˜์ด์ง€๋ฅผ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

iframeDescription.png

๋„ค์ด๋ฒ„ ์ง€๋„์—์„œ๋Š” ํ˜„์žฌ ์œ„ ์‚ฌ์ง„์ฒ˜๋Ÿผ ์ด 2๊ฐœ์˜ Iframe์ด ์‚ฝ์ž…๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.


  • ๋นจ๊ฐ„์ƒ‰ ํ…Œ๋‘๋ฆฌ: ๊ฒ€์ƒ‰ ๋ฐ ๊ฒฐ๊ณผ ์„น์…˜ Iframe
  • ํŒŒ๋ž€์ƒ‰ ํ…Œ๋‘๋ฆฌ: ๊ฒ€์ƒ‰ ์ƒ์„ธ ์„น์…˜ Iframe ์ด๋ ‡๊ฒŒ ํ•œ ์‚ฌ์ดํŠธ์— ์—ฌ๋Ÿฌ Iframe์ด ์กด์žฌํ•˜์—ฌ Iframe๊ฐ„ html ํƒœ๊ทธ ์„ ํƒ์„ ๋„˜๋‚˜๋“ค๋•Œ, Iframe ์ „ํ™˜์„ ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. Iframe ์ „ํ™˜์€ ์œ„์—์„œ ์†Œ๊ฐœํ•œ webdriver์˜ ๋ชจ๋“ˆ ์ค‘์— switch_to ๋‚ด๋ถ€ํ•จ์ˆ˜๋กœ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Iframe ์ „ํ™˜์€ ์‹ค์ œ ํฌ๋กค๋ง์„ ์ง„ํ–‰ํ•ด๋ณผ ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


3. ๋‹ค์Œ ํฌ์ŠคํŒ… ๋ฏธ๋ฆฌ๋ณด๊ธฐ

๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋„ค์ด๋ฒ„ ์ง€๋„์—์„œ ํ‚ค์›Œ๋“œ๋ฅผ ํ†ตํ•œ ์ง€์—ญ ๊ฒ€์ƒ‰์„ ์ง„ํ–‰ํ•œ ๋’ค, ๊ฒ€์ƒ‰๊ฒฐ๊ณผ ๋ฐ์ดํ„ฐ ํฌ๋กค๋ง์œผ๋กœ ํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์ณ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


์ž์„ธํ•œ ์ฝ”๋“œ๋Š” ์—ฌ๊ธฐ์—