파이썬 - 웹 페이지 데이터 수집을 위한 scrapy Crawler 사용법 요약
윈도우 사용자라고 해도, 간단한 실습에 불과하니 쓸데없이 ^^ 디렉터리를 어지럽히지 말고 WSL에 맡기면 좋습니다. 게다가 파이썬 환경이니 virtualenv로 한 번 더 격리를 하면 좋겠지요. ^^
$ cd ~
~$ mkdir pyenv
~$ cd pyenv
~/pyenv$ virtualenv scraptest
~/pyenv$ source scraptest/bin/activate
(scraptest) testusr@TESTPC:~/pyenv$
scrapy를 설치하고,
(scraptest) testusr@TESTPC:~/pyenv$ pip install scrapy
(scraptest) testusr@TESTPC:~/pyenv$ python --version
Python 3.8.10
(scraptest) testusr@TESTPC:~/pyenv$ scrapy --version
Scrapy 2.5.0 - no active project
Usage:
scrapy <command> [options] [args]
Available commands:
bench Run quick benchmark test
commands
fetch Fetch a URL using the Scrapy downloader
genspider Generate new spider using pre-defined templates
runspider Run a self-contained spider (without creating a project)
settings Get settings values
shell Interactive scraping console
startproject Create new project
version Print Scrapy version
view Open URL in browser, as seen by Scrapy
[ more ] More commands available when run from project directory
Use "scrapy <command> -h" to see more info about a command
프로젝트를 하나 만듭니다.
(scraptest) testusr@TESTPC:~/pyenv$ cd scraptest/
(scraptest) testusr@TESTPC:~/pyenv/scraptest$
(scraptest) testusr@TESTPC:~/pyenv/scraptest$ scrapy startproject sample1
New Scrapy project 'sample1', using template directory '/home/testusr/pyenv/scraptest/lib/python3.8/site-packages/scrapy/templates/project', created in:
/home/testusr/pyenv/scraptest/sample1
You can start your first spider with:
cd sample1
scrapy genspider example example.com
(scraptest) testusr@TESTPC:~/pyenv/scraptest$ cd sample1/
(scraptest) testusr@TESTPC:~/pyenv/scraptest/sample1$
스파이더를 생성하고,
(scraptest) testusr@TESTPC:~/pyenv/scraptest/sample1$ scrapy genspider sysnet sysnet.pe.kr
Created spider 'sysnet' using template 'basic' in module:
sample1.spiders.sysnet
(scraptest) testusr@TESTPC:~/pyenv/scraptest/sample1$ tree
.
├── sample1
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-38.pyc
│ │ └── settings.cpython-38.pyc
│ ├── items.py
│ ├── middlewares.py
│ ├── pipelines.py
│ ├── settings.py
│ └── spiders
│ ├── __init__.py
│ ├── __pycache__
│ │ └── __init__.cpython-38.pyc
│ └── sysnet.py
└── scrapy.cfg
(scraptest) testusr@TESTPC:~/pyenv/scraptest/sample1$ cat sample1/spiders/sysnet.py
import scrapy
class SysnetSpider(scrapy.Spider):
name = 'sysnet'
allowed_domains = ['sysnet.pe.kr']
start_urls = ['http://sysnet.pe.kr/']
def parse(self, response):
pass
여기서, scrap 관련한 코드를 원하는 목적에 맞게 수정해야 합니다. 가령 제 경우에는 제 웹 사이트에 있는 글의 "제목"을 스크립하고 싶은데요, 다음과 같은 식으로 위의 코드를 변경해야 합니다.
# items.py의 기본 내용을 다음과 같이 변경
import scrapy
class Sample1Item(scrapy.Item):
title = scrapy.Field()
# ./spiders/sysnet.py의 기본 내용을 다음과 같이 변경
import scrapy
from ..items import Sample1Item
class SysnetSpider(scrapy.Spider):
name = 'sysnet'
allowed_domains = ['www.sysnet.pe.kr']
start_urls = ['https://www.sysnet.pe.kr/']
def start_requests(self):
maxPage = 5
for i in range(0, maxPage):
yield scrapy.Request(self.start_urls[0] + "Default.aspx?mode=2&sub=0&pageno={0}".format(i), self.parse)
def parse(self, response):
items = []
# CSS 예제 (1)
# for article in response.css('#contentPane > table.postlist > tr'):
# item = Sample1Item()
# item['title'] = article.css("td:nth-child(5) > a::text").extract()[0]
# items.append(item)
# CSS 예제 (1)
# for article in response.css('#contentPane > table.postlist > tr > td:nth-child(5) > a::text'):
# item = Sample1Item()
# item['title'] = article.extract()
# items.append(item)
# XPath 예제 (1)
# for article in response.xpath('//*[@id="contentPane"]/table[2]/tr'):
# item = Sample1Item()
# item['title'] = article.xpath("td[5]/a/node()").extract()[0]
# items.append(item)
# XPath 예제 (2)
for article in response.xpath('//*[@id="contentPane"]/table[2]/tr/td[5]/a/node()'):
item = Sample1Item()
item['title'] = article.extract()
items.append(item)
print(item['title'])
return items
# pipelines.py의 기본 내용을 다음과 같이 변경
from itemadapter import ItemAdapter
from scrapy.exporters import JsonItemExporter
import codecs
class Sample1Pipeline:
def process_item(self, item, spider):
return item
class JsonPipeline:
def __init__(self):
self.file = open('result.json', 'wb')
self.exporter = JsonItemExporter(self.file, encoding='utf-8', ensure_ascii=False)
self.exporter.start_exporting()
def process_item(self, item, spider):
self.exporter.export_item(item)
return item
def close_spider(self, spider):
self.exporter.finish_exporting()
self.file.close()
# settings.py에 ITEM_PIPELINES 설정을 새롭게 추가
# ...[생략]...
ITEM_PIPELINES = {
'sample1.pipelines.JsonPipeline': 300,
}
# ...[생략]...
이렇게 하고, scrapy를 다음과 같이 실행하면,
(scraptest) testusr@TESTPC:~/pyenv/scraptest/sample1$ scrapy crawl sysnet
오류 없이 실행되었으면 result.json 파일에 다음과 같이 5페이지에 해당하는 분량의 제목이 추출된 것을 볼 수 있습니다. ^^
[{"title": "기부/후원"},{"title": ".NET Framework: 1113. C# 10 - (13) 문자열 보간 성능 개선"},{"title": "개발 환경 구성: 603. GoLand - WSL 환경과 연동"},...[생략]...,{"title": ".NET Framework: 1080. xUnit 단위 테스트에 메서드/클래스 수준의 문맥 제공 - Fixture"},{"title": ".NET Framework: 1079. MSTestv2 단위 테스트에 메서드/클래스/어셈블리 수준의 문맥 제공"},{"title": ".NET Framework: 1078. C# 단위 테스트 - MSTestv2/NUnit의 Assert.Inconclusive 사용법(?)"}]
위에서, Spider의 parse 함수 내의 코드에서 결과물을 XPath 또는 CSS를 이용해 select하는 것이 어려울 때는 scrapy를 명령행에서 실행시켜 테스트하는 것이 더 편합니다.
일례로, 위의 경우 페이지 하나를 scrap 하는 다음의 명령을 실행하고,
$ scrapy shell 'https://www.sysnet.pe.kr/Default.aspx?mode=2&sub=0&pageno=0'
...[생략]...
021-09-04 16:56:06 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.sysnet.pe.kr/Default.aspx?mode=2&sub=0&pageno=0> (referer: None)
[s] Available Scrapy objects:
[s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s] crawler <scrapy.crawler.Crawler object at 0x7f063aad5100>
[s] item {}
[s] request <GET https://www.sysnet.pe.kr/Default.aspx?mode=2&sub=0&pageno=0>
[s] response <200 https://www.sysnet.pe.kr/Default.aspx?mode=2&sub=0&pageno=0>
[s] settings <scrapy.settings.Settings object at 0x7f063aad2d90>
[s] spider <SysnetSpider 'sysnet' at 0x7f063a7a4100>
[s] Useful shortcuts:
[s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s] fetch(req) Fetch a scrapy.Request and update local objects
[s] shelp() Shell help (print this help)
[s] view(response) View response in a browser
>>>
진입한 shell 모드에서 다음과 같은 식으로 테스트해 볼 수 있습니다.
>>> response.css('#contentPane > table.postlist > tbody').getall()
[]
>>> response.css('#contentPane > table.postlist > tr').getall()
[...[생략]...]
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]