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

비밀번호

댓글 작성자
 




... 46  47  48  49  50  51  52  53  54  55  56  57  [58]  59  60  ...
NoWriterDateCnt.TitleFile(s)
12197정성태3/17/202011247VS.NET IDE: 144. .NET Core 콘솔 응용 프로그램을 배포(publish) 시 docker image 자동 생성 - 두 번째 이야기 [1]
12196정성태3/17/20209149오류 유형: 608. The ServicedComponent being invoked is not correctly configured (Use regsvcs to re-register).
12195정성태3/16/202010918.NET Framework: 902. C# - 프로세스의 모든 핸들을 열람 - 세 번째 이야기
12194정성태3/16/202013201오류 유형: 607. PostgreSQL - Npgsql.NpgsqlException: sorry, too many clients already
12193정성태3/16/20209929개발 환경 구성: 485. docker - SAP Adaptive Server Enterprise 컨테이너 실행 [1]
12192정성태3/14/202012363개발 환경 구성: 484. docker - Sybase Anywhere 16 컨테이너 실행
12191정성태3/14/202012742개발 환경 구성: 483. docker - OracleXE 컨테이너 실행 [1]
12190정성태3/14/20208815오류 유형: 606. Docker Desktop 업그레이드 시 "The process cannot access the file 'C:\Program Files\Docker\Docker\resources\dockerd.exe' because it is being used by another process."
12189정성태3/13/202013722개발 환경 구성: 482. Facebook OAuth 처리 시 상태 정보 전달 방법과 "유효한 OAuth 리디렉션 URI" 설정 규칙
12188정성태3/13/202016271Windows: 169. 부팅 시점에 실행되는 chkdsk 결과를 확인하는 방법
12187정성태3/12/20208596오류 유형: 605. NtpClient was unable to set a manual peer to use as a time source because of duplicate error on '...'.
12186정성태3/12/20209751오류 유형: 604. The SysVol Permissions for one or more GPOs on this domain controller and not in sync with the permissions for the GPOs on the Baseline domain controller.
12185정성태3/11/202010476오류 유형: 603. The browser service was unable to retrieve a list of servers from the browser master...
12184정성태3/11/202011834오류 유형: 602. Automatic certificate enrollment for local system failed (0x800706ba) The RPC server is unavailable. [3]
12183정성태3/11/202010226오류 유형: 601. Warning: DsGetDcName returned information for \\[...], when we were trying to reach [...].
12182정성태3/11/202011398.NET Framework: 901. C# Windows Forms - Vista/7 이후의 Progress Bar 업데이트가 느린 문제파일 다운로드1
12181정성태3/11/202012154기타: 76. 재현 가능한 최소한의 예제 프로젝트란? - 두 번째 예제파일 다운로드1
12180정성태3/10/20208774오류 유형: 600. "Docker Desktop for Windows" - EXPOSE 포트가 LISTENING 되지 않는 문제
12179정성태3/10/202020252개발 환경 구성: 481. docker - PostgreSQL 컨테이너 실행
12178정성태3/10/202011778개발 환경 구성: 480. Linux 운영체제의 docker를 위한 tcp 바인딩 추가 [1]
12177정성태3/9/202011346개발 환경 구성: 479. docker - MySQL 컨테이너 실행
12176정성태3/9/202010776개발 환경 구성: 478. 파일의 (sha256 등의) 해시 값(checksum) 확인하는 방법
12175정성태3/8/202010899개발 환경 구성: 477. "Docker Desktop for Windows"의 "Linux Container" 모드를 위한 tcp 바인딩 추가
12174정성태3/7/202010475개발 환경 구성: 476. DockerDesktopVM의 파일 시스템 접근 [3]
12173정성태3/7/202011468개발 환경 구성: 475. docker - SQL Server 2019 컨테이너 실행 [1]
12172정성태3/7/202016313개발 환경 구성: 474. docker - container에서 root 권한 명령어 실행(sudo)
... 46  47  48  49  50  51  52  53  54  55  56  57  [58]  59  60  ...