현대 Scrapy 개발자 가이드 (Part 3): 자동 생성 페이지 객체와 Web Scraping Co-pilot

발행: (2025년 12월 17일 오전 03:55 GMT+9)
9 min read
원문: Dev.to

I’m happy to translate the article for you, but I’ll need the full text of the article (the content you’d like translated). Could you please paste the article’s text here? Once I have it, I’ll provide a Korean translation while keeping the source link, formatting, markdown, and any code blocks exactly as they appear.

사전 요구 사항 및 설정

  • Part 1을 완료했습니다 (위의 링크를 참조).
  • Visual Studio Code가 설치되어 있습니다.
  • Web Scraping Co‑pilot 확장 프로그램 (지금 설치할 예정입니다).

단계 1: Web Scraping Co‑pilot 설치

  1. VS Code를 열고 Extensions 탭으로 이동합니다.

  2. Web Scraping Co‑pilot(출판사 Zyte)을 검색하고 설치합니다.

    Web Scraping Co‑pilot

  3. 설치가 완료되면 사이드바에 새로운 아이콘이 표시됩니다. 아이콘을 클릭하면 확장 프로그램이 자동으로 Scrapy 프로젝트를 감지합니다.

  4. 프롬프트가 표시되면 몇 가지 종속성(예: pytest)을 설치하도록 허용합니다. 이렇게 하면 AI 기반 생성에 필요한 환경이 준비됩니다.

Step 2: BookItem 자동 생성

Part 1에서 만든 스파이더를 시작점으로 삼아 Co‑pilot에게 BookItem에 대한 페이지 객체를 만들게 하고, Part 2에서 만든 것보다 더 많은 필드를 추가하도록 합니다.

  1. Co‑pilot 채팅 창을 엽니다.

  2. “Web Scraping.” 을 선택합니다.

  3. 다음과 같은 프롬프트를 입력합니다:

    Create a page object for the item BookItem using the sample URL https://books.toscrape.com/catalogue/the-host_979/index.html

Co‑pilot은 다음을 수행합니다:

  • 프로젝트 확인scrapy‑poetpytest가 설치되어 있는지 확인하고, 없으면 설치를 제안합니다.
  • scrapy‑poet 설정 추가settings.pyADDONSSCRAPY_POET_DISCOVER 항목을 자동으로 삽입합니다.
  • items.py 생성 – 페이지에서 발견할 수 있는 모든 필드를 포함한 새로운 BookItem 클래스를 생성합니다.
# tutorial/items.py (Auto‑Generated!)
import attrs

@attrs.define
class BookItem:
    """
    The structured data we extract from a book *detail* page.
    """
    name: str
    price: str
    url: str
    availability: str          #  str:
        return self.response.css("h1::text").get()

    @field
    def price(self) -> str:
        return self.response.css("p.price_color::text").get()

    @field
    def url(self) -> str:
        return self.response.url

    @field
    def availability(self) -> str:
        # The second element contains the actual text
        return self.response.css("p.availability::text").getall()[1].strip()

    @field
    def number_of_reviews(self) -> int:
        return int(self.response.css("table tr:last-child td::text").get())

    @field
    def upc(self) -> str:
        return self.response.css("table tr:first-child td::text").get()

대략 30 초 만에 Co‑pilot은 Part 2에서 수동으로 했던 모든 작업을 수행했으며, 추가 필드까지 포함했습니다.

Step 3: AI‑생성 테스트 실행

Co‑pilot이 여러분을 위해 단위 테스트도 작성했습니다. 이제 tests 폴더에 test_bookstoscrape_com.py가 들어 있습니다.

Co‑pilot UI의 “Run Tests” 또는 터미널에서 테스트를 실행합니다:

$ pytest
=================== test session starts ===================
...
tests/test_bookstoscrape_com.py::test_book_detail[book_0] PASSED
tests/test_bookstoscrape_com.py::test_book_detail[book_1] PASSED
...
=================== 8 passed in 0.10s ====================

파싱 로직이 완전히 테스트되었으며, 테스트 코드를 한 줄도 작성하지 않았습니다.

4단계: 스파이더 리팩토링 (쉬운 방법)

이제 tutorial/spiders/books.py 를 새로운 아키텍처를 사용하도록 업데이트합니다. Part 2에서 했던 것과 동일합니다.

# tutorial/spiders/books.py

import scrapy
# 새로 자동 생성된 Item 클래스를 가져옵니다
from tutorial.items import BookItem

class BooksSpider(scrapy.Spider):
    name = "books"
    # ... (Part 1의 나머지 스파이더 코드) ...

    async def parse_listpage(self, response):
        product_urls = response.css("article.product_pod h3 a::attr(href)").getall()
        for url in product_urls:
            # Scrapy에게 parse_book을 호출하도록 지시합니다
            yield response.follow(url, callback=self.parse_book)

    async def parse_book(self, response):
        # Page Object가 모든 파싱을 담당합니다
        page = BookDetailPage(response)
        item = await page.to_item()
        yield item

자동으로 생성된 BookDetailPage가 모든 파싱을 처리하므로, 스파이더는 크게 단순화됩니다.

🎉 이제 끝!

몇 분 만에 Web Scraping Co‑pilot은 다음을 수행했습니다.

  1. 모든 관련 필드를 포함한 BookItem 스키마를 생성했습니다.
  2. 완전한 기능을 갖춘 Page Object(BookDetailPage)를 생성했습니다.
  3. 픽스처와 포괄적인 단위 테스트를 만들었습니다.
  4. 스파이더 코드를 깔끔하고 async‑ready 구현으로 단순화했습니다.

이제 크롤링 전략, 데이터 파이프라인, 스케일링에 집중하고, 반복적인 보일러플레이트 작업은 AI에게 맡기세요. 즐거운 스크래핑 되세요!

t_page_url = response.css("li.next a::attr(href)").get()
if next_page_url:
    yield response.follow(next_page_url, callback=self.parse_listpage)

# BookItem을 요청하면 scrapy‑poet이 나머지를 처리합니다!
async def parse_book(self, response, book: BookItem):
    yield book

Step 5: Auto‑Generating our BookListPage

리스트 페이지에 대해서도 동일한 과정을 반복하여 리팩터링을 마무리할 수 있습니다.

Co‑pilot에게 프롬프트하기:

샘플 URL을 사용하여 리스트 아이템 BookListPage에 대한 페이지 객체를 생성해 주세요

Result

  • Co‑pilot은 BookListPage 아이템을 **items.py**에 생성합니다.
  • book_urlsnext_page_url 파서를 포함한 BookListPageObject를 **bookstoscrape_com.py**에 생성합니다.
  • 테스트 코드를 작성하고 통과시킵니다.

이제 스파이더를 마지막으로 한 번 더 업데이트하여 완전하게 설계된 형태로 만들 수 있습니다.

# tutorial/spiders/books.py (FINAL VERSION)

import scrapy
from tutorial.items import BookItem, BookListPage   # Import both

class BooksSpider(scrapy.Spider):
    # ... (name, allowed_domains, url) ...

    async def start(self):
        yield scrapy.Request(self.url, callback=self.parse_listpage)

    # 이제 BookListPage 아이템을 요청합니다!
    async def parse_listpage(self, response, page: BookListPage):
        # 모든 파싱 로직이 스파이더에서 사라졌습니다.
        for url in page.book_urls:
            yield response.follow(url, callback=self.parse_book)

        if page.next_page_url:
            yield response.follow(page.next_page_url, callback=self.parse_listpage)

    async def parse_book(self, response, book: BookItem):
        yield book

우리 스파이더는 이제 크롤러에 불과합니다. 파싱 로직이 전혀 없으며, 셀렉터를 찾고 파서를 작성하는 모든 어려운 작업은 Co‑pilot에 의해 자동화되었습니다.

결론: “하이브리드 개발자”

Web Scraping Co‑pilot은 여러분을 대체하지 않습니다. 오히려 가속화합니다. “반복 작업”(셀렉터 찾기, 보일러플레이트 작성, 테스트 생성)이라는 90 %의 작업을 자동화해 주어, 여러분은 중요한 10 %인 크롤링 로직, 전략, 복잡한 사이트 처리에 집중할 수 있습니다.

이것이 우리가 Scrapy 유지보수자로서 스파이더를 전문적으로 구축하는 방식입니다.

다음은? 커뮤니티에 참여하세요

💬 Discord에서 대화하기 – 이 Scrapy 코드가 막히셨나요? 유지보수자와 5천 명 이상의 개발자에게 Discord에서 물어보세요.
▶️ YouTube에서 시청하기 – 이 게시물은 저희 영상에 기반합니다! 채널에서 전체 walkthrough를 시청하세요.
📩 더 읽기 – 더 보고 싶으신가요? 파트 2에서는 Scrapy Items와 Pipelines를 다룰 예정입니다. Extract 뉴스레터를 구독해 놓으면 놓치지 않을 수 있습니다.

Back to Blog

관련 글

더 보기 »