マルチパート構造のメールヘッダにおける情報抽出について
下記コミュニティのトピックの回答を参考に、Mailヘッダ内から特定の要素を抽出するスクリプトを作成しています。
URL:
https://kompira.zendesk.com/hc/ja/community/posts/30220124126361-%E3%83%A1%E3%83%BC%E3%83%AB%E3%81%AB%E6%B7%BB%E4%BB%98%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB%E6%8A%BD%E5%87%BA%E6%96%B9%E6%B3%95%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6
mail_body①の要素を順番にpartに加え、FilenameパートとContent-Typeパートで判定・抽出させる動作を想定しています。
メールを送付し、本スクリプトを実行時、下記エラーが確認されました。
フィールドエラー: 不正なオブジェクト "Content-Type": string indices must be integers'
おそらくmail_bodyの一要素目である「Content-Type」がpartに格納された際に生じたエラーとみられます。
Pythonのナレッジにはなりますが、エラー内容より、Content-Typeを辞書型ではなく文字列で読み込んでしまっていることが原因と考えました。
【Content-Type内容】
'Content-Type': 'multipart/mixed;\r\n\tboundary="----=_NextPart_001_01E7_01DA95AB.EA0A9800"'
本件のエラー原因、また文字列から配列、辞書型へ変換(読み込みの変更)するライブラリはございますでしょうか。
【補足】
なお、JSONの階層を下げ、mail_body②の形式にした結果、辞書型と認識され、実行完了となりました。
ただ、本階層には最終的に抽出したいFilenameが含まれていません。
エラー環境では、Bodyなどネストされたフィールドが残っており、この点も実行失敗の要因になるのでしょうか。
-
正式なコメント
メールデータのご提供ありがとうございます。
ご希望の操作を少し一般化して、以下のようなライブラリオブジェクトを作成してみました。
import re
def filter_parts(parsed_mail, type_pattern=None, is_attachment=None):
"""
mail_parse() の結果から指定した形式のパートを抽出する
Args:
parsed_mail(dict[str]): mail_parse() 結果の辞書データ
type_pattern(str): 抽出するパートを Content-Type で指定する
'^text/html' などと正規表現で指定する
is_attachment(bool): 添付ファイルパートを抽出するか指定する
True: 添付ファイルパートだけを抽出する
False: 添付ファイルパート以外を抽出する
上記以外: 添付ファイルパートであるかは検知しない
Returns:
list[str]: 条件にマッチしたパートのリスト
"""
if type_pattern:
type_pattern = re.compile(type_pattern)
parts = []
def _filter(part):
if part.get('Is-Multipart', False):
for subpart in part['Body']:
_filter(subpart)
return
if type_pattern:
content_type = part.get('Content-Type', '')
if not type_pattern.match(content_type):
return
if isinstance(is_attachment, bool):
has_filename = bool(part.get('Filename', ''))
if is_attachment ^ has_filename:
return
parts.append(part)
_filter(parsed_mail)
return partsこのライブラリオブジェクトを例えば「mail_lib」という名前で作成しておき、同じディレクトリにあるジョブフローから以下のように呼び出してみてください。ジョブフローのパラメータ raw_mail_txt にはヘッダを含むメール全体テキストを渡してください。
|raw_mail_txt|
[filter_parts = ./mail_lib.filter_parts] ->
[parsed = mail_parse(raw_mail_txt)] ->
print("----- HTML パートの抽出 -----") ->
[html_parts = filter_parts(parsed, '^text/html')] ->
print(html_parts) ->
print("----- TEXT 形式の添付ファイルの抽出 -----") ->
[attach_parts = filter_parts(parsed, '^text', is_attachment=true)] ->
print(attach_parts)filter_parts() 関数が抽出したデータは mail_parse() で得られる各パートと同じ構造です。このサンプルを実行すると、HTML パートと、TEXT 形式の添付ファイルを抽出できているかと思います。
参考になさってみてください。
コメントアクション -
フィックスポイントの高橋です。
メールを送付し、本スクリプトを実行時、下記エラーが確認されました。
フィールドエラー: 不正なオブジェクト "Content-Type": string indices must be integers'おそらくmail_bodyの一要素目である「Content-Type」がpartに格納された際に生じたエラーとみられます。
リンク先にサンプルとして提示したジョブフローでは、簡単にするためメールのマルチパートが入れ子構造になっている場合などには対応していません。
送付されたメールのヘッダを含んだ全文を張り付けていただき、判定したい(または抽出したい)条件を示していただければ、また別のサンプルをご提示できるかもしれません。
(なお、本処理は特に JSON とは関係がありませんので、タイトルと補足部分については意図を読み取りかねますが、マルチパートの構造を JSON と見立てておられるのかと推測しました)
以上、参考になさってください。
-
リンク先にサンプルとして提示したジョブフローでは、簡単にするためメールのマルチパートが入れ子構造になっている場合などには対応していません。
送付されたメールのヘッダを含んだ全文を張り付けていただき、判定したい(または抽出したい)条件を示していただければ、また別のサンプルをご提示できるかもしれません。
ご回答ありがとうございます。
ヘッダ情報の添付が漏れていました。こちらがヘッダとなります。
Body{
'Content-Type': 'multipart/mixed;\r\n\tboundary="----=_NextPart_001_006C_01DA9002.31E9C830"',
'Is-Multipart': True,
'Body':
[
{
'Content-Type': 'multipart/alternative;\r\n\tboundary="----=_NextPart_002_006D_01DA9002.31E9C830"',
'Is-Multipart': True,
'Body':
[
{
'Content-Type': 'text/plain;\r\n\tcharset="iso-2022-jp"',
'Content-Transfer-Encoding': '7bit',
'Is-Multipart': False,
'Body': '添付ファイルテスト\r\n\r\n',
'Filename': None
},
{
'Content-Type': 'text/html;\r\n\tcharset="iso-2022-jp"',
'Content-Transfer-Encoding': 'quoted-printable',
'Is-Multipart': False,
'Body': '【HTML情報が含まれる】',
'Filename': None
}
]
},
{
'Content-Type': 'text/plain; name="添付ファイルのタイトルです。.txt"',
'Content-Transfer-Encoding': '7bit',
'Content-Disposition': 'attachment; filename="添付ファイルのタイトルです。.txt"',
'Is-Multipart': False,
'Body': '',
'Filename': '添付ファイルのタイトルです。.txt'
}
]
}(なお、本処理は特に JSON とは関係がありませんので、タイトルと補足部分については意図を読み取りかねますが、マルチパートの構造を JSON と見立てておられるのかと推測しました)
こちら、読み替えいただいた意図で問題ございません。ありがとうございます。
-
データのご提示ありがとうございます。やはりマルチパートが入れ子構造になっているようですので、先に提示させていただいたサンプルジョブフローでは適切に扱うことはできません。
お知りになりたいこととしては、このような構造を持つメールから、添付ファイルの情報(名前と内容)を取得したい、ということでよろしかったでしょうか?
ただ、本階層には最終的に抽出したいFilenameが含まれていません。
また、入れ子になったパートに含まれる添付ファイルについて、たしかに Filename が含まれていないように見受けられます。こちらを調査するには、mail_parse() 前の元データが必要になりますが、ご提供いただくことは可能でしょうか。
-
お知りになりたいこととしては、このような構造を持つメールから、添付ファイルの情報(名前と内容)を取得したい、ということでよろしかったでしょうか?
はい、ご認識の通りです。
また、一旦は添付ファイル名称を抽出できれば問題ございません。
ただ、本階層には最終的に抽出したいFilenameが含まれていません。
また、入れ子になったパートに含まれる添付ファイルについて、たしかに Filename が含まれていないように見受けられます。こちらを調査するには、mail_parse() 前の元データが必要になりますが、ご提供いただくことは可能でしょうか。
Filenameについては、昨日展開しましたヘッダ内Bodyの最後にある以下情報です。
こちらのtxtファイル名称を抽出できれば問題ございません。
'Filename': '添付ファイルのタイトルです。.txt'
また、メールヘッダの元データが必要な場合、ユーザ情報が含まれますのでお手数ですがメールでお送りさせていただいてもよろしいでしょうか。
-
また、メールヘッダの元データが必要な場合、ユーザ情報が含まれますのでお手数ですがメールでお送りさせていただいてもよろしいでしょうか。
では、お手数ですが、 takahashi+zendesk@fixpoint.co.jp までお送りいただけますでしょうか。
サインインしてコメントを残してください。
コメント
8件のコメント