개발자공부일기

크롤링 도구들(Selenium, Playwright, Scrapy, Requests) 본문

Language/Python

크롤링 도구들(Selenium, Playwright, Scrapy, Requests)

JavaCPP 2025. 5. 2. 18:27

회사측에서 한달인턴을 시작하기 전에 알아보면 좋다고하신 크롤링 도구들을 살펴볼 예정이다.

모두 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