通常、scrapy crawl コマンドで Spider を実行したとき、内部で例外が発生してもコマンド自体の終了コードは0(正常終了)になってしまう。このため、ログを見ないと失敗していることに気づけない。
scrapyの設定自体にはそういう機能はなさそうだったのでアレコレした。
サンプルコード
たとえばこういう “example” Spider があり、
1
2
3
4
5
6
7
8
9
10
11
12
|
import scrapy
class ExampleSpider(scrapy.Spider):
name = "example"
start_urls = [
"http://localhost:8080",
]
def parse(self, response):
print(response.url)
raise Exception("hogehoge")
|
これを
で実行した場合、ログを見れば例外が発生していることがわかるが、終了コードは 0 になっている。
エラー時に終了コードを設定する方法
以下の方針でエラー時に終了コードを設定できる。
- 直接 scrapy crawl で起動するのではなく、普通の Python スクリプトから Spider を呼び出す
- Spider 内で問題が発生したとき、CloseSpider 例外を投げて Spider を終了する。ここで終了理由を設定する
- scrapy.signalmanager.dispatcher を使って Spider の終了イベントをハンドルする。終了理由を確認し、異常終了していれば終了コードを設定する
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
import logging
import sys
import scrapy
from scrapy.exceptions import CloseSpider
from scrapy.utils.project import get_project_settings
from scrapy.spiderloader import SpiderLoader
from scrapy.crawler import CrawlerProcess
from scrapy.signalmanager import dispatcher
logging.basicConfig()
logger = logging.getLogger()
settings = get_project_settings()
loader = SpiderLoader(settings)
class ExampleSpider(scrapy.Spider):
name = "example"
start_urls = [
"http://localhost:8080",
]
def parse(self, response):
print(response.url)
# エラー時に CloseSpider を投げる(終了するけど…)
raise CloseSpider("hogehoge")
# サンプルコードなので横着して一緒に書いてるけど、ここから下は main.py とかを作ってそこに書くのがいいと思う。
def run_spider(spider_name, args):
spider = loader.load(spider_name)
exit_code = None
def _spider_closed(spider, reason):
nonlocal exit_code
# 正常終了の場合、reason は finished。
# CloseSpider で終了した場合、reason は CloseSpider のコンストラクタに渡した値。
if reason != "finished":
exit_code = 1
else:
exit_code = 0
dispatcher.connect(_spider_closed, signal=scrapy.signals.spider_closed)
# CrawlerProcessの第2引数(install_root_handler)を False にしておかないと logging の設定が引き継がれない
process = CrawlerProcess(settings, False)
process.crawl(spider, **args)
process.start()
return exit_code
if __name__ == "__main__":
exit_code = run_spider("example", {})
sys.exit(exit_code)
|
こんな感じにして、スクリプトを直接実行
1
|
python app/spiders/ExampleSpider.py
|
すると、ちゃんと終了コードが 1 になる。