こんにちわ、Takaです。最近仕事でPythonの『Scrapy』を多く使用するのですが、今回はそのScrapyでクローリングする際にダウンロードエラーを取得してハンドリングする方法を備忘録も兼ねて紹介したいと思います。
Scrapyとは、Pythonのクローリング・スクレイピングのフルスタックフレームワークになります。とても強力で便利なフレームワークになり、冗長的な処理をフレームワーク側がやってくれるので、クローリング・スクレイピング処理に集中することができます。
なお、今回のコードはScrapyのバージョン1.4.0をベースにしたものです。
さて、まずはScrapyのSpiderクラスのサンプルコードをみていきましょう。
import scrapy
class MySpider(scrapy.Spider):
name = 'example'
allowed_domains = ['example.com']
start_urls = [
'http://www.example.com/1.html',
'http://www.example.com/2.html',
'http://www.example.com/3.html',
]
def parse(self, response):
for h3 in response.xpath('//h3').extract():
yield {"title": h3}
上のコードは、Spiderクラスの基本となるようなコードで、これが実際に何をしているのかを簡単に言うと、「example」という名前のSpiderが、「start_urls」に記載されてあるurlをクローリングして、それぞれのページにある「h3」タグのタイトルを取得しています。
基本的にはこのコードで問題なくクローリングもスクレイピングもできます。すごく簡単ですね。
しかし、上のコードではダウンロードエラーが発生した際の処理が書かれていません。Web上に存在するページは突然なくなってしまうことも多々あるので、クローリングをする際はしっかりとダウンロード状況をチェックしてダウンロードエラーが発生した場合に、管理者に通知するなどの処理が必要になってきます。
そこで、上のコードに追記してエラーハンドリングをしていきます。
追記したコードがこちらになります。
import scrapy
from scrapy.spidermiddlewares.httperror import HttpError
from twisted.internet.error import DNSLookupError
from twisted.internet.error import TimeoutError, TCPTimedOutError
class MySpider(scrapy.Spider):
name = 'example'
allowed_domains = ['example.com']
start_urls = [
'http://www.example.com/1.html',
'http://www.example.com/2.html',
'http://www.example.com/3.html',
]
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(
url,
callback=self.parse,
errback=self.errback_handle,
dont_filter=True
)
def parse(self, response):
for h3 in response.xpath('//h3').extract():
yield {"title": h3}
def errback_handle(self, failure):
self.logger.error("### ERRBACK ###")
if failure.check(HttpError):
response = failure.value.response
self.logger.error('ErrorType : %s',failure.type)
self.logger.error('HttpError on %s', response.url)
self.logger.error('HttpError on %s', response.status)
elif failure.check(DNSLookupError):
request = failure.request
self.logger.error('ErrorType : %s',failure.type)
self.logger.error('DNSLookupError on %s', request.url)
elif failure.check(TimeoutError, TCPTimedOutError):
request = failure.request
self.logger.error('ErrorType : %s',failure.type)
self.logger.error('TimeoutError on %s', request.url)
else:
request = failure.request
self.logger.error('ErrorType : %s',failure.type)
self.logger.error(request.url)
最初のコードでは「start_requests」というメソッドが省略されており、「start_urls」に記載されたurlをダウンロードすると自動でparseメソッドが呼び出されていますが、新たに「start_requests」というメソッドは追加するこで、ダウンロード処理を細かく設定しています。
実際に何をやっているかと言うと、「start_requests」メソッド内の「yield scrapy.Request」の引数にて、「callback」と「errback」と「dont_filter」を設定しています。そしてこの中の「errback」が例外エラーが発生した際に呼び出されるメソッドになり、そこでエラーハンドリングをしています。ここでは「errback_handle」というメソッドを呼び出すようになっています。
「errback_handle」メソッドでは「HttpError」、「DNSLookupError」、「TimeoutError」の3つと、それ以外のエラーをチェックしてそれぞれのエラーハンドリングをしています。ここでは単純にエラーログを吐き出していますが、DBにエラー内容を保存したり管理者にメールで通知するなど適切な処理をそれぞれの環境に合わせて実装してみてください。
なお、上記のエラーハンドリングには下記のライブラリが必要になってきますのでimportすることも忘れないようにしてください。
from scrapy.spidermiddlewares.httperror import HttpError
from twisted.internet.error import DNSLookupError
from twisted.internet.error import TimeoutError, TCPTimedOutError
それでは、今回はここまで。また!
この記事のまとめ
- PythonのScrapyでダウンロードエラーを取得する方法の備忘録