なんでScrapyをやろうと思ったか
世は2019年GWまっただ中。今後の仕事の効率化のため、この時間を活用してスクレーピングやらない手はない。何を効率化するかって?そんなことは、できるようになってから考えるとしよう。
Scrapyとは
An open source and collaborative framework for extracting the data you need from websites.In a fast, simple, yet extensible way.
Webサイトから簡単に欲しい情報を抽出させてやるための仕組み。ということです。ざっくりでも何のためにあるか確認すると理解の助けになりますよね。
前提
- OS:macOS High Sierra 10.13.6
- Python:3.6.1
- Scrapy:1.6.0
準備
仮想環境作成
$ cd [project dir]
$ python -m venv [venv name]
ここでは、[project dir]をscrapy
、[venv name]をvenv
にします。
以降は(venv)
は仮想環境に切り替えている前提とします。([venv name]を別名にしている場合置き換えて下さい。)
ちなみに、仮想環境を作成するコマンドvenv
はPython3.3以上の標準とのことなので、基本的にはこのコマンドを利用するようにする。
仮想環境に切り替える。
$ source venv/bin/activate
プロンプトの先頭が(venv)
になっていることを確認する。
scrapyをインストールします。
(venv)$ pip install scrapy
もしかしたら依存関係でエラーになるかもしれないが、落ち着いて必要なライブラリをインストールしましょう。
インストールしたらScrapyのバージョンを確認します。
(venv)$ scrapy version
Scrapy 1.6.0
試しのScrapyに記載されているサンプルコードmyspider.py
を作成します。
import scrapy
class BlogSpider(scrapy.Spider):
name = 'blogspider'
start_urls = ['https://blog.scrapinghub.com']
def parse(self, response):
"""
blogタイトルを抽出し次のページに移動していく。
"""
for title in response.css('.post-header>h2'):
yield {'title': title.css('a::text').get()}
for next_page in response.css('a.next-posts-link'):
yield response.follow(next_page, self.parse)
-
parse():
リクエストに対してダウンロードされたレスポンスを受け取るための呼び出されるメソッド。response
パラメータはページコンテンツを保持するTextResponse
のインスタンスです。
Scrapyのドキュメント
日本語訳Scrapyドキュメント
作成したmyspider.pyを実行します。
(venv)$ scrapy runspider myspider.py -0 sample.jl
-
jl
:JSON Lines形式のファイル。一度に1レコードずつ処理できる構造化データを格納するのに便利なフォーマットです。(jsonファイルだと、配列かつカンマ区切り列形式でファイル出力されます)
以下出力内容のsample.jl
ファイルが作成されます。
{"title": "From The Creators Of Scrapy: Artificial Intelligence Data Extraction API"}
{"title": "Scrapinghub\u2019s New AI Powered Developer Data Extraction API for E-Commerce & Article Extraction"}
:
Scrapyの開始
Yahoo!ニュースをスクレイピングしてみます。
ScrapyではクロールやスクレイピングにSpiderを利用します。
Spiderのスクレイピングライフサイクル
- 最初のURLをクロールする最初のリクエストを生成し, それらのリクエストからダウンロードされたレスポンスで呼び出されるコールバック関数を指定します.
* 実行する最初のリクエストは,start_requests()
メソッドを呼び出すことによって取得されます. このメソッドは, デフォルトでstart_urls
で指定されたURLとRequest
のコールバック関数として呼ばれるparse
メソッドで,Request
を生成します.- コールバック関数では, レスポンス(Webページ)を解析し, 抽出されたデータ,
Item
オブジェクト,Request
オブジェクト, またはこれらのオブジェクトの反復可能なものを返します これらのリクエストには, コールバックが含まれ, Scrapyによってダウンロードされ, その後指定されたコールバックによってリクエストが処理されます.- コールバック関数では, セレクタ を使用してページの内容を解析します(ただし, BeautifulSoup, lxmlなどの任意のメカニズムを使用することもできます).
- 最後に, スパイダーから返されたアイテムは, 通常, データベース(一部の アイテムパイプライン 内)に永続化されるか, または フィードのエクスポート を使用してファイルに書き込まれます.
引用:https://scrapy-ja.readthedocs.io/ja/latest/topics/spiders.html
Scrapyプロジェクトを作成
以下コマンドを実行してプロジェクトを作成します。
ここでは[project name]をmyproject
としてます。
(venv)$ scrapy startproject [project name]
...
(venv)$ cd myproject
(venv)$ tree .
.
├── myproject
│ ├── __init__.py
│ ├── __pycache__
│ ├── items.py
│ ├── middlewares.py
│ ├── pipelines.py
│ ├── settings.py
│ └── spiders
│ ├── __init__.py
│ └── __pycache__
└── scrapy.cfg
まずは、最低限の環境設定を行います。
settings.pyのDOWNLOAD_DELAY
のコメントを削除しWebサイトへのリクエストの間隔を秒単位に設定します。(接続先に負荷を与えないためにも必ず設定すること)
#DOWNLOAD_DELAY = 3
↓
DOWNLOAD_DELAY = 3
また、スクレイピングの結果をファイル出力する際の文字コードを追記し指定します。
FEED_EXPORT_ENCODING = 'utf-8'
Itemを作成
スクレイピングは構造化されていないソース(通常はWebサイト)から構造化データを抽出することである。ScrapyではItem
クラスを利用することでPythonの扱いやすい構造に収める事ができます。Item
クラスはscrapy.Item
を継承します。
import scrapy
class NewsTopics(scrapy.Item):
title = scrapy.Field()
summary = scrapy.Field()
Spiderを作成
(venv)$ scrapy genspider news news.yahoo.co.jp
Created spider 'news' using template 'basic' in module:
myproject.spiders.news
spidersフォルダにnews.py
ファイルが作成されます。
# -*- coding: utf-8 -*-
import scrapy
class NewsSpider(scrapy.Spider):
name = 'news'
allowed_domains = ['news.yahoo.co.jp']
start_urls = ['http://news.yahoo.co.jp/']
def parse(self, response):
for url in response.css('ul.topicsList_main a::attr("href")').re(r'/pickup/\d+$'):
yield scrapy.Request(response.urljoin(url), self.parse_topics)
def parse_topics(self, response):
item = Headline()
item['title'] = response.css('title ::text').extract_first()
item['summary'] = response.css('.tpcNews_summary').xpath(
'string()').extract_first()
yield item
Spiderの実行
(venv)$ scrapy crawl news -o topics.jl
topics.jl
にスクレイピングした結果が出力されていることを確認します。
ここまでで、Scrapyを利用したクローリングとスクレイピングのやり方になります。
書簡としては、WebアプリケーションでいうとこのDjangoのように、スクレイピングのフレームワークだけあり小さく始めるにもとっつきやすいと思います。他にもパイプラインやシェルなどの仕組みがあるみたい。
今後調べたいこと。
- 機械学習用のデータを収集方法について
- クローリングとスクレイピングは機構を分けたほうがよいので、Scrapyを利用した分離方法について
- SPAやログインが必要なサイトのクローリング方法について
終了