前回・前々回に引き続き、Scrapyを使ってこのブログのクローリングを行います。
今回は細々としたところで、Spiderクラスのparseメソッドへ値を受け渡す方法と、エラーハンドリングについてです。Spiderの実装は前々回の投稿も参考にしてみてください。
なお、以下のフォルダ構造となってます。
parseへ任意の値を渡す
parseでスクレイピングして得た値を別のページのparse処理でも使い回したい、など、parseメソッドに任意の値を渡して利用したいケースがあります。
そういった場合、metaプロパティを使います。
- リクエストを作るときに
request.meta['key'] = value
で値を設定 - parseメソッド内では
value = response.meta['key']
で値を取得
2018年のエントリタイトルと投稿日を、1月から順にスクレイピングしていく例を示します。archive_spider.pyに実装しています。
http://ohke.hateblo.jp/archive/2018/{月}
へ1月から順にアクセスしていきます- start_requestsでは、
request.meta['month']
に初期値1を渡してリクエストしています - parseでは、
response.meta['month']
で今見ている月を取り出し、インクリメントして次のリクエストを作っています
import scrapy from ..items import PostItem class ArchiveSpider(scrapy.Spider): name = 'archive_spider' url_format = 'https://ohke.hateblo.jp/archive/2018/{}' filename = '2018.txt' def start_requests(self): start_month = 1 url = self.url_format.format(start_month) request = scrapy.Request(url=url, callback=self.parse) request.meta['month'] = start_month # 値の設定 yield request def parse(self, response): month = response.meta['month'] # 値の取得 title_list = response.css('a.entry-title-link::text').extract() date_list = response.xpath('//time/@datetime').extract() with open(self.filename, 'a') as f: for t, d in zip(title_list, date_list): f.write('{}\t{}\n'.format(d, t)) next_month = month + 1 next_url = self.url_format.format(month+1) # オプションmetaに辞書形式でも渡せます yield scrapy.Request(url=next_url, callback=self.parse, meta={'month': month})
エラーハンドリング
スクレイピングをしていると、リンクが切れていたり、権限のためにアクセスできない、などの問題は避けられません。
Scrapyでは、リトライなどの基本的な設定をsettings.pyでできます。
RETRY_TIMES
: リトライ回数 (デフォルトでは、2)RETRY_HTTP_CODES
: リトライするHTTPステータスコード (デフォルトでは、[500, 502, 503, 504, 408])HTTPERROR_ALLOWED_CODES
: エラーとして処理するHTTPステータスコード (デフォルトは [] で全てエラー扱い)
Spiderごとに設定することもでき、例えば400エラーをparse内でハンドリングして正常終了する場合は、以下のような実装になります。
monthが13の場合、存在しない月なので400が返ってきます。それをエラーとせず、parse内でreturnして終了させています。
class ArchiveSpider(scrapy.Spider): name = 'archive_spider' handle_httpstatus_list = [400] # ステータス400の場合はエラーにしない # 省略 def parse(self, response): # ステータスが400の場合は、クローリングを止める if response.status in [400]: print(response.status) return month = response.meta['month'] title_list = response.css('a.entry-title-link::text').extract() date_list = response.xpath('//time/@datetime').extract() with open(self.filename, 'a') as f: for t, d in zip(title_list, date_list): f.write('{}\t{}\n'.format(d, t)) next_month = month + 1 next_url = self.url_format.format(month+1) yield scrapy.Request(url=next_url, callback=self.parse, meta={'month': month})
まとめ
今回もScrapyの使い方について、parseへのパラメータの渡し方と、エラーハンドリニングについて触れました。
参考文献
Pythonクローリング&スクレイピング -データ収集・解析のための実践開発ガイド-
- 作者: 加藤耕太
- 出版社/メーカー: 技術評論社
- 発売日: 2016/12/16
- メディア: 大型本
- この商品を含むブログ (3件) を見る