添付ファイルの扱い
回答済み【実現したいこと】
添付ファイル付きメールを受信し、その添付ファイル(.csv、.xlsx、.xls、.txtなど)の中身を解析し、
・得られた情報(日付、文字列など)からジョブフローを実行
・.txtや.csvに書かれたURLからスクレイピングや死活監視
といった動作を考えております、そこで
【質問】
・メールチャネルにてメールを受信した際、添付ファイルはどうなるのか
(どこかのディレクトリに保存されたりするのか、それともメール同様サーバーから消えるのか)
・openpyxlやbeautifulsoup4といったPythonのライブラリや、open()といったPythonの組み込み関数を使う以外にExcelファイルを扱う、スクレイピングをする、テキストファイルを扱う方法はないか
よろしくお願いします。
-
正式なコメント
フィックスポイント開発部の髙橋です。
メールチャネルでメールを受信した段階では、メールはヘッダや本文だけでなく添付ファイルなども含めて、メール送信元あるいは中継サーバにてエンコードされたメッセージ形式のまま記録されています。これは通常テキストですので、メールチャネルから読み取ったものはジョブフローでは単一の文字列となっています。もちろんこのままでは扱いにくいので、組み込み関数 mail_parse() などを用いてパースすることで件名など特定のヘッダや本文などとして扱うことができるようになります。
> ・メールチャネルにてメールを受信した際、添付ファイルはどうなるのか
> (どこかのディレクトリに保存されたりするのか、それともメール同様サーバーから消えるのか)上記のとおり添付ファイルを含めた形式のメッセージとしてチャネルに保持されるため、ファイルとしては存在しません。メールサーバ上でも通常メッセージ単位に処理されるため、添付ファイルといった単位で扱われることはないと思います。
メールチャネルでメールを受信して、内容に応じた処理を実施する、といった例としては以下のコラムが参考になるかと思います。
https://www.kompira.jp/column/execute_jobflow_by_mail/
さて、添付ファイルのあるメールですが、これはマルチパート形式になります。mail_parse() の結果、マルチパートであるかどうかは、パース結果の `Is-Multipart` キーの要素が真であるかで確認できます。また、マルチパートであるときは `Body` キーの要素が、各パートのパース結果として辞書の配列となります。
メールチャネルから受信したメールを mail_parse() でパースして、添付ファイルがあった場合にそれを kompira サーバの指定してディレクトリ(/tmp)に保存する、というジョブフローの例を以下に示します。
|dest = '/tmp'|
<./mchan> ->
print("メールを受信しました") ->
[raw_mail = $RESULT] ->
print("メールを解析します") ->
[parsed = mail_parse(raw_mail)] ->
[from = parsed['From']] ->
{ if parsed['Is-Multipart'] |
then:
print("$from からマルチパートメールを受信しました") ->
{ for part in parsed['Body'] |
{ if part['Filename'] |
then:
print("---- 添付パート: ファイル名=%Filename ----" % part) ->
["cat > $dest/%Filename" % part << part['Body']]
else:
print("---- 本文パート ----") ->
print(part['Body'])
}
}
else:
print("$from からシングルパートメールを受信しました") ->
print(parsed)
}各パートがどういった形式であるかは、Content-Type ヘッダを見るほうがよいですが、ここでは簡単のために Filename が取得できるかどうかで、添付ファイルのパートか本文のパートかを判断しています。
["cat > $dest/%Filename" % part << part['Body']]
添付ファイルである場合、ファイル本体はそのパートの `Body` キーの要素に含まれているので、これを cat コマンドの標準入力に渡し、リダイレクトで /tmp 配下に添付ファイル名で保存しています。
なお、複雑な構造のメールを受信した場合、上記では処理が不十分なことにご注意ください。たとえば、マルチパートは入れ子になりうるので、マルチパートの中に、さらにマルチパートが入っている場合があり、上記の単純なループでは処理できません。
さて、ここまででメールの添付ファイルにはアクセスできましたが、その種別や形式に応じた解析処理については、ジョブフローだけでは対応が難しく、個別に処理する python ライブラリを用意していただくことが多いです。
>・openpyxlやbeautifulsoup4といったPythonのライブラリや、open()といったPythonの組み込み関数を
> 使う以外にExcelファイルを扱う、スクレイピングをする、テキストファイルを扱う方法はないかテキスト形式の添付ファイルであれば、ジョブフローでも基本的な文字列操作メソッドや正規表現マッチは利用できるので、簡単な解析なら可能であるとは思いますが、Excel ファイルの操作などはやり openpyxl などのライブラリを利用いただくことになります。
URLに対するアクセスであれば urlopen() 組み込みジョブで可能ですが、それで得られた内容を解析する処理などについては、これも同様に beautifulsoup4 などのライブラリを組み合わせて実現することが多いです。
参考になさってください。
コメントアクション -
回答ありがとうございました。
testJobflow4というジョブフローに上記コードを貼り付け実行してみた所、送信元メーラーによっては失敗することがあります。
失敗するときは
$ERROR = 'aborted at line 24 in /root/*******/********/testJobflow4: Undefined field "Filename"'
というエラーコードが出るのですが、これはbody要素の中身が複雑になっているため、単純なループでは取れないということでしょうか?
また、成功した場合も---- 添付パート: ファイル名=hoge.xlsx ---- [localhost] local: (cat > /tmp/hoge.xlsx) < /tmp/ke-9qumOF.dat
となるのですが、これは/tmp以下にファイルの実態は作られないのでしょうか?重ね重ねの質問になって申し訳ないのですが、よろしくお願いします
-
ご確認ありがとうございます。
> 失敗するときは
> $ERROR = 'aborted at line 24 in /root/*******/********/testJobflow4: Undefined field "Filename"'
> というエラーコードが出るのですが、これはbody要素の中身が複雑になっているため、
> 単純なループでは取れないということでしょうか?そうですね、詳しくはメールのソースを見てみないと分かりませんが、メールの構造が複雑なために適切に解析できていない可能性があります。上記サンプルではパートが1階層で単純に並んだマルチパートメールは扱えますが、2階層以上の入れ子になったマルチパートメールは解析できないはずです。
> [localhost] local: (cat > /tmp/hoge.xlsx) < /tmp/ke-9qumOF.dat
> となるのですが、これは/tmp以下にファイルの実態は作られないのでしょうか?このときは、/tmp に hoge.xlsx というファイルの実体が保存されていると思いますが、いかがでしょうか?
サインインしてコメントを残してください。
コメント
4件のコメント