ScrapyのMailSenderでUnicodeDecodeError

Scrapy のバージョンを上げた影響か、MailSender の send で UnicodeDecodeError が出るようになってしまった。

Traceback (most recent call last):
File "/Users/***/.pyenv/versions/2.7.17/envs/scrapy1.8/lib/python2.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
current.result = callback(current.result, *args, **kw)
File "/Users/***/scm/git/primon/primon/pipelines.py", line 42, in close_spider
self._send_mail(message)
File "/Users/***/scm/git/primon/primon/pipelines.py", line 22, in _send_mail
self.mailer.send(to=["***"], subject="Test", body=message.encode('shift_jis'))
File "/Users/***/.pyenv/versions/2.7.17/envs/scrapy1.8/lib/python2.7/site-packages/scrapy/mail.py", line 101, in send
dfd = self._sendmail(rcpts, msg.as_string().encode(charset or 'utf-8'))
UnicodeDecodeError: 'ascii' codec can't decode byte 0x8d in position 179: ordinal not in range(128)

元々は Scrapy 1.0.5 だったのを 1.8.0 に上げた。同時に Python も 2.7.9 から 2.7.17 に上げている。

Scrapy の History を見ると、1.1.0 で「Sending non-ASCII emails」に関する修正が入っているのでこれが怪しい。

https://docs.scrapy.org/en/latest/news.html#additional-new-features-and-enhancements

しかしこの対応の issue を見る限りでは、文字コード指定してメール送れるようになった感じがするのだが。下記のように修正してもダメ。

// self.mailer.send(to=["***"], subject="Test", body=message.encode('shift_jis')) 
self.mailer.send(to=["***"], subject="Test", body=message, charset='shift_jis') // NG
self.mailer.send(to=["***"], subject="Test", body=message, charset='utf-8') // NG

Python 2.7 を使っていることも影響しているのか??と思い調べてみると、ビンゴ。

https://github.com/scrapy/scrapy/issues/3710

この issue は open のまま。ただ、sys.setdefaultencoding すればいけそう。

/Users/****/.pyenv/versions/2.7.17/envs/scrapy1.8/lib/python2.7/site-packages に sitecustomize.py を下記の内容で作成。

import sys
sys.setdefaultencoding('utf-8')

ついにエラーなくなった!と思ったの束の間。メールが文字化けしていた…。

メールをテキストエディタで開くと文字化けせずにUTF-8で表示された。Content-Transfer-Encoding: base64 になっているが、base64でエンコードされていないためメーラーでは文字化けしている気がする。

ふと思い立ち、MailSender の send で charset=’iso-2022-jp’ を指定すれば base64 でエンコードする必要がなくなるのでいけるのでは?と思ったらビンゴ!ようやく文字化けせずに送れるようになった。

mailer = MailSender.from_settings(spider.settings)
message = u'日本語の内容'
mailer.send(to=["test@example.com"], subject="test", body=message, charset='iso-2022-jp')

という感じ。

ちなみにメールは送れるようになったが、最後に下記のエラーが残っている。

exceptions.AttributeError: 'NoneType' object has no attribute 'bio_read'

これは close_spider の中でメールを送っているためのようで、send の戻り値を close_spider で return すれば解消された。

def close_spider(self, spider):
    mailer = MailSender.from_settings(spider.settings)
    message = u'日本語の内容'
    return mailer.send(to=["test@example.com"], subject="test", body=message, charset='iso-2022-jp')

https://github.com/scrapy/scrapy/issues/3478#issuecomment-498058336

ただこの対応も完璧じゃないようで、時々エラーが出る。タイミングの問題なのだろうか。

コメントを残す