웹에서 데이터를 긁어올 일이 생겨서 여러 도구들을 공부하면서 정리한 내용
처음엔 나도 같은거 아닌가 했는데 찾아보니 좀 다르다.
Scraping: 특정 웹사이트에서 내가 원하는 데이터만 쏙쏙 뽑아오는 것
Crawling: 웹사이트를 돌아다니면서 링크를 따라가고 URL을 수집하는 것
실제로는 둘 다 섞어쓰는 경우가 많다.
제일 먼저 배운 도구인데 간단한 작업에는 이게 제일 편하다.
import requests
from bs4 import BeautifulSoup
url = "https://example.com"
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
# 이런식으로 태그를 찾아서 데이터를 추출
titles = soup.find_all('h2', class_='title')
for title in titles:
print(title.text.strip())
실제로 써보니 네이버 뉴스 같은 정적 페이지는 잘 되는데, 인스타나 유튜브 댓글 같은 것은 안된다.
JavaScript로 렌더링되는 페이지를 크롤링해야 해서 찾아본 게 Selenium
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
options = webdriver.ChromeOptions()
options.add_argument('--headless') # 브라우저 안띄우고 백그라운드 실행
driver = webdriver.Chrome(options=options)
driver.get("https://example.com")
# JavaScript 로딩을 기다린다 (이거 안하면 에러남)
wait = WebDriverWait(driver, 10)
element = wait.until(EC.presence_of_element_located((By.CLASS_NAME, "content")))
# 데이터 뽑기
items = driver.find_elements(By.CSS_SELECTOR, ".item")
for item in items:
print(item.text)
driver.quit() # 이거 꼭 해줘야 한다. 안그러면 크롬 프로세스가 계속 살아있다
삽질했던 것:
# 이렇게 바로 데이터를 가져오면 안된다
driver.get(url)
data = driver.find_element(By.CLASS_NAME, "content").text # 에러남
# JavaScript 로딩을 기다려야 한다
wait = WebDriverWait(driver, 10)
element = wait.until(EC.presence_of_element_located((By.CLASS_NAME, "content")))
data = element.text # 이제 된다
처음에 이거 몰라서 한참 헤맸다.
멀티프로세싱으로 여러 브라우저를 동시에 띄우면 좀 나아진다.
from multiprocessing import Pool
def crawl(url):
driver = webdriver.Chrome(options=options)
# 크롤링 로직
driver.quit()
return data
urls = ['url1', 'url2', 'url3']
with Pool(3) as p: # 3개 동시 실행
results = p.map(crawl, urls)
근데 메모리 문제는 여전하다.
큰 프로젝트를 하면서 Selenium이 너무 느려서 찾아본 게 Scrapy
import scrapy
class MySpider(scrapy.Spider):
name = 'example'
start_urls = ['https://example.com']
def parse(self, response):
# XPath를 쓸 수 있다 (CSS selector보다 강력)
for item in response.xpath('//div[@class="item"]'):
yield {
'title': item.xpath('.//h2/text()').get(),
'price': item.xpath('.//span[@class="price"]/text()').get(),
}
# 다음 페이지를 자동으로 따라감
next_page = response.xpath('//a[@class="next"]/@href').get()
if next_page:
yield response.follow(next_page, self.parse)
실행은 터미널에서
scrapy crawl example -o result.json
# pipelines.py
class CleanPipeline:
def process_item(self, item, spider):
item['price'] = item['price'].replace('원', '').replace(',', '')
return item
scrapy startproject myproject
이러면 폴더가 우르르 생긴다
그래도 BeautifulSoup과 같이 쓸 수 있다:
def parse(self, response):
soup = BeautifulSoup(response.text, 'html.parser')
# BeautifulSoup 문법 사용 가능
내가 경험해보니까
실제 썼던 것: 네이버 블로그 포스팅 제목 수집
실제 썼던 것: 인스타그램 해시태그 검색 결과, 쿠팡 상품 리뷰
실제 썼던 것: 부동산 매물 정보 수집 (수천개 페이지)
| Beautiful Soup | Selenium | Scrapy | |
|---|---|---|---|
| 난이도 | 쉬움 | 보통 | 어려움 |
| 속도 | 빠름 | 느림 | 빠름 |
| JS 렌더링 | X | O | △ (플러그인) |
| 대규모 크롤링 | 비추 | 비추 | 추천 |
| 배우는 시간 | 1-2시간 | 하루 | 1주일 |
Selenium 대신 Playwright를 많이 쓰는 추세
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto('https://example.com')
content = page.content()
browser.close()
Selenium보다 빠르고 API도 더 깔끔하다. 근데 아직 자료는 Selenium이 더 많다.
대부분의 사이트에 https://example.com/robots.txt로 가면 크롤링 규칙이 나와있다
User-agent: *
Disallow: /admin/
이런식으로. 지켜주는 게 좋다.
안하면 차단당할 수 있다
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
response = requests.get(url, headers=headers)
너무 빠르게 요청하면 서버 부담 + IP 차단된다
import time
time.sleep(1) # 1초 쉬기
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"에러 발생: {e}")
# 로깅하거나 재시도 로직
보통 여러 개를 조합해서 사용한다
무조건 한 도구만 쓸 필요는 없다.
처음엔 Beautiful Soup으로 시작해서 필요할 때마다 다른 것을 배우는 게 낫다.
웹 스크래핑은 결국 상황에 맞는 도구를 선택하는 게 중요하다. 간단한 작업에는 Beautiful Soup, 동적 페이지에는 Selenium이나 Playwright, 대규모 프로젝트에는 Scrapy를 쓰면 된다.
참고 자료