Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

파이썬 - 웹 페이지 데이터 수집을 위한 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()
[...[생략]...]




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 10/1/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 136  137  138  139  140  141  [142]  143  144  145  146  147  148  149  150  ...
NoWriterDateCnt.TitleFile(s)
1504정성태9/24/201330270.NET Framework: 387. UDP 브로드캐스팅을 이용해 서비스 측의 IP 주소를 구하는 방법 [1]파일 다운로드1
1503정성태9/21/201335416개발 환경 구성: 199. Visual Studio - github 연동 [7]
1502정성태9/21/201338985개발 환경 구성: 198. Visual Studio - git을 이용한 로컬 소스 컨트롤
1501정성태9/21/201346097개발 환경 구성: 197. Visual Studio를 위한 Git 환경 설정 [5]
1500정성태9/20/201345077.NET Framework: 386. C# 버전의 한글 형태소 분석기 [1]파일 다운로드1
1499정성태9/20/201321660개발 환경 구성: 196. Windows Azure - Cloud Service의 인스턴스 타입 변경하는 방법
1498정성태9/20/201327798Windows: 76. 윈도우 8.1 / 서버 2012 R2 마이그레이션 [5]
1497정성태9/20/201360063웹: 28. IE 11로 바꾼 후 발생하는 문제 정리
1496정성태9/20/201332381Windows: 75. 윈도우 8.1, 2012 R2 설치 후 원격 접속이 안 되는 문제
1495정성태9/20/201323530웹: 27. IE 11 - YBM Sisa.com에서 검색된 영단어의 발음 기호가 안 나오는 문제
1494정성태9/13/201333107.NET Framework: 385. Html Agility Pack 소개 - 웹 문서에서 텍스트만 분리하는 방법 [2]파일 다운로드1
1493정성태9/13/201334887.NET Framework: 384. WebClient.DownloadString 문자열 인코딩 문제
1492정성태9/13/201322339오류 유형: 186. The .NET assembly 'Microsoft.Vsa' could not be found.
1491정성태9/9/201325466.NET Framework: 383. RSAParameters의 ToXmlString과 ExportParameters의 결과 비교
1490정성태9/7/201360470기타: 34. 도서: 시작하세요! C# 프로그래밍: 기본 문법부터 실전 예제까지 [7]
1489정성태9/4/201344911오류 유형: 185. 오피스 워드 파일이 저장되지 않는 문제 [2]
1488정성태8/27/201329048.NET Framework: 382. WCF에서 DataSet을 binary encoding으로 직렬화하는 방법파일 다운로드1
1487정성태8/27/201331354개발 환경 구성: 195. 로컬 PC에서의 WCF 통신을 Fiddler로 보는 방법 [1]
1486정성태8/27/201328856.NET Framework: 381. SqlCommand를 이용해 Microsoft SQL 서버의 쿼리 실행 계획을 구하는 방법파일 다운로드1
1485정성태8/26/201332555.NET Framework: 380. 프로세스 스스로 풀 덤프 남기는 방법 [3]파일 다운로드1
1484정성태8/23/201326802제니퍼 .NET: 24. 제니퍼 닷넷 적용 사례 (4) - GZIP 인코딩으로 인한 성능 하락
1483정성태8/23/201326924.NET Framework: 379. System.IO.MemoryStream, ArraySegment&lt;T&gt; 의 효율적인 사용법 [1]
1482정성태8/23/201320360.NET Framework: 378. Java / C# - 정수의 부호 유무에 따른 16진수 문자열 변환
1481정성태8/22/201321182오류 유형: 184. PaaS 유형(Cloud Services)의 Azure VM에 연결할 때 계정 만료 에러가 발생하는 경우
1480정성태8/22/201337860개발 환경 구성: 194. 윈도우 서버의 80 포트에 대한 port forwarding 설정 방법파일 다운로드1
1479정성태8/14/201325188오류 유형: 183. IIS - 바인딩 추가 시 Object reference not set to an instance of an object 오류 [5]
... 136  137  138  139  140  141  [142]  143  144  145  146  147  148  149  150  ...