Scrapy를 이용한 웹 스크래핑: 단계별 튜토리얼

이 실용적인 튜토리얼에서 Python과 Scrapy를 활용하여 숙제나 점심 메뉴 등 학교 관련 정보의 수집 및 정리를 자동화하는 방법을 배워보세요.
6 분 읽기
Web Scraping With Scrapy blog image

웹 스크래핑은 웹사이트에서 데이터를 수집하는 프로그래밍 방식이며, 시장 조사, 가격 모니터링, 데이터 분석, 리드 생성 등 웹 스크래핑의 활용 사례는 무궁무진합니다.

이 튜토리얼에서는 학교에서 보내온 정보를 수집하고 정리하는 데 초점을 맞춘 실용적인 활용 사례를 살펴보겠습니다. 여기서는 주로 숙제 과제와 학교 급식 정보에 집중할 것입니다.

다음은 최종 프로젝트의 대략적인 아키텍처 다이어그램입니다:

Diagram of the final project

필수 조건

이 튜토리얼을 따라하려면 다음이 필요합니다:

  • Python 3.10 이상
  • 활성화된 가상 환경
  • Scrapy CLI 2.11.1
  • Visual Studio Code

개인정보 보호를 위해 이 가상의 학교 시스템 웹사이트를 사용합니다: https://systemcraftsman.github.io/scrapy-demo/website/.

프로젝트 생성

터미널에서 기본 프로젝트 디렉터리를 생성하세요(어디에나 생성 가능):

mkdir school-scraper

새로 생성한 폴더로 이동하여 다음 명령어를 실행하여 새로운 Scrapy 프로젝트를 생성하세요:

cd school-scraper & 
scrapy startproject school_scraper

프로젝트 구조는 다음과 같아야 합니다:

school-scraper
└── school_scraper
    ├── school_scraper
    │   ├── __init__.py
    │   ├── items.py
    │   ├── middlewares.py
    │   ├── pipelines.py
    │   ├── settings.py
    │   └── spiders
    │       └── __init__.py
    └── scrapy.cfg

이전 명령어는 school_scraper 디렉터리를 두 단계로 생성합니다. 내부 디렉터리에는 자동 생성된 파일들이 있습니다: middlewares.py(Scrapy 미들웨어 정의), pipelines.py(데이터 변형을 위한 커스텀 파이프라인 정의), settings.py(스크래핑 애플리케이션의 일반 설정 정의).

가장 중요한 것은 스파이더가 위치한 spiders 폴더입니다. 스파이더는 특정 사이트를 특정 방식으로 스크래핑하는 데 활용할 수 있는 Python 클래스입니다. 이들은 스크래핑 시스템 내 관심사 분리 원칙을 준수하여 각 스크래핑 작업마다 전용 스파이더를 생성할 수 있게 합니다.

아직 생성된 스파이더가 없으므로 이 폴더는 비어 있지만, 다음 단계에서 첫 번째 스파이더를 생성하게 될 것입니다.

숙제 스파이더 생성

학교 시스템에서 숙제 데이터를 스크래핑하려면, 먼저 시스템에 로그인한 후 숙제 할당 페이지로 이동하여 데이터를 스크래핑하는 스파이더를 생성해야 합니다:

diagram showing the process of creating a spider and navigating to scrape the data

웹 스크래핑용 스파이더를 생성하려면 Scrapy CLI를 사용합니다. 프로젝트의 school-scraper/school_scraper 디렉터리로 이동한 후 다음 명령어를 실행하여 spiders 폴더 내에 HomeworkSpider라는 이름의 스파이더를 생성하세요:

scrapy genspider homework_spider systemcraftsman.github.io/scrapy-demo/website/index.html

참고: 활성화된 가상 환경에서 모든 Python 또는 Scrapy 관련 명령어를 실행하는 것을 잊지 마세요.

scrapy genspider 명령어는 스파이더를 생성합니다. 다음 매개변수는 스파이더 이름(예: homework_spider)이며, 마지막 매개변수는 스파이더의 시작 URL을 정의합니다. 이렇게 하면 systemcraftsman.github.io가 Scrapy에 의해 허용된 도메인으로 인식됩니다.

출력 결과는 다음과 같아야 합니다:

모듈에서 템플릿 'basic'을 사용하여 'homework_spider' 스파이더 생성:
  school_scraper.spiders.homework_spider

school_scraper/spiders 디렉터리 아래에 homework_spider.py 파일을 생성해야 하며, 내용은 다음과 같아야 합니다:

class HomeworkSpiderSpider(scrapy.Spider):
    name = "homework_spider"
    allowed_domains = ["systemcraftsman.github.io"]
    start_urls = ["https://systemcraftsman.github.io/scrapy-demo/website/index.html"]

    def parse(self, response):
        pass

클래스 이름을 HomeworkSpider로 변경하여 클래스 이름에서 중복된 Spider를 제거합니다. parse 함수는 스크래핑을 시작하는 초기 함수입니다. 이 경우 시스템에 로그인하는 작업입니다.

참고: https://systemcraftsman.github.io/scrapy-demo/index.html의 로그인 양식은 몇 줄의 자바스크립트로 구성된 가짜 로그인 양식입니다. 해당 페이지는 HTML이므로 POST 요청을 수락하지 않으며, 대신 HTTP GET 요청을 사용하여 로그인을 모방합니다.

parse 함수를 다음과 같이 업데이트하세요:

...코드 생략...
    def parse(self, response):
        formdata = {'username': 'student',
                    'password': '12345'}
        return FormRequest(url=self.welcome_page_url, method="GET", formdata=formdata,
                           callback=self.after_login)

여기서 index.html 페이지 내 로그인 양식을 제출하기 위한 폼 요청을 생성합니다. 제출된 양식은 정의된 welcome_page_url로 리디렉션되어야 하며, 스크래핑 프로세스를 계속하기 위한 콜백 함수를 가져야 합니다. 곧 after_login 콜백 함수를 추가할 것입니다.

다른 변수들이 정의된 클래스 상단에 welcome_page_url을 추가하여 정의하세요:

...코드 생략...
    start_urls = ["https://systemcraftsman.github.io/scrapy-demo/website/index.html"]
    welcome_page_url = "https://systemcraftsman.github.io/scrapy-demo/website/welcome.html"
...코드 생략...

그런 다음 클래스 내 parse 함수 바로 다음에 after_login 함수를 추가하세요:

...코드 생략...
    def after_login(self, response):
        if response.status == 200:
            return Request(url=self.homework_page_url,
                           callback=self.parse_homework_page
                           )
...코드 생략...

after_login 함수는 응답 상태가 200(성공)인지 확인합니다. 그런 다음 숙제 페이지로 이동하여 parse_homework_page 콜백 함수를 호출합니다. 이 함수는 다음 단계에서 정의할 것입니다.

다른 변수들이 정의된 클래스 상단에 homework_page_url을 추가하여 정의하세요:

...코드 생략...
    start_urls = ["https://systemcraftsman.github.io/scrapy-demo/website/index.html"]
    welcome_page_url = "https://systemcraftsman.github.io/scrapy-demo/website/welcome.html"
    homework_page_url = "https://systemcraftsman.github.io/scrapy-demo/website/homeworks.html"
...코드 생략...

클래스 내 after_login 함수 뒤에 parse_homework_page 함수를 추가하세요:

...코드 생략...
    def parse_homework_page(self, response):
        if response.status == 200:
            data = {}
            rows = response.xpath('//*[@class="table table-file"]//tbody//tr')
            for row in rows:
                if self._get_item(row, 4) == self.date_str:
                    if self._get_item(row, 2) not in data:
                        data[self._get_item(row, 2)] = self._get_item(row, 3)
                    else:
                        data[self._get_item(row, 2) + "-2"] = self._get_item(row, 3)
            return data
...코드 생략...

parse_homework_page 함수는 응답 상태가 200 ( 성공)인지 확인한 후, HTML 테이블로 제공되는 숙제 데이터를 파싱합니다.

함수는 HTTP 200 코드를 확인한 후 XPath를 사용하여 각 행의 데이터를 추출합니다. 각 행을 추출한 후, 함수는 데이터를 반복 처리하며 Spider 클래스에 추가해야 하는 비공개 _get_item 함수를 사용하여 특정 항목을 추출합니다.

_get_item 함수는 다음과 같아야 합니다:

...코드 생략...
    def _get_item(self, row, col_number):
        item_str = ""
        contents = row.xpath(f'td[{col_number}]//text()').extract()
        for content in contents:
            item_str = item_str + content
        return item_str

_get_item 함수는 XPath와 행/열 번호를 활용해 각 셀의 내용을 가져옵니다. 셀에 여러 단락이 포함된 경우, 함수는 이를 반복 처리하며 각 단락을 연결합니다.

parse_homework_page 함수는 date_str도 정의해야 합니다. 정적 웹사이트에 있는 날짜 데이터인 2024년 3월 12일로 정의해야 합니다.

참고: 실제 환경에서는 웹사이트 데이터가 동적으로 변경되므로 날짜를 동적으로 정의해야 합니다.

다른 변수들이 정의된 클래스 상단에 date_str을 추가하여 정의하세요:

...코드 생략...
    homework_page_url = "https://systemcraftsman.github.io/scrapy-demo/website/homeworks.html"
    date_str = "12.03.2024"
...코드 생략...

최종 homework_spider.py 파일은 다음과 같습니다:

import scrapy

from scrapy import FormRequest, Request


class HomeworkSpider(scrapy.Spider):
    name = "homework_spider"
    allowed_domains = ["systemcraftsman.github.io"]
    start_urls = ["https://systemcraftsman.github.io/scrapy-demo/website/index.html"]
    welcome_page_url = "https://systemcraftsman.github.io/scrapy-demo/website/welcome.html"
    homework_page_url = "https://systemcraftsman.github.io/scrapy-demo/website/homeworks.html"
    date_str = "12.03.2024"

    def parse(self, response):
        formdata = {'username': 'student',
                    'password': '12345'}
        return FormRequest(url=self.welcome_page_url, method="GET", formdata=formdata,
                           callback=self.after_login)

    def after_login(self, response):
        if response.status == 200:
            return Request(url=self.homework_page_url,
                           callback=self.parse_homework_page
                           )

    def parse_homework_page(self, response):
    if response.status == 200:
        data = {}
        rows = response.xpath('//*[@class="table table-file"]//tbody//tr')
        for row in rows:
            if self._get_item(row, 4) == self.date_str:
                    if self._get_item(row, 2) not in data:
                        data[self._get_item(row, 2)] = self._get_item(row, 3)
                    else:
                        data[self._get_item(row, 2) + "-2"] = self._get_item(row, 3)
            return data

    def _get_item(self, row, col_number):
        item_str = ""
        contents = row.xpath(f'td[{col_number}]//text()').extract()
        for content in contents:
            item_str = item_str + content
        return item_str

school-scraper/school_scraper 디렉터리에서 다음 명령어를 실행하여 숙제 데이터가 성공적으로 스크래핑되는지 확인하세요:

scrapy crawl homework_spider

다른 로그 사이에 스크랩된 출력이 표시되어야 합니다:

...출력 생략...
2024-03-20 01:36:05 [scrapy.core.scraper] DEBUG: <200 https://systemcraftsman.github.io/scrapy-demo/website/homeworks.html>에서 스크래핑됨
{'MATHS': "수학 주제 설명 연습장-6 13페이지를 작성하세요.n", 'ENGLISH': '독서 기록장 100-107페이지에 있는 "매니와 그의 괴물 매너" 이야기를 읽고, 이야기 내용에 따라 108페이지와 109페이지의 활동을 완료하세요.nn독서 기록장 책의 100-107페이지에 있는 "Manny and His Monster Manners"라는 이야기를 읽고 108쪽과 109쪽의 활동을 이야기에 따라 완료하세요.n'}
2024-03-20 01:36:05 [scrapy.core.engine] INFO: 스파이더 종료 (완료)
...출력 생략...

축하합니다! 첫 번째 스파이더를 구현하셨습니다. 다음 스파이더를 만들어 보세요!

식단 목록 스파이더 만들기

식단 목록 페이지를 크롤링하는 스파이더를 생성하려면 school-scraper/school_scraper 디렉터리에서 다음 명령어를 실행하세요:

scrapy genspider meal_spider systemcraftsman.github.io/scrapy-demo/website/index.html

생성된 스파이더 클래스는 다음과 같아야 합니다:

class MealSpiderSpider(scrapy.Spider):
    name = "meal_spider"
    allowed_domains = ["systemcraftsman.github.io"]
    start_urls = ["https://systemcraftsman.github.io/scrapy-demo/website/index.html"]

    def parse(self, response):
        pass

식사 스파이더 생성 과정은 숙제 스파이더 생성 과정과 매우 유사합니다. 유일한 차이는 HTML 스크래핑 대상 페이지입니다.

시간을 절약하기 위해 meal_spider.py 의 모든 내용을 다음과 같이 교체하세요:

import scrapy

from datetime import datetime

from scrapy import FormRequest, Request


class MealSpider(scrapy.Spider):
    name = "meal_spider"
    allowed_domains = ["systemcraftsman.github.io"]
    start_urls = ["https://systemcraftsman.github.io/scrapy-demo/website/index.html"]
    welcome_page_url = "https://systemcraftsman.github.io/scrapy-demo/website/welcome.html"
    meal_page_url = "https://systemcraftsman.github.io/scrapy-demo/website/meal-list.html"
    date_str = "13.03.2024"

    def parse(self, response):
        formdata = {'username': 'student',
                    'password': '12345'}
        return FormRequest(url=self.welcome_page_url, method="GET", formdata=formdata,
                           callback=self.after_login)

    def after_login(self, response):
        if response.status == 200:
            return Request(url=self.meal_page_url,
                           callback=self.parse_meal_page
                           )

    def parse_meal_page(self, response):
    if response.status == 200:
        data = {"BREAKFAST": "", "LUNCH": "", "SALAD/DESSERT": "", "FRUIT TIME": ""}
            week_no = datetime.strptime(self.date_str, '%d.%m.%Y').isoweekday()
            rows = response.xpath('//*[@class="table table-condensed table-yemek-listesi"]//tr')
            key = ""
            try:
                for row in rows[1:]:
                    if self._get_item(row, week_no) in data.keys():
                        key = self._get_item(row, week_no)
                    else:
                        data[key] = self._get_item(row, week_no, "n")
            finally:
                return data

    def _get_item(self, row, col_number, seperator=""):
        item_str = ""
        contents = row.xpath(f'td[{col_number}]//text()').extract()
        for i, content in enumerate(contents):
            item_str = item_str + content + seperator
        return item_str

parse 함수와 after_login 함수는 거의 동일합니다. 유일한 차이는 콜백 함수 parse_meal_page의 이름으로, 다른 XPath 로직을 사용하여 식사 페이지의 HTML을 파싱합니다. 이 함수는 또한 숙제용으로 만든 것과 유사하게 작동하는 _get_item이라는 사설 함수의 도움을 받습니다.

숙제 페이지와 식단 목록 페이지에서 테이블 사용 방식이 다르기 때문에 데이터 파싱 및 처리 방식도 달라집니다.

meal_spider를 검증하려면 school-scraper/school_scraper 디렉터리에서 다음 명령어를 실행하세요:

scrapy crawl meal_spider

출력 결과는 다음과 같아야 합니다:

...출력 생략...
2024-03-20 02:44:42 [scrapy.core.scraper] DEBUG: Scraped from <200 https://systemcraftsman.github.io/scrapy-demo/website/meal-list.html>
{'BREAKFAST': 'PANCAKE n KREM PEYNİR n SÜZME PEYNİR nnKAKAOLU FINDIK KREMASI n SÜTn', 'LUNCH': 'TARHANA ÇORBAnEKŞİLİ KÖFTEnERİŞTEn', 'SALAD/DESSERT': '아이란 n 빨간 양배추 샐러드 n 로카리 고베크 샐러드', '과일 타임': '호두 & 건포도'}
2024-03-20 02:44:42 [scrapy.core.engine] INFO: 스파이더 종료 (완료)
...출력 생략...

참고: 데이터는 원본 웹사이트에서 가져온 것이므로, 원본 형식을 유지하기 위해 번역되지 않았습니다.

데이터 포맷팅

숙제 과제 및 식단 목록 페이지용으로 생성한 스크레이퍼는 JSON 형식의 데이터를 추출할 준비가 되어 있습니다. 그러나 프로그래밍 방식으로 스파이더를 실행하여 데이터를 포맷팅할 수도 있습니다.

Python 애플리케이션에서는 일반적으로 main.py 파일이 진입점 역할을 하여 핵심 구성 요소를 호출함으로써 애플리케이션을 초기화합니다. 그러나 이 Scrapy 프로젝트에서는 Scrapy CLI가 스파이더 구현을 위한 사전 구축된 프레임워크를 제공하며 동일한 CLI를 통해 스파이더를 실행할 수 있으므로 진입점을 생성하지 않았습니다.

이 시나리오에서 데이터를 포맷팅하려면 인수를 받아 그에 따라 스크래핑하는 기본적인 Python 명령줄 프로그램을 구축할 것입니다.

school-scraper 프로젝트의 루트 디렉터리에 main.py 파일을 생성하고 다음 내용을 입력하세요:

import sys

from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings

from school_scraper.school_scraper.spiders.homework_spider import HomeworkSpider
from school_scraper.school_scraper.spiders.meal_spider import MealSpider

results = []


class ResultsPipeline(object):
    def process_item(self, item, spider):
        results.append(item)


def _prepare_message(title, data_dict):
    if len(data_dict.items()) == 0:
        return None

    message = f"===={title}====n----------------n"
    for key, value in data_dict.items():
        message = message + f"==={key}===n{value}n----------------n"

    return message


def main(args=None):
    if args is None:
        args = sys.argv
    settings = get_project_settings()
    settings.set("ITEM_PIPELINES", {'__main__.ResultsPipeline': 1})
    process = CrawlerProcess(settings)

    if args[1] == "homework":
        process.crawl(HomeworkSpider)
        process.start()
        print(_prepare_message("HOMEWORK ASSIGNMENTS", results[0]))
    elif args[1] == "meal":
        process.crawl(MealSpider)
        process.start()
        print(_prepare_message("MEAL LIST", results[0]))


if __name__ == "__main__":
    main()

main.py 파일에는 main 함수가 있으며, 이는 애플리케이션의 진입점 함수입니다. main.py를 실행하면 main 메서드가 호출됩니다. main 메서드는 args라는 배열 인수를 받아 프로그램에 인수를 전달하는 데 사용할 수 있습니다.

main.py는 먼저 args 값을 확인하고 ResultsPipeline이라는 파이프라인을 정의하여 Scrapy 크롤러 설정을 구성합니다. 보시다시피 ResultsPipeline은 이 파일에서 정의되지만, 파이프라인은 pipelines 패키지 아래에서 정의합니다.

ResultsPipeline은 단순히 결과를 가져와 results라는 배열에 추가합니다. 즉, results 배열은 형식화된 메시지를 준비하는 데 사용되는 _prepare_message라는 비공개 함수의 입력으로 사용될 수 있습니다. 이는 메인 함수에서 각 스파이더별로 수행되며, args 배열의 두 번째 인자(스파이더 유형을 나타냄)를 통해 구분이 가능합니다. 스파이더 유형이 homework인 경우 크롤러 프로세스는 HomeworkSpider를 호출하여 시작합니다. 스파이더 유형이 meal인 경우 크롤러 프로세스는 MealSpider를 호출하여 시작합니다.

스파이더가 시작되면 주입된 ResultsPipeline이 results 배열에 데이터를 추가하며, 메인 함수는 각 스파이더별로 _prepare_message를 호출하여 데이터 출력 형식을 지정하는 데 이를 활용할 수 있습니다.

홈워크 과제를 가져오려면 메인 프로젝트 디렉터리에서 새로 구현한 main.py를 다음 명령어로 실행하세요:

python main.py homework

출력 결과는 다음과 같아야 합니다:

...출력 생략...
====과제 목록====
----------------
===수학===
Matematik Konu Anlatımlı Çalışma Defteri-6 13페이지를 작성하세요.

----------------
===ENGLISH===
독서 기록장(Reading Log) 100-107쪽에 있는 "Manny and His Monster Manners" 이야기를 읽고, 이야기 내용에 따라 108쪽과 109쪽의 활동을 완료하세요.

독서 기록장 책의 100-107쪽에 있는 "매니와 그의 괴물 같은 예절"이라는 이야기를 읽고, 108쪽과 109쪽의 활동을 이야기에 따라 완성하세요.

----------------
...출력 생략...

당일 식단 목록을 확인하려면 파이썬 main.py meal 명령어를 실행하세요. 출력 결과는 다음과 같습니다:

...출력 생략...
====식단 목록====
----------------
===아침식사===
팬케이크 
 크림 치즈 
 수즈메 치즈 

초콜릿 호두 크림 
 우유

----------------
===점심===
타르하나 수프
신맛 고프테
에리슈테

----------------
===샐러드/디저트===
아이란
붉은 양배추 샐러드
로카리 고벡 샐러드

----------------
===과일 타임===
호두& 건포도

----------------

...출력 생략...

웹 스크래핑 시 흔히 발생하는 장애물 극복 팁

축하합니다! 여기까지 왔다면 공식적으로 Scrapy 스크레이퍼를 만들었습니다.

Scrapy로 웹 스크래퍼를 만드는 것은 쉽지만, 구현 과정에서 CAPTCHA, IP 차단, 세션 또는 쿠키 관리, 동적 웹사이트와 같은 장애물에 직면할 수 있습니다. 이러한 다양한 시나리오를 처리하는 몇 가지 팁을 살펴보겠습니다:

동적 웹사이트

동적 웹사이트는 시스템 구성, 위치, 연령, 성별 등의 요소에 따라 방문자에게 다른 콘텐츠를 제공합니다. 예를 들어, 동일한 동적 웹사이트를 방문한 두 사람이 각자에게 맞춤화된 서로 다른 콘텐츠를 볼 수 있습니다.

Scrapy는 동적 웹 콘텐츠를 스크래핑할 수 있지만, 이를 위해 설계된 것은 아닙니다. 동적 콘텐츠를 스크래핑하려면 Scrapy를 정기적으로 실행하도록 스케줄링하고, 결과를 저장 및 비교하여 시간 경과에 따른 웹 페이지의 변경 사항을 추적해야 합니다.

특정 경우, 웹 페이지의 동적 콘텐츠는 정적 콘텐츠로 취급될 수 있습니다. 특히 해당 페이지가 가끔씩만 업데이트되는 경우에 해당됩니다.

CAPTCHA

일반적으로 CAPTCHA는 영숫자 문자가 포함된 동적 이미지입니다. 페이지 방문자는 CAPTCHA 이미지의 일치하는 값을 입력해야 검증 과정을 통과할 수 있습니다.

CAPTCHA는 웹 페이지에서 방문자가 인간(스파이더나 봇이 아닌)임을 확인하고, 종종 웹 스크래핑을 방지하기 위해 사용됩니다.

여기에서 작업한 더미 학교 시스템은 CAPTCHA 시스템을 사용하지 않지만, CAPTCHA를 만나게 되는 경우 Scrapy 미들웨어를 만들어 CAPTCHA를 다운로드하고 OCR 라이브러리를 사용하여 텍스트로 변환할 수 있습니다.

세션 및 쿠키 조작

웹 페이지를 열면 해당 페이지 시스템 내에서 세션이 시작됩니다. 이 세션은 로그인 정보 및 기타 관련 데이터를 유지하여 시스템 전반에서 사용자를 식별합니다.

마찬가지로 쿠키를 사용해 웹 페이지 방문자에 대한 정보를 추적할 수 있습니다. 그러나 세션 데이터와 달리 쿠키는 웹사이트 서버가 아닌 방문자의 컴퓨터에 저장되며, 사용자가 원할 경우 삭제할 수 있습니다. 따라서 쿠키로 세션을 유지할 수는 없지만, 데이터 손실이 치명적이지 않은 다양한 보조 작업에 활용할 수 있습니다.

사용자의 세션을 조작하거나 쿠키를 업데이트해야 하는 상황이 발생할 수 있습니다. Scrapy는 내장 기능이나 호환되는 타사 라이브러리를 통해 두 상황 모두 처리할 수 있습니다.

IP 차단

IP 차단(IP 주소 차단)은 웹사이트가 특정 접속 IP 주소를 차단하는 보안 기술입니다. 이 기술은 주로 봇이나 스파이더가 민감한 정보에 접근하는 것을 방지하여 인간 사용자만 데이터에 접근하고 처리할 수 있도록 보장하기 위해 사용됩니다. CAPTCHA와 함께 기업들은 웹 스크래핑 활동을 억제하기 위해 IP 차단을 활용합니다.

이 시나리오에서 학교 시스템은 IP 차단 메커니즘을 사용하지 않습니다. 그러나 만약 이를 구현했다면, 동적 IP를 사용하거나 프록시 벽 뒤에 IP 주소를 숨기는 등의 전략을 채택해야 웹사이트 스크래핑을 계속할 수 있을 것입니다.

결론

이 글에서는 Scrapy를 사용하여 로그인 및 XPath를 통한 테이블 파싱을 위한 스파이더를 만드는 방법을 배웠습니다. 또한 향상된 데이터 제어를 위해 프로그래밍 방식으로 스파이더를 트리거하는 방법도 익혔습니다.

본 튜토리얼의 전체 코드는 이 GitHub 저장소에서 확인할 수 있습니다.

Scrapy의 기능을 확장하고 스크래핑 장애를 극복하고자 하는 분들을 위해 Bright Data는 공개 웹 데이터에 맞춤화된 솔루션을 제공합니다. Bright Data의 Scrapy 통합은 스크래핑 기능을 강화하고, 프록시 서비스는 IP 차단 회피를 돕고, Web Unlocker는 CAPTCHA 및 동적 콘텐츠 처리를 간소화하여 Scrapy를 통한 데이터 수집을 더욱 효율적으로 만듭니다.

지금 등록하시고 당사 데이터 전문가와 스크래핑 솔루션에 대해 상담해 보십시오.