← Back to list

누구나 할 수 있는 크롤링 #1

누구나 할 수 있는 크롤링 #2

Eugene Lim · 2024-07-20 02:34 · 0 claps · 7.5 min read
#python #crawling #firebase-functions
Open on Medium ↗

누구나 할 수 있는 크롤링 #1

누구나 할 수 있는 크롤링 #2

동생이 특정 브랜드의 옷을 좋아하는데, 항상 세일을 늦게 알게 되어서 구매를 놓친다고 했다. 그래서 동생을 위해 간단한 웹크롤링을 적용한 프로젝트를 해보기로 했다.

어떻게 구현할 수 있을지 생각해본다.

  1. 내가 액션을 하지 않아야 함
  2. 화면을 자동으로 읽어야 함
  3. 이 읽은 데이터 중에 내가 원하는 부분을 나에게 알려줘야 함

그럼 이 문제를 해결할 수 있는 기술은 이정도가 될 것 같다

  • 특정 시간마다 자동으로 스크립트를 돌릴 crontab 또는 스케줄러
  • html parsing
  • 내 핸드폰으로 서머리를 전송

그럼 필요한 tech spec 은,

  1. scheduler — firebase function
  2. html parsing — python + beautifulsoup
  3. send summary messages — slack api

이정도면 꽤 어렵지 않게 구현할 수 있을 것 같다.

작업을 시작해보자.

파싱할 페이지를 찾는다.

나는 동생이 좋아하는 옷의 홈페이지 중 세일 페이지를 선택했다

https://www.ssfshop.com/Maison-Kitsune/On-Sale/list?dspCtgryNo=SFMA33&brandShopNo=BDMA07A22&brndShopId=BQMKT

https://www.ssfshop.com/Maison-Kitsune/On-Sale/list?dspCtgryNo=SFMA33&brandShopNo=BDMA07A22&brndShopId=BQMKT

개발환경을 구성해보자

요즘 vscode로 python 개발을 많이하니 다운로드를 한다.

[embed]Visual Studio Code - Code Editing. Redefined Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud…code.visualstudio.com

아래에서 2번째 아이콘(사각형이 4개 있는 아이콘 — 익스텐션)

아래에서 2번째 아이콘(사각형이 4개 있는 아이콘 — 익스텐션)

다운로드 이후에 python extension도 설치해준다.

python SDK 버전은 3.11.6으로 했다.

⚠️ 현재 python 3.12가 최신이지만, 나중에 사용할 firebase function에서 python 3.11까지만 지원하고 있어서 3.11.6으로 설정했다. [firebase function 환경설정](https://firebase.google.com/docs/functions/get-started?hl=ko&_gl=15vqoe6_upMQ.._gaMTg4NjA4MjA3LjE3MjE0MzU0NDQ._ga_CW55HF8NVTMTcyMTQzNTQ0NC4xLjAuMTcyMTQzNTQ0NC4wLjAuMA..&gen=2nd#set-up-your-environment-and-the-firebase-cli)*

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 중 어느 부분에서 이 영역을 표시하는지 확인할 수 있다.

여기서 30%할인이 표시되는 아이템들의 리스트를 찾고 싶다

여기서 30%할인이 표시되는 아이템들의 리스트를 찾고 싶다

<div class="info">
  ...
  <em class="sale">
    ...
    "30%" <-- <em> 태그안의 텍스트를 가져오면 몇 퍼센트 할인을 하는지 알 수 있다
  </em>
</div>

그런데 우리는 이 화면에 나오는 모든 리스트 중 30%이상의 아이템만 뽑고 싶기 때문에 각 아이템들을 전부 탐색해야한다. 그럼 ▼ 아래로 펼쳐져있는 세모 아이콘을 클릭해서 한 번 접어보자. 그럼 다음처럼 모든 리스트를 볼 수 있다.

list_goods까지 올라가보면 왼쪽 리스트가 전부 선택된 것을 볼 수 있다

list_goods까지 올라가보면 왼쪽 리스트가 전부 선택된 것을 볼 수 있다

<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로 접근할 수 있게 해준다.

contents 가 배열이다

contents 가 배열이다

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:
  # 이 아이템을 이제 슬랙으로 전송하면 된다

슬랙으로 전송하는 부분은 다음 포스팅으로 ..


메타데이터
post_id
7c08fde0c30a
slug
누구나-할-수-있는-크롤링-1-7c08fde0c30a
url
https://medium.com/@eugenegm/%EB%88%84%EA%B5%AC%EB%82%98-%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%ED%81%AC%EB%A1%A4%EB%A7%81-1-7c08fde0c30a
canonical_url
https://medium.com/@eugenegm/%EB%88%84%EA%B5%AC%EB%82%98-%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%ED%81%AC%EB%A1%A4%EB%A7%81-1-7c08fde0c30a
author_url
https://medium.com/@eugenegm
status
ok
fetched_at
2026-06-21 07:44:09