티스토리 뷰

Web Crawling

Scrapy 간단 사용법

nickas 2020. 5. 10. 23:25

설치

$ pip install scrapy

프로젝트 생성

$ scrapy startproject project_name

아래와 같이 디렉토리와 파일들이 생성됨

$ tree project_name
project_name
├── scrapy.cfg
└── project_name
    ├── __init__.py
    ├── items.py
    ├── middlewares.py
    ├── pipelines.py
    ├── settings.py
    └── spiders
        └── __init__.py

간단 spider 생성

Spiders 디렉토리 안에 파이썬 파일 생성

$ cd project_name/project_name/spiders
$ touch example.py

scrapy.Spider를 상속하는 클래스를 생성

# example.py
import scrapy

class ExampleSpider(scrapy.Spider):
    name = 'example'

    def start_requests(self):
        urls = [
            'http://www.example.com/'
        ]

        return [scrapy.Request(url=url, callback=self.parse) for url in urls]
        # 또는
        #for url in urls
        #    yield scrapy.Reqest(url=url, callback=self.parse

    def parse(self, response):
        url = response.url
        title = response.css('h1::text').get()
        print(f'URL is: {url}')
        print(f'Title is: {title}')
  • name (필수)
    spider 이름을 지정. 프로젝트 안에서 유일한 이름이어야 함.
  • start_requests(self) (필수)
    • spider 시작 시 실행되는 메서드로 scrapy.Request()를 사용하여 Request 오브젝트를 만들어 리턴하면 Scrapy 스케쥴러에(queue) 등록되고 순서가 되면 Scrapy downloader가 파라미터로 전달된 url을 사용하여 페이지를 요청.
    • callback 파라미터를 사용하여 downloader가 전달해 주는 Response 오브젝트를 처리할 메서드를 지정.
    • 이 메서드는 spider 시작 시 한번만 실행되기 때문에 위와 같이 Request 오브젝트를 리스트 형식으로 만들어 리턴하거나 yield를 사용하여 generator 형식으로 사용
  • scrapy.Request()
    Request 오브젝트를 만들기 위해 사용.
    • url
      요청할 사이트 주소 http 형식
    • callback
      Response 오브젝트를 처리할 메서드를 지정
  • parse(self, response) (필수)
    • Response오브젝트를 처리하는 callback 메서드로 argument로Response` 오브젝트를 받음.
    • 위 코드와 같이 Request 오브젝트를 만들 때 callback를 사용자가 지정하기 때문에 parse란 이름 대신 다른 이름으로 사용해도 됨.
  • response.css('h1::text').get()
    css selector를 사용하여 h1 태그를 찾고 텍스트를 반환

$ scrapy genspider [template_name] <spider_name> <domain_name> 명령을 사용하면 템플릿을 사용하여 자동으로 템플릿에 맞는 클래스가 작성된 spider_name으로된 파이썬 파일을 spiders 폴더 안에 만듬.
클래스는 아래에 있는 코드가 줄여진 형태.
$ scrapy genspider -l 명령으로 사용할 수 있는 템플릿을 확인할 수 있음.

Scrapy 실행

위에서 정의한 spider를 실행하고 start_requests 메서드를 실행함.

$ scrapy runspider example.py
# 또는 
# scrapy crawl <spider name>

Spider 코드 줄이기

# example.py
import scrapy

class ExampleSpider(scrapy.Spider):
    name = 'example'
    start_urls = [
        'http://www.example.com/'
    ]

    def parse(self, response):
        url = response.url
        title = response.css('h1::text').get()
        print(f'URL is: {url}')
        print(f'Title is: {title}')
  • start_requests 메서드 대신 start_urls를 사용하면 spider가 시작 될 때 알아서 작성된 URL을 사용하여 Request 오브젝트를 생성하고 리턴.
  • callback으로 self.parse 메서드를 사용하기 때문에 이름을 변경하면 안됨.

Item 사용하기

Crawl한 페이지에서 원하는 데이터를 추출한 후 구조적으로 저장하기 위해 사용.
Item이라는 오브젝트에 attribute 형태로 데이터가 저장되고 원할 경우 이 데이터를 JSON이나 CSV 같이 파일로 저장할 수 있음.

물론 파이썬에서 제공하는 open 모듈을 사용하여 parsing 단계에서 파일 형태로 저장할 수 있지만 이런 수고로움 없이 Scrapy에서 편리하게 사용할 수 있는 방법을 제공. 또한 많은 양의 데이터를 파일로 저장할 경우 I/O 병목이나 오버헤드 또는 쓰레드 처리 등등 여러가지 문제들이 발생할 수 있기 때문에 심신의 안정과 평안을 얻기 위해서도 Scrapy에서 제공하는 방법을 사용. 근본적으로 framework를 사용하는 이유이기도……

보통 items.py에 클래스를 정의(그냥 spyder가 정의된 파일에 같이 사용해도 되나 편의상 구분하여 정의)

클래스는 scrapy.Item을 상속해야 하고 데이터들을 저장할 필드를 scrapy.Filed 오브젝트로 만듬.

example 스파이더를 item을 사용하게 변경 하면

# items.py
import scrapy

class ExampleItem(scrapy.Item):
    url = scrapy.Field()
    title = scrapy.Field()

# example.py
import scrapy
from ..items import ExampleItem  # 위에서 정의한 클래스 import

class ExampleSpider(scrapy.Spider):
    name = 'example'
    start_urls = [
        'http://www.example.com/'
    ]

    def parse(self, response):
        example_item = ExampleItem()        # Item 오브젝트 생성
        example_item['url'] = response.url  # 데이터 저장
        example_item['title'] = response.css('h1::text').get()

        return example_item

처음 작성한 코드와 다른 점은 Item 오브젝트는 반드시 리턴되어야 한다는 것이다.
처음 작성한 코드는 명시적으로 데이터를 출력하게 한것이고, 여기서는 오브젝트를 리턴해야 로그에 데이터가 출력도 되고 나중에 파일로 저장될 때 사용되어 진다.

Django를 경험해 본 사람은 item이 Django의 Model과 비슷한걸 느낄 것이다. 실제로 Scrapy 문서에도 유사하다고 나와 있다.

Item 파일로 저장하기

$ scrapy runspider examle.py -o example.csv -t csv
$ scrapy runspider examle.py -o example.json -t json
$ scrapy runspider examle.py -o example.xml -t xml

-t 옵션을 사용하지 않아도 Scrapy가 파일 확장자를 보고 자동으로 확장자에 맞게 저장 해줌.

Logging 사용하기

CLI에서 Scrapy를 실행하면 실행되는 내용이 출력되는데 내용이 길 경우(보통은 긴 내용이 출력됨) 확인하기가 어렵기 때문에 출력 내용을 파일로 저장해서 확인하면 편함.

아래와 같이 settings.py에서 로깅을 설정할 수 있음(CLI에 출력 안됨).

LOG_FILE = 'spider.log'  # log 파일 위치
LOG_LEVEL = 'INFO'       # 기본은 DEBUG 임

또는 Scrapy를 실행 할 때 설정

$ scrapy runspider example.py -s LOG_FILE=spider.log

사이트 제한으로 crawling이 안되는 경우

Scrapy를 실행 할 경우 간혹 사이트에서 제한을 걸어 crawling을 못하는 경우가 발생. 왜냐하면 Scrapy는 실행 시 robots.txt 파일을 확인해서 제한이 걸려 있는 페이지를 crawling 하지 않기 때문.
이 문제를 해결할려면 settings.py에서 ROBOTSTXT_OBEY = True을 False로 수정하면 됨.

(주의)그러나 과도하게 사이트에 부하(crawling)를 주면 IP가 차단될 수도 있음.

과도한 요청으로 인한 차단을 피하는 방법

과도하게 사이트를 crawling 할 경우 사이트에서(관리가 잘되는 곳)에서 IP를 차단 할 수도 있다. 이를 예방하기 위해 DOWNLOAD_DELAYCONCURRENT_REQUESTS 수정해 요청(request)을 조절할 수 있다.

# settings.py
DOWNLOAD_DELAY = 0.25    # 250ms 기다림, 기본값은 안기다림
CONCURRENT_REQUESTS = 1  # 기본값은 16

위 설정은 한 개의 request 후 0.25초를 기다림

  • DOWNLOAD_DELAY은 request 후 얼마를 기다릴 지 설정
  • CONCURRENT_REQUESTS은 한번 request 할 때 동시에 몇개의 request를 할지 설정. 예를 들어 총 100개의 페이지(url)를 요청(request)해야 한다면, 한번 요청할 때 20개 씩 나눠서 요청 할 수 있음.

HTTP cache를 사용

아래와 같이 settings.py에 설정을 하면 cache 기능을 사용할 수 있다.

# settings.py
HTTPCACHE_ENABLED = True  # 기본 값은 False

위와 같이 설정을 하면 한번 request한 페이지 정보들이 프로젝트 디렉토리의 .scrapy 안에 저장(cache)되고 다음에 같은 페이지를 request 할 때 cache를 사용하여 속도를 향상 시킬 수 있다.

'Web Crawling' 카테고리의 다른 글

Scrapy 구조 알아보기  (0) 2020.05.17
최근에 올라온 글
글 보관함