누구나 할 수 있는 크롤링 #1
동생이 특정 브랜드의 옷을 좋아하는데, 항상 세일을 늦게 알게 되어서 구매를 놓친다고 했다. 그래서 동생을 위해 간단한 웹크롤링을 적용한 프로젝트를 해보기로 했다.
어떻게 구현할 수 있을지 생각해본다.
- 내가 액션을 하지 않아야 함
- 화면을 자동으로 읽어야 함
- 이 읽은 데이터 중에 내가 원하는 부분을 나에게 알려줘야 함
그럼 이 문제를 해결할 수 있는 기술은 이정도가 될 것 같다
- 특정 시간마다 자동으로 스크립트를 돌릴 crontab 또는 스케줄러
- html parsing
- 내 핸드폰으로 서머리를 전송
그럼 필요한 tech spec 은,
- scheduler — firebase function
- html parsing — python + beautifulsoup
- send summary messages — slack api
이정도면 꽤 어렵지 않게 구현할 수 있을 것 같다.
작업을 시작해보자.
파싱할 페이지를 찾는다.
나는 동생이 좋아하는 옷의 홈페이지 중 세일 페이지를 선택했다
개발환경을 구성해보자
요즘 vscode로 python 개발을 많이하니 다운로드를 한다.
다운로드 이후에 python extension도 설치해준다.
python SDK 버전은 3.11.6으로 했다.
⚠️ 현재 python 3.12가 최신이지만, 나중에 사용할 firebase function에서 python 3.11까지만 지원하고 있어서 3.11.6으로 설정했다.
firebase function 환경설정
File -> New File 을 선택한 후에 파일 이름을 “test.py”로 하고
print("Hello World!")
그 이후에 상단 오른쪽 ▶ 버튼을 클릭해서 실행해보자!
하단 화면에 Hello World! 가 프린트되는 것을 확인할 수 있다.
python 라이브러리 선택
- requests — http 통신을 쉽게 해주는 라이브러리
- beautifulsoup4 — html 파싱을 해주는 라이브러리
$ python3 -m pip install requests==2.31.0
$ pyyhon4 -m pip install beautifulsoup4==4.12.2
라이브러리를 설치하고 이제 함수를 만들어보자.
def hello_world():
print("Hello, world!")
return
hello_world()
이제 기본적인 준비는 다 되었다.
requests lib를 통해 html을 잘 가져오는지 확인해보자.
import requests
def check_web():
url = "{내가 파싱하려는 url}"
page = requests.get(url)
print(page.text) # <- html이 나오는 것을 확인할 수 있다
check_web()
html도 잘 나오는 것을 확인했다!
이제 html의 어느 부분을 값을 확인해야하는지를 찾아야 한다. 크롬으로 웹페이지를 열고 F12
를 클릭하면 오른쪽에 html이 나오는 것을 확인할 수 있다. 여기서 내가 원하는 부분을 왼쪽 화면에서 클릭하면 html 중 어느 부분에서 이 영역을 표시하는지 확인할 수 있다.
<div class="info">
...
<em class="sale">
...
"30%" <-- <em> 태그안의 텍스트를 가져오면 몇 퍼센트 할인을 하는지 알 수 있다
</em>
</div>
그런데 우리는 이 화면에 나오는 모든 리스트 중 30%이상의 아이템만 뽑고 싶기 때문에 각 아이템들을 전부 탐색해야한다. 그럼 ▼ 아래로 펼쳐져있는 세모 아이콘을 클릭해서 한 번 접어보자. 그럼 다음처럼 모든 리스트를 볼 수 있다.
<div class="list_goods">
<ul>
<li>첫번째 아이템</li>
<li>두번째 아이템들</li>
...
</ul>
</div>
결국 list_goods를 찾아서 그 안의 아이템들을 모두 한 번씩 확인하면 우리가 원하는 30%이상 할인하는 아이템을 찾을 수 있다!
import requests
from bs4 import BeautifulSoup # <- 이제 beatifulsoup을 이용해서 html을 파싱해보자
def check_web():
url = "{내가 파싱하려는 url}"
page = requests.get(url)
soup = BeautifulSoup(page.text, 'html.parser')
# select로 .list_goods 아래 ul 아래 li의 리스트를 받을 수 있다
list = soup.select('.list_goods > ul > li')
for good in list:
print(good) # 여기까지 잘 왔나 한 번 아이템을 프린트 해본다
check_web()
너무 많이 나와서 뭔가 찍히는 것 같다. 그럼 아이템 이름과 가격을 한 번 print 해보자.
import requests
from bs4 import BeautifulSoup # <- 이제 beatifulsoup을 이용해서 html을 파싱해보자
def check_web():
url = "{내가 파싱하려는 url}"
page = requests.get(url)
soup = BeautifulSoup(page.text, 'html.parser')
list = soup.select('.list_goods > ul > li')
for good in list:
name = good.select('.name')
sale = good.find('span', {'class':'price'}) # span tag 중에 class=price를 찾는다
print("name =", name) # 각 아이템의 이름
print("sale =", sale) # 각 아이템의 가격
check_web()
good.find() 를 쓰면 good 객체 아래로 하위 태그들을 array로 접근할 수 있게 해준다.
sale.contents[2] # 395,000
sale.contents[7] # 할인율 30%
이제 파싱은 거의 끝났다.
won = sale.contents[2].get_text().strip() # \t를 없애주기 위에 strip()을 썼다
percentage = sale.contents[7] # 할인율 30%
discount = sale.contents[7].contents[1] # 30%까지 찾는다
30%에서 30만 뽑아서 할인율을 체크해야하므로, 숫자를 뽑아내는 함수를 하나 추가한다.
def extract_number(text):
"""Extracts the number from a string.
Args:
text: A string.
Returns:
The number extracted from the string, or None if no number is found.
"""
match = re.search(r'\d+', text)
if match:
return int(match.group())
else:
return None
discount = extract_number(discount) # 30이라는 숫자가 나오고
if discount >= 30:
# 이 아이템을 이제 슬랙으로 전송하면 된다
슬랙으로 전송하는 부분은 다음 포스팅으로 ..