UnsplashのKrzysztof Niewolnyが撮影した写真
Scrapyは非常に便利だが、何かをスクレイピングしたいというニーズは、そんなに頻繁に起こるわけではないので、そんなことがあるたびに、毎回、使い方をググっている。いい加減めんどうくさいので、簡単な使い方を、ここにまとめておくことにした。
ここでは、ざっくりとした手順だけを書いておき、各々のステップの詳細は別途ググるつもりで書いている。
B-Leagueの試合日程を公式ページからスクレイピングするのに使ったプロジェクトをサンプルとしておいておくlink。
以下のコマンド実行例やコードのサンプルは、このサンプルを念頭に書かれている。
主な手順
Scrapyでスクレピングをするときには、以下のようなステップを踏むことになる。
Scrapyプロジェクトの構成や、Item・Spider・PipelineといったScrapy用語については、以下に述べるステップの詳細の中で説明する。
- プロジェクトの作成
- Spiderの作成
- Itemの定義
- Pipelineの定義
- 設定ファイルの調整
プロジェクトの作成
- プロジェクト用のディレクトリを作る
- poetryで仮想環境を作る
- scrapyをインストールする
- poetryの仮想環境に入る
$ mkdir scraping-bleague
$ cd scraping-bleague
$ poetry init
$ poetry add scrapy
$ poetry shell
するとscrapy
コマンドが使えるようになる。これを使って、プロジェクトを初期化する。
$ scrapy startproject bleague
これによって、Scrapyプロジェクトの雛形が出来上がる。
Scrapyプロジェクトは、主に
- 設定ファイル
- Spiderの定義: どのURLを訪問するか・そのURLが指すリソースからどうやって情報を取得するか、などを書くところ
- Itemの定義: 取得していようとしている情報の形を定義するところ
- Pipelineの定義: 取得した情報を、どうやって処理する(DBに保存したらファイルに書いたり統計処理をしたり)かを書くところ
といった構成要素からなる。上のコマンドで、spider以外の雛形が作成される。
以下のような雛形が作成されるはず。
scraping-bleague
├── bleague
│ ├── bleague
│ │ ├── __init__.py
│ │ ├── items.py
│ │ ├── middlewares.py
│ │ ├── pipelines.py
│ │ ├── settings.py
│ │ └── spiders
│ │ └── __init__.py
│ └── scrapy.cfg
├── poetry.lock
└── pyproject.toml
追記: 上のコマンドだと、scraping-bleague/bleague/bleague以下に.pyファイルを配置していくことになる。深すぎる場合は、pyproject.toml, poetry.lockファイルと同じ階層にコードを移してしまっても良い。
以下のコマンドは、全てscrapy.cfg
ファイルが存在する場所で実行する・・ので、そこに移動しておく。
cd bleague
Spiderの作成
Scrapyでは、訪問するURLや、得られたリソースをどうやってパースするかといった情報はSpiderクラスの子クラスとして定義する。
以下のコマンドで、www.bleague.jp
ドメイン以下のURLからスクレピングするためのSpiderの雛形が作成される。
$ scrapy genspider bleague-match www.bleague.jp
これによって、以下のようなscraping-bleague/bleague/bleague/spiders/bleague_match.py
ファイルができる。
import scrapy
class BleagueMatchSpider(scrapy.Spider):
name = 'bleague-match'
allowed_domains = ['www.bleague.jp']
start_urls = ['http://www.bleague.jp/']
def parse(self, response):
pass
この段階で、とりあえずクローラを走らせることができる。
$ scrapy crawl bleague-match
spiderの書き方
spiderを書くにあたって必要な基礎知識を箇条書きしておく。
- spiderは、最初に
start_urls
にあるurlをgetする - getの結果が、
response
オブジェクトに入った状態でparse
が呼ばれる-
response.xpath
などで要素にアクセスできる
-
- 必要な情報を取得したら、それを辞書またはItem(詳細は後述)クラスのオブジェクトにまとめて
yield
する - 訪問したページからリンクを取得して別のページを訪問したい場合は
response.follow()
をyield
する -
scrapy crawl <spider name> -a <key>=<value>
という形で、コマンド引数を利用できる。ここで与えた値は、spiderの__init__
の引数として受け取ることができる。コマンドで与えるkey
が__init__
の引数名になる。この値は、文字列として渡ってくるので、適宜パースが必要
B-leagueのトップページからaタグを2つ抜き出し、そのリンク先のタイトルを取得する例
class BleagueMatchSpider(scrapy.Spider):
name = 'bleague-match'
allowed_domains = ['www.bleague.jp']
start_urls = ['http://www.bleague.jp/']
def parse(self, response):
links = response.xpath("//a")
cnt = 0
i = 0
while cnt < 2:
url = links[i].attrib["href"]
if any(map(lambda d: d in url, self.allowed_domains)):
cnt += 1
yield response.follow(url, self.get_title)
i += 1
def get_title(self, response):
title = response.xpath("//title/text()").get()
yield dict(title=title)
開発用のrepl
from scrapy.shell import inspect_response
inspect_response(response, self)
というコードを書いておくと、クロール中に上記のコードが評価される時点で、対話環境に入ることができる。この中で、response
の中身などを見ることができるので便利。view(response)
で取得したhtmlをブラウザで見ることができる。
Itemの定義
自動生成される雛形の中に、scraping-bleague/bleague/bleague/items.py
というファイルがある。
この中に、スクレイピングによって取得したデータの形式をItem
クラスのサブクラスとして定義する。
ここにItemとして定義せずに、dict
を利用して任意のデータ形式を使うこともできる。
Pipelineの定義
自動生成される雛形の中に、scraping-bleague/bleague/bleague/pipelines.py
というファイルがある。
この中に、取得したItem
(またはdict
)をどうやって処理するかを書く。
Pipelineの書き方
pipelineを書くにあたって必要な基礎知識を箇条書きしておく。
-
item
に、spiderがyieldしたオブジェクトが渡される -
spider
には、それをyieldしたSpiderオブジェクトが渡される - pipelineは(名前から想像されるように)、複数定義して、それらを順番に適用するように想定されている。そのため、
process_item
の最後に、渡されたitemをreturn
して、次の処理にオブジェクトを渡す - 適用されるPipelineや、それらの順番は、
scraping-bleague/bleague/bleague/settings.py
のITEM_PIPELINES
で指定する- なので、Pipelineを定義したら、この項目を編集しなければ処理に反映されない
Itemからtitleを抜き出して、/tmp/titles.txtに追記していく例
class BleaguePipeline:
def process_item(self, item, spider):
title = item["title"]
with open("/tmp/titles.txt", "a") as fp:
fp.write(title + "\n")
return item
ここまでを実装して、scrapy crawl bleague-match
を実行すると/tmp/titles.txt
に値が入るはず。
設定の調整
クローラの設定は、scraping-bleague/bleague/bleague/settings.py
で記述される。
- robots.txtに従うか or Not
- 同一ドメインに同時に投げるリクエストの数
などを、ここでコントロールできる。
トラブルシュート
Mac OS + pyenvで作業した際に、_lzmaというパッケージがないというエラーを吐いた。この方法で対処link