개발자공부일기
크롤링 도구들(Selenium, Playwright, Scrapy, Requests) 본문
회사측에서 한달인턴을 시작하기 전에 알아보면 좋다고하신 크롤링 도구들을 살펴볼 예정이다.
모두 Python에서 동작한다(Python에서만 동작하는 도구도 있지만 아닌 도구들도 있다.)
1. Requests
개요
- HTTP 요청을 통해 웹 페이지의 HTML 코드를 가져오는 가장 기본적인 크롤링 도구입니다.
- 자바스크립트 렌더링이 필요 없는 정적 페이지 크롤링에 매우 적합합니다.
동작 방식
- 서버에 GET, POST 등 HTTP 요청을 보냅니다.
- 서버로부터 받은 응답(response)의 본문을 파싱하거나 저장합니다.
- 보통 BeautifulSoup, lxml 등의 HTML 파서와 함께 사용합니다.
장점
- 가볍고 빠름: 렌더링이 없기 때문에 속도가 매우 빠름.
- 의존성 낮음: 브라우저 필요 없음.
- 단순한 API: .get(), .post() 등 직관적인 함수.
단점
- 자바스크립트 렌더링 불가: 동적 로딩 콘텐츠를 처리하지 못함.
- 쿠키/세션 조작이 제한적: 복잡한 인증 흐름이나 동적 요청에 약함.
주요 사용 사례
- 뉴스 기사, 게시판 등 정적 콘텐츠 수집
- Open API 연동
2. Selenium
개요
- 웹 브라우저를 자동으로 제어하여 사람처럼 행동하게 하는 자동화 및 테스트 프레임워크입니다.
- 동적 렌더링 페이지 크롤링 가능 (브라우저 직접 실행).
동작 방식
- 웹 드라이버(예: ChromeDriver)를 통해 실제 브라우저를 띄움.
- DOM을 탐색하고 사용자 입력(클릭, 스크롤 등)을 자동화함.
- 브라우저를 통해 실행되므로 자바스크립트 렌더링 포함한 웹 페이지 전체 접근 가능.
장점
- 강력한 브라우저 제어: 스크롤, 클릭, 입력 등 모든 사용자 행동 가능.
- 자바스크립트 완벽 지원: 실사용자와 동일하게 페이지 확인 가능.
- 테스트 자동화에도 사용 가능.
단점
- 속도 느림: 전체 브라우저를 실행해야 하므로 무거움.
- 병렬처리 어려움: 멀티탭 또는 다수 인스턴스 실행 시 리소스 부담 큼.
- 설정 복잡: WebDriver 버전 관리 필요.
주요 사용 사례
- 로그인 필요 사이트 크롤링
- 무한 스크롤 페이지 처리
- 자동화된 브라우저 테스트
3. Playwright
개요
- 마이크로소프트에서 만든 차세대 브라우저 자동화 프레임워크.
- Selenium보다 빠르고 안정적인 성능을 목표로 개발됨.
동작 방식
- Chromium, Firefox, WebKit을 직접 제어.
- 자바스크립트 렌더링과 상호작용을 완벽히 지원.
- Headless/Headful 모드 모두 지원.
- Node.js가 기본 언어이나 Python 버전도 존재.
장점
- 병렬성과 성능 우수: 다중 브라우저, 탭 병렬 실행 쉬움.
- 모던 웹 호환성 높음: 최신 웹 앱 구조 잘 처리함.
- 스크린샷, PDF 저장, 비디오 녹화 등 부가 기능 풍부
- 동기/비동기 API 모두 지원
단점
- 학습 곡선 존재: 설정과 개념이 Selenium보다 다소 복잡할 수 있음.
- 자원 사용: 브라우저 실행 기반이므로 Requests보다는 무거움.
주요 사용 사례
- 싱글 페이지 애플리케이션(SPA) 크롤링
- 복잡한 유저 인터페이스 자동화
- 여러 브라우저/모바일 환경 테스트
4. Scrapy
개요
- 대규모 웹 크롤링에 특화된 프레임워크.
- 비동기 처리와 파이프라인, 중복 제거, 로깅 등 크롤링 전체 플로우를 체계적으로 구성 가능.
동작 방식
- 자체 스케줄러와 다운로더를 사용하여 요청을 병렬적으로 처리.
- Item, Spider, Pipeline, DownloaderMiddleware 등을 활용하여 데이터를 수집, 정제, 저장함.
- 기본적으로 Requests 기반이나, Playwright/Selenium과 통합하여 동적 렌더링도 처리 가능.
장점
- 성능 좋고 구조적임: 수천~수만 개 페이지 크롤링에 적합.
- 재사용성과 유지보수 용이: 프로젝트 구조가 체계적.
- 중복 방지, 로깅, 캐싱 등 고급 기능 포함
- playwright와 결합 시 강력한 동적 렌더링 지원
단점
- 초기 설정과 구조 파악 어려움: 단순 크롤러 만들기엔 무겁다고 느껴질 수 있음.
- 동적 페이지 단독 처리 어려움: 기본은 정적 요청.
주요 사용 사례
- 뉴스, 쇼핑몰, 구인 사이트 등 대량 수집
- 데이터 정제 및 파이프라인 구축이 필요한 작업
요약 비교표
항목 | Requests | Selenium | Playwright | Scrapy |
렌더링 | X | O | O | X(기본) / O(확장) |
속도 | ★★★★★ | ★★☆☆☆ | ★★★★☆ | ★★★★☆ |
설정 난이도 | ★☆☆☆☆ | ★★★☆☆ | ★★★★☆ | ★★★★☆ |
대규모 처리 | ★★☆☆☆ | ★☆☆☆☆ | ★★★★☆ | ★★★★★ |
병렬 처리 | X | 어려움 | O | O |
동적 데이터 수집 | X | O | O | O (외부 도구 필요) |
유지보수 | 낮음 | 중간 | 중간 | 높음 (프레임워크 기반) |
추천 대상 | 정적 웹 페이지 | 단순 자동화 / 테스트 | SPA/JS 많은 사이트 | 구조적 수집, 대규모 크롤링 |
나는 nexon의 채용사이트를 크롤링 해보기 위해 chatGPT의 도움을 받아 코드를 짜봤다.
nexon의 채용사이트는 자바스크립트를 사용한 동적 웹사이트라 Playwright만을 써서 만들었다.
# playwright의 동기 API를 사용하여 크롤링 작업을 진행합니다.
from playwright.sync_api import sync_playwright
import pandas as pd # pandas를 사용하여 데이터를 DataFrame 형식으로 변환하고 저장합니다.
import time
# 넥슨 채용 정보를 크롤링하는 함수 정의
def scrape_nexon_jobs():
# Playwright를 사용하여 브라우저를 시작합니다.
with sync_playwright() as p:
# 크롬 브라우저를 헤드리스 모드가 아닌 상태로 실행합니다.
browser = p.chromium.launch(headless=False)
# 새 페이지를 엽니다.
page = browser.new_page()
# 넥슨 채용 사이트로 이동합니다.
page.goto("https://careers.nexon.com/recruit")
last_height = page.evaluate("document.body.scrollHeight")
while True:
# 페이지를 조금씩 스크롤 내리기
# 넥슨의 채용공고는 맨끝으로 내리면 갱신되지 않고 자연스래 스크롤이
# 내려가야 갱신되기에 아래 코드로 천천히 스크롤을 내렸다.
page.evaluate("window.scrollTo(0, document.body.scrollHeight - 1000)")
time.sleep(2) # 페이지 로딩 대기
# 새로운 높이가 이전 높이와 같다면 끝에 도달한 것입니다.
new_height = page.evaluate("document.body.scrollHeight")
if new_height == last_height:
break
last_height = new_height
# 채용 공고 목록이 로드될 때까지 기다립니다.
page.wait_for_selector("a[href^='/recruit/']")
# 채용 공고들을 저장할 빈 리스트를 초기화합니다.
jobs = []
# 모든 채용 공고 요소를 선택합니다.
items = page.query_selector_all("a[href^='/recruit/']")
# 각 채용 공고 항목에 대해 반복하면서 정보를 추출합니다.
for item in items:
# 공고의 링크를 가져옵니다.
link = item.get_attribute("href")
# 전체 URL을 생성합니다.
full_link = f"https://careers.nexon.com{link}"
# 공고 제목을 추출합니다.
title_elem = item.query_selector("h4")
title = title_elem.inner_text().strip() if title_elem else ""
# 회사명을 추출합니다.
company_elem = item.query_selector(".text-company")
company = company_elem.inner_text().strip() if company_elem else ""
# 마감일을 추출합니다.
deadline_elem = item.query_selector(".d-day")
deadline = deadline_elem.inner_text().strip() if deadline_elem else ""
# 추출한 정보를 딕셔너리로 저장하고, jobs 리스트에 추가합니다.
jobs.append({
"제목": title,
"회사명": company,
"마감일": deadline,
"링크": full_link
})
# 브라우저를 닫습니다.
browser.close()
# 수집한 채용 공고 정보를 반환합니다.
return jobs
# 함수 실행하여 채용 정보 리스트를 가져옵니다.
job_list = scrape_nexon_jobs()
# 가져온 정보를 pandas DataFrame으로 변환합니다.
df = pd.DataFrame(job_list)
# DataFrame을 CSV 파일로 저장합니다. 한글이 깨지지 않도록 utf-8-sig 인코딩을 사용합니다.
df.to_csv("scraper/output/nexon_jobs.csv", index=False, encoding="utf-8-sig")
# 저장된 데이터를 콘솔에 출력하여 확인합니다.
print(df)
이 코드를 실행하면 브라우저를 프로그램이 실행하고 알아서 스크롤을 내린다.
그리고 모든 채용공고를 갱신해서 더이상 갱신이 안되면 csv파일에 크롤링한 데이터를 저장하고 종료한다.
결과물은 여기서 볼 수 있다.
https://github.com/JavaCPP0/projects/blob/main/Python_crawler/scraper/output/nexon_jobs.csv