【Python】Scrapyでダウンロードエラーを取得する方法

 こんにちわ、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 3

オライリージャパン

この記事のまとめ

  • PythonのScrapyでダウンロードエラーを取得する方法の備忘録

お仕事のご相談・ご依頼
お気軽にお問い合わせください!

お仕事の依頼はこちら

著者プロフィール

Taka

東京、奄美大島を拠点にサーフィンとスノーボードが好きなフリーランスのWebクリエイターです。普段はプログラム書いたりデザインしたり映像作ったりしています。いろいろな人の話しを聞くのが好きなので、このブログを通して多くの人と繋がりが出来たら嬉しいです。noteとInstagramもやっているのでフォローしてくれたらありがたいです!

人気記事

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です