2016-05-25追記:2016年5月にリリースされたScrapy 1.1はPython 3に対応したので、Python 3を使えばこのページの問題はそもそも発生しません。何もしなくてもItem内の日本語が読めます。ぜひPython 3とScrapy 1.1以降を使いましょう!
問題
Scrapyでは、SpiderからyieldしたItemがログに出力されますが、日本語など非ASCIIの文字列を含むフィールドはUnicodeエスケープされた状態で出力されるので、パット見で読めず、もどかしい思いをします。
(雑な) 対処法
Itemクラスに以下のような__unicode__()
メソッドを定義すると読みやすくなります。
class MyItem(scrapy.Item):
# ...
def __unicode__(self):
return repr(self).decode('unicode_escape')
実行例
以下のSpiderを実行します。これは12factor.netというWebサイトで翻訳のある言語の一覧を取得するSpiderです。
# coding: utf-8
import scrapy
class MyItem(scrapy.Item):
languages = scrapy.Field()
def __unicode__(self):
return repr(self).decode('unicode_escape')
class TwelveFactorSpider(scrapy.Spider):
name = '12factor'
start_urls = ['http://12factor.net/']
def parse(self, response):
yield MyItem(languages=response.css('#locales a::text, #locales span::text').extract())
Before (__unicode__
なし)
Unicode文字列がエスケープされています。
$ scrapy runspider twelve_factor_spider.py
2016-01-13 17:56:46 [scrapy] INFO: Scrapy 1.0.4 started (bot: scrapybot)
2016-01-13 17:56:46 [scrapy] INFO: Optional features available: ssl, http11
2016-01-13 17:56:46 [scrapy] INFO: Overridden settings: {}
...
2016-01-13 17:56:47 [scrapy] DEBUG: Scraped from <200 http://12factor.net/>
{'languages': [u'Espa\xf1ol (es)',
u'Fran\xe7ais (fr)',
u'Deutsch (de)',
u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 (ru)',
u'English (en)',
u'\u65e5\u672c\u8a9e (ja)',
u'Italiano (it)',
u'Brazilian Portuguese (pt_br)',
u'\u7b80\u4f53\u4e2d\u6587 (zh_cn)',
u'\ud55c\uad6d\uc5b4 (ko)']}
...
After (__unicode__
あり)
Unicode文字列がエスケープされておらず、読みやすいです。
$ scrapy runspider twelve_factor_spider.py
2016-01-13 17:57:25 [scrapy] INFO: Scrapy 1.0.4 started (bot: scrapybot)
2016-01-13 17:57:25 [scrapy] INFO: Optional features available: ssl, http11
2016-01-13 17:57:25 [scrapy] INFO: Overridden settings: {}
...
2016-01-13 17:57:25 [scrapy] DEBUG: Scraped from <200 http://12factor.net/>
{'languages': [u'Español (es)',
u'Français (fr)',
u'Deutsch (de)',
u'Русский (ru)',
u'English (en)',
u'日本語 (ja)',
u'Italiano (it)',
u'Brazilian Portuguese (pt_br)',
u'简体中文 (zh_cn)',
u'한국어 (ko)']}
...
ちなみに、あくまでログへの出力内容を変えるだけで、-o/--output
でファイルに保存した場合の内容などには影響しません。
注意点
decode('unicode_escape')
を通すと、空白文字 (\n
, \r
, \t
, \u3000
など) や制御文字のUnicodeエスケープもアンエスケープされてしまいます。このため、空白文字やバイト列が含まれるItemでは逆に中身がわかりづらくなる可能性が大いにあります。使い方には気をつけてください。
ScrapyがPython 3に対応したらこんな面倒なことしなくて良くなるはずです。
おまけ:もう少し真面目に表示可能な文字のみをアンエスケープする関数を書いてみました。 → http://ideone.com/J77aaN
まとめ
- Itemの
__unicode__()
を定義するとログへの出力内容を変更できる。 - Python 3が便利。