Scrapyのスクレピングが簡単すぎて今更感動した話
僕はPHP
でスクレイピングする時はGoutteを使っていた。
サッやりたい時とかは便利だったりするが、robots.txt
の中身だったりの確認やページング処理については自分で実装が必要なため手間だなと思っていた。
ふと最近Python
をよく使ってるし、スクレイピング業界で有名なScrapyを一度使ってみるか・・と思ったのがきっかけである。
Scrapyとは
Scrapy | A Fast and Powerful Scraping and Web Crawling Framework
スクレイピングに特化しているフレームワークなだけあって、スクレイピングでやりたいことは基本的になんでもできる。(JS必須のサイトははまだ試してないが)
また、フレームワークというだけあって、やり方に沿ってプログラミングしていけば楽々できてしまうものである。実際にやってみる。
インストール
いつも通りのpip install
を行う。
pip install scrapy
プロジェクトの作成
Scrapyではプロジェクトのテンプレートを作ることができる。
scrapy startproject localhost
今回はローカルのサイトをスクレイピングしてみるため、localhost
を名前をつける。中身を確認すると、
$ cd localhost
$ tree
.
├── localhost
│ ├── __init__.py
│ ├── __pycache__
│ ├── items.py
│ ├── middlewares.py
│ ├── pipelines.py
│ ├── settings.py
│ └── spiders
│ ├── __init__.py
│ └── __pycache__
└── scrapy.cfg
データの格納場所を作る
スクレイピングしてきたデータは、Entity
のようなデータの入れ物を設定する必要があるためそこを修正する。
$ vim items.py # -*- coding: utf-8 -*- # Define here the models for your scraped items # # See documentation in: # https://doc.scrapy.org/en/latest/topics/items.html import scrapy class LocalhostItem(scrapy.Item): # この部分だけ追加 user_name = scrapy.Field()
今回は、user_name
というデータを取ってくるためその入れ物を追加しておく。
Spiderでスクレイピングの処理を書く
実際にクローリングやスクレイピングを行うのはこのSpiderのお仕事で、その準備をする。
まずはSpider
を作るためにコマンドを叩く。
scrapy genspider local localhost.com
スクレイピングの名前、スクレイピングするホスト名という感じで書く。
実行すると、spider
ディレクトリいかにlocal.py
ができるので編集する。
$ vim spider/local.py
デフォルトは以下のような感じになる。
# -*- coding: utf-8 -*- import scrapy class LocalSpider(scrapy.Spider): name = 'local' allowed_domains = ['localhost.com'] start_urls = ['http://localhost.com/'] def parse(self, response): pass
start_urls
はクロールしたいパスなどを複数記述することができる。
ただし、HTMLの構造が似ていないと難しいので基本的には同じ系統のデータだが、パス名が違う場合のみ書く。異なる場合はまた別にspider
ファイルを作る。
start_urls = ['http://localhost.com/path1', 'http://localhost.com/path2']
次に実際のデータを取得するためのロジックをparse
に書く。
# 先ほど作った入れ物のインポート from localhost.items import LocalhostItem ...省略 def parse(self, response): # response.cssでデータの中身をcssみたく取ってこれる for sel in response.css('.column > section > .section-accountlist'): article = LocalhostItem() article['user_name'] = sel.css('.username::text').extract_first() yield article
response.css
でcss
を指定するとデータを取得することができる。今回は複数あるため、for
で回し、さらにそこからデータ取得し、格納する。
さらに今回はページング処理もあるので、ページングする際の「次へ」のリンクを探し、再帰を行えるようにする。
def parse(self, response): # response.cssでデータの中身をcssみたく取ってこれる for sel in response.css('.column > section > .section-accountlist'): article = LocalhostItem() article['user_name'] = sel.css('.username::text').extract_first() yield article next_page = response.css('nav .link-next::attr("href")') if next_page: url = response.urljoin(next_page.extract_first()) yield scrapy.Request(url, callback=self.parse)
Spiderの準備はこれで終わり。
実際にスクレイピングしてみる
spider
の名前で起動できる。
$ scrapy crawl local -o result.csv
-o
オプションをつけると、データの中身をアウトプットできる。
これで、後は勝手にスクレイピングし、データ(今回だとuser_name
)を書き込んでくれる。
終わりに
Scrapy
はわりと有名だったので知っていたが、ここまで便利だとは思っておらずメモがてら今回は書いた。
実際にはデータベースも絡んだ実装などにもなってくると思うので、次はその辺かきたいなー。