Python을 배워보자

Scrapy: 강력한 파이썬 웹 크롤링 프레임워크를 활용한 데이터 수집 가이드

_Blue_Sky_ 2024. 12. 25. 22:06
728x90
728x90

 

 

 

Scrapy는 파이썬으로 작성된 오픈 소스 웹 크롤링 프레임워크입니다. 웹 사이트에서 필요한 데이터를 자동으로 추출하여 원하는 형식으로 저장하는 작업을 웹 크롤링이라고 하는데, Scrapy는 이러한 작업을 효율적이고 체계적으로 수행하도록 도와주는 강력한 도구입니다.

Scrapy의 주요 특징

  • 빠르고 효율적: 비동기 네트워킹 라이브러리인 Twisted를 기반으로 하여 빠른 속도를 자랑하며, 대규모 웹 사이트를 크롤링하는 데 적합합니다.
  • 유연하고 확장성이 좋음: 다양한 데이터 저장 방식, 미들웨어, 아이템 파이프라인 등을 통해 커스터마이징이 가능하여 복잡한 크롤링 작업에도 유용하게 활용할 수 있습니다.
  • 간편한 사용: 파이썬 기반으로 개발되어 Python 문법에 익숙한 개발자라면 쉽게 학습하고 사용할 수 있습니다.
  • 강력한 기능: XPath, CSS 선택자 등을 이용하여 HTML에서 데이터를 추출하고, 다양한 형식으로 저장할 수 있습니다.

Scrapy를 사용하는 이유

  • 복잡한 웹 사이트 크롤링: 자바스크립트로 동적으로 생성되는 콘텐츠, 다양한 레이아웃을 가진 웹 페이지 등 복잡한 구조의 웹 사이트에서도 효과적으로 데이터를 추출할 수 있습니다.
  • 대규모 데이터 수집: 많은 양의 데이터를 빠르고 안정적으로 수집해야 할 때 Scrapy는 최적의 선택입니다.
  • 데이터 파이프라인 구축: 수집한 데이터를 정제하고 분석하기 위한 파이프라인을 구축할 수 있습니다.

Scrapy의 기본적인 작동 방식

  1. Spider 생성: 크롤링할 웹 사이트와 추출할 데이터를 정의하는 클래스입니다.
  2. Request 생성: 크롤링할 URL을 포함하는 Request 객체를 생성합니다.
  3. Response 처리: Request에 대한 Response를 받아 HTML을 파싱하고 데이터를 추출합니다.
  4. Item 생성: 추출한 데이터를 저장하기 위한 Item 객체를 생성합니다.
  5. Item Pipeline: Item을 처리하고 저장하는 파이프라인입니다.

Scrapy의 활용 예시

  • 뉴스 기사 수집: 다양한 뉴스 사이트에서 특정 키워드를 포함하는 기사를 수집하여 분석
  • 제품 정보 수집: 온라인 쇼핑몰의 제품 정보를 수집하여 가격 비교
  • SNS 데이터 분석: SNS에서 특정 해시태그를 가진 게시물을 수집하여 분석
  • 웹 사이트 모니터링: 웹 사이트의 변경 사항을 모니터링

 

728x90
import scrapy

class ExampleSpider(scrapy.Spider):
    # 스파이더 이름 설정
    name = 'example_spider'

    # 크롤링 허용 도메인 설정 (example.com 에서만 크롤링하도록 설정)
    allowed_domains = ['example.com']

    # 시작 URL 설정 (크롤링을 시작하는 첫 페이지)
    start_urls = ['https://example.com']

    def parse(self, response):
        # 웹 페이지의 타이틀 추출
        title = response.css('title::text').get()
        print(f"** Main Page Title: {title} **")

        # 모든 a 태그의 href 속성값 추출 (링크 목록)
        hrefs = response.css('a::attr(href)').getall()

        # 추출한 링크 목록을 반복하며 follow_link 함수 호출
        for href in hrefs:
            yield scrapy.Request(href, callback=self.parse_details)

    def parse_details(self, response):
        # 상세 페이지의 모든 p 태그 안의 텍스트 추출
        content = response.css('p::text').getall()
        content = "\n".join(content)  # 리스트를 한 줄 문자열로 변환
        print(f"\n** Detail Page Content: \n{content} **")

        # 추출한 내용을 사전(dict) 형태로 yield
        yield {'content': content}

설명

  1. import scrapy: scrapy 라이브러리를 불러옵니다.
  2. class ExampleSpider(scrapy.Spider): ExampleSpider라는 이름의 스파이더 클래스를 정의합니다.
  3. name = 'example_spider': 스파이더의 이름을 설정합니다.
  4. allowed_domains = ['example.com']: 크롤링을 허용하는 도메인을 설정합니다. 이 코드에서는 'example.com' 에서만 크롤링하도록 설정되어 있습니다.
  5. start_urls = ['https://example.com']: 크롤링을 시작하는 첫 페이지 URL을 설정합니다.
  6. def parse(self, response):: 웹 페이지를 다운로드 받으면 실행되는 메서드입니다.
    • title = response.css('title::text').get(): response 객체에서 title 태그의 텍스트를 추출합니다.
    • print(f" Main Page Title: {title} "): 추출한 타이틀을 출력합니다.
    • hrefs = response.css('a::attr(href)').getall(): response 객체에서 모든 a 태그의 href 속성값을 리스트 형태로 추출합니다.
    • for href in hrefs: hrefs 리스트를 반복하며 각 링크에 대해 다음을 수행합니다.
      • yield scrapy.Request(href, callback=self.parse_details): 추출한 링크(href)에 대해 새로운 Request 객체를 생성합니다. callback 매개변수에는 parse_details 메서드를 지정합니다.
  7. def parse_details(self, response):: 상세 페이지를 다운로드 받으면 실행되는 메서드입니다.
    • content = response.css('p::text').getall(): response 객체에서 모든 p 태그 안의 텍스트를 리스트 형태로 추출합니다.
    • content = "\n".join(content): 리스트에 있는 문자열들을 줄 바꿈 문자(\n)로 연결하여 하나의 문자열로 만듭니다.
    • print(f"\n Detail Page Content: \n{content} "): 추출한 내용을 출력합니다.
    • yield {'content': content}: 추출한 내용을 'content' 키값을 가지는 사전(dict) 형태로 yield합니다.

 

728x90

예시 HTML

<!DOCTYPE html>
<html>
<head>
  <title>Example Website</title>
</head>
<body>
  <h1>This is the main page</h1>
  <p>This is some content on the main page.</p>
  <a href="detail_page1.html">Go to detail page 1</a>
  <a href="detail_page2.html">Go to detail page 2</a>
</body>
</html>

실행 결과

Running scrapy crawl example_spider
** Main Page Title: Example Website **

** Detail Page Content:
 

 


다른예

 

import scrapy


class NewsSpider(scrapy.Spider):
    # 스파이더 이름 설정
    name = 'news_spider'

    # 크롤링 허용 도메인 설정
    allowed_domains = ['news.example.com']

    # 시작 URL 설정
    start_urls = ['https://news.example.com/']

    def parse(self, response):
        # 뉴스 기사 목록 추출 (h2 태그 안에 a 태그가 있는 구조)
        news_links = response.css('h2 a::attr(href)')

        # 추출한 링크 목록을 반복하며 상세 페이지 크롤링
        for news_link in news_links:
            yield scrapy.Request(news_link.get(), callback=self.parse_news)

    def parse_news(self, response):
        # 뉴스 제목 추출
        title = response.css('h1::text').get()

        # 뉴스 본문 p 태그 안의 텍스트 추출 (리스트 형태)
        content = response.css('p::text').getall()
        # 리스트를 하나의 문자열로 변환
        content = "\n".join(content)

        # 뉴스 작성 시간 추출 (span 태그 클래스명이 "date")
        date = response.css('span.date::text').get()

        # 추출한 데이터 사전(dict) 형태로 yield
        yield {
            'title': title,
            'content': content,
            'date': date,
        }


# 예시 HTML (상세 페이지 포함)
html_content = """
<!DOCTYPE html>
<html>
<head>
  <title>News Website</title>
</head>
<body>
  <h1>This is the main page</h1>
  <p>This is some content on the main page with links to news articles.</p>
  <a href="article_1.html">News Article 1</a>
  <a href="article_2.html">News Article 2</a>
  <article>
    <h1>This is News Article 1</h1>
    <p>This is the content of the first news article.</p>
    <p>This is another paragraph of content.</p>
    <span class="date">2024-12-26</span>
  </article>
</body>
</html>
"""

# 실행 결과 (콘솔 출력)
# {'title': 'This is News Article 1', 'content': 'This is the content of the first news article.\nThis is another paragraph of content.', 'date': '2024-12-26'}
# ... (다른 뉴스 기사 결과도 출력)

설명

  1. import scrapy: scrapy 라이브러리를 불러옵니다.
  2. class NewsSpider(scrapy.Spider): NewsSpider라는 이름의 스파이더 클래스를 정의합니다.
  3. name = 'news_spider': 스파이더의 이름을 설정합니다.
  4. allowed_domains = ['news.example.com']: 크롤링을 허용하는 도메인을 설정합니다.
  5. start_urls = ['https://news.example.com/']: 크롤링을 시작하는 첫 페이지 URL을 설정합니다.
  6. def parse(self, response):: 웹 페이지를 다운로드 받으면 실행되는 메서드입니다.
    • news_links = response.css('h2 a::attr(href)'): h2 태그 안에 a 태그가 있는 모든 링크의 href 속성값을 추출합니다.
    • for news_link in news_links: 추출한 링크 목록을 반복하며 각 뉴스 기사 상세 페이지로 이동합니다.
      • yield scrapy.Request(news_link.get(), callback=self.parse_news): 추출한 링크에 대해 새로운 Request 객체를 생성합니다. callback 매개변수에는 parse_news 메서드를 지정합니다.
  7. def parse_news(self, response):: 상세 페이지를 다운로드 받으면 실행되는 메서드입니다.
    • title = response.css('h1::text').get(): 뉴스 제목을 h1 태그의 텍스트 내용으로 추출합니다.
    • content = response.css('p::text').getall(): 뉴스 본문의 모든 p 태그 안
728x90
728x90