19.1.2. email.parser: 電子メールメッセージのパース

ソースコード: Lib/email/parser.py


メッセージオブジェクト構造体の作成には 2 つの方法があります。 一つ目は Message オブジェクトをインスタンス化し、 attach()set_payload() を呼び出しつなぎあわせることで、一から作る方法です。 二つ目は電子メールメッセージのフラットテクスト表現のパースです。

email パッケージが提供する標準的なパーザ (解析器) は、MIME 文書を含むほとんどの電子メールの文書構造を解釈できます。 パーザに文字列やファイルオブジェクトを渡せば、パーザはそのオブジェクト構造のルート Message インスタンスを返します。 簡単な非 MIME メッセージの場合、このルートオブジェクトのペイロードはメッセージのテキストを含む文字列になるでしょう。 MIME メッセージであれば、ルートオブジェクトの is_multipart() メソッドは True を返します。 ルート下の要素には get_payload()walk() メソッドによってアクセスすることができます。

実際には 2つのパーザインターフェイスが使用可能です。 古典的な Parser API と増分的な FeedParser API です。 メッセージのテキスト全体を文字列としてメモリ上に保持している場合や、メッセージ全体がファイルシステム上のファイルにある場合は 古典的な Parser API が良いです。 入力待ちによるブロックがありえるようなストリームからメッセージを読んでいる場合 (例えば電子メールメッセージをソケットから読み込んでいる場合) は、FeedParser の方が適切です。 FeedParser はメッセージを増分的に受け取ってパースします。 パーザを閉じたときはルートオブジェクトのみを返します [1]

パーザを拡張できる方法は制限されていることに注意してください。 もちろん、自分でパーザを一から実装することはできます。 email パッケージのバンドルされたパーザと Message クラスの間には不思議な関係はありませんから、カスタムパーザはそれが必要とするやりかたでメッセージオブジェクトツリーを作成することができます。

19.1.2.1. FeedParser API

email.feedparser モジュールからインポートされる FeedParser は email メッセージをインクリメンタルに解析するのに向いた API を提供します。これは email メッセージのテキストを (ソケットなどの) 読み込みがブロックされる可能性のある情報源から入力するときに必要となります。もちろん FeedParser は文字列またはファイルにすべて格納されている email メッセージを解析するのにも使うことができますが、このような場合には旧式の Parser API のほうが便利かもしれません。これら 2つのパーザ API の意味論と得られる結果は同一です。

FeedParser API は簡単です。まずインスタンスをつくり、それにテキストを (それ以上テキストが必要なくなるまで) 流しこみます。その後パーザを close すると根っこ (root) のメッセージオブジェクトが返されます。標準に従ったメッセージを解析する場合、 FeedParser は非常に正確であり、標準に従っていないメッセージでもちゃんと動きます。そのさい、これはメッセージがどのように壊れていると認識されたかについての情報を残します。これはメッセージオブジェクトの defects 属性にリストとして現れ、メッセージ中に発見された問題が記録されます。パーザが検出できる障害 (defect) については email.errors モジュールを参照してください。

以下は FeedParser の API です:

class email.parser.FeedParser(_factory=email.message.Message, *, policy=policy.compat32)

FeedParser インスタンスを作成します。オプション引数 _factory には引数なしの callable を指定し、これはつねに新しいメッセージオブジェクトの作成が必要になったときに呼び出されます。デフォルトでは、これは email.message.Message クラスになっています。

policy が指定された場合 (それは policy クラスのインスタンスでなければなりません) メッセージの表現を更新するために、ポリシーが指定するルールを用います。もし policy が設定されていなければ、 compat32 ポリシーが使われます。このポリシーは、 Python 3.2 バージョンの email パッケージとの後方互換性を維持します。詳細については policy ドキュメンテーションを見てください。

バージョン 3.3 で変更: キーワード引数 policy が追加されました。

feed(data)

FeedParser にデータを供給します。 data は 1行または複数行からなる文字列を渡します。渡される行は完結していなくてもよく、その場合 FeedParser は部分的な行を適切につなぎ合わせます。文字列中の各行は標準的な 3種類の行末文字 (復帰 CR、改行 LF、または CR+LF) どれかの組み合わせでよく、これらが混在してもかまいません。

close()

FeedParser を close し、それまでに渡されたすべてのデータの解析を完了させ根っこ (root) のメッセージオブジェクトを返します。 FeedParser を close したあとにさらにデータを feed した場合の挙動は未定義です。

class email.parser.BytesFeedParser(_factory=email.message.Message)

feed() メソッドへの入力が文字列ではなくバイト列でなければならないことを除いて FeedParser と同等に動作します。

バージョン 3.2 で追加.

19.1.2.2. Parser クラス API

email.parser から import される Parser クラスはメッセージの内容が文字列やファイルで完全な状態のときに、メッセージのパースに利用できる API を提供しています。 email.parser モジュールはヘッダのみのパーサも提供しています。それは HeaderParserBytesHeaderParser と呼ばれるパーサで、メッセージのヘッダにのみ興味があるときに利用できます。 HeaderParserBytesHeaderParser はそのような状況でより速く動作します、なぜならそれらはメッセージボディのパースを試みる代わりにボディそのものを文字列としてペイロードに格納するからです。これらは ParserBytesParser クラスと同じ API を持っています。

バージョン 3.3 で追加: BytesHeaderParser クラス。

class email.parser.Parser(_class=email.message.Message, *, policy=policy.compat32)

Parser クラスのコンストラクタです。オプション引数 _class をとることができます。これは呼び出し可能なオブジェクト (関数やクラス) でなければならず、メッセージ内コンポーネント (sub-message object) が作成されるときは常にそのファクトリクラスとして使用されます。デフォルトではこれは Message になっています (email.message 参照)。このファクトリクラスは引数なしで呼び出されます。

policy が指定された場合 (それは policy クラスのインスタンスでなければなりません) メッセージの表現を更新するために、ポリシーが指定するルールを用います。もし policy が設定されていなければ、 compat32 ポリシーが使われます。このポリシーは、 Python 3.2 バージョンの email パッケージとの後方互換性を維持します。詳細については policy ドキュメンテーションを見てください。

バージョン 3.3 で変更: 2.4 で非推奨になった strict 引数の削除。キーワード引数 policy の追加。

それ以外の Parser メソッドは以下のとおりです:

parse(fp, headersonly=False)

ファイルなどストリーム形式 (file-like) のオブジェクト fp からすべてのデータを読み込み、得られたテキストを解析して基底 (root) メッセージオブジェクト構造を返します。 fp はストリーム形式のオブジェクトで readline() および read() 両方のメソッドをサポートしている必要があります。

fp に格納されているテキスト文字列は、一連の RFC 2822 形式のヘッダおよびヘッダ継続行 (header continuation lines) によって構成されている必要があります。オプションとして、最初にエンペローブヘッダが来ることもできます。ヘッダ部分はデータの終端か、ひとつの空行によって終了したとみなされます。ヘッダ部分に続くデータはメッセージ本体となります (MIME エンコードされた subpart を含んでいるかもしれません)。

オプション引数 headersonly はヘッダを読み終えた後にパースを止めるかを指定するフラグです。デフォルトは False で、ファイルの内容全体をパースします。

parsestr(text, headersonly=False)

parse() メソッドに似ていますが、ファイルなどのストリーム形式のかわりに文字列を引数としてとるところが違います。文字列に対してこのメソッドを呼ぶことは、 textStringIO インスタンスとして作成して parse() を適用するのと同じです。

オプション引数 headersonlyparse() メソッドものものと同様です。

class email.parser.BytesParser(_class=email.message.Message, *, policy=policy.compat32)

このクラスは Parser と入力としてバイト列を扱うことを除いて、全く同じです。_classstrict 引数は Parser のコンストラクタと同様に解釈されます。

policy が指定された場合 (それは policy クラスのインスタンスでなければなりません) メッセージの表現を更新するために、ポリシーが指定するルールを用います。もし policy が設定されていなければ、 compat32 ポリシーが使われます。このポリシーは、 Python 3.2 バージョンの email パッケージとの後方互換性を維持します。詳細については policy ドキュメンテーションを見てください。

バージョン 3.3 で変更: strict 引数の削除。キーワード引数 policy の追加。

parse(fp, headersonly=False)

バイナリ file-like オブジェクト fp からすべてのデータを読み込み、得られたバイト列を解析してメッセージオブジェクト構造を返します。 fp はストリーム形式のオブジェクトで readline() および read() 両方のメソッドをサポートしている必要があります。

fp に格納されているバイト列は、一連の RFC 2822 形式のヘッダおよびヘッダ継続行 (header continuation lines) によって構成されている必要があります。オプションとして、最初にエンペローブヘッダが来ることもできます。ヘッダ部分はデータの終端か、ひとつの空行によって終了したとみなされます。ヘッダ部分に続くデータはメッセージ本体となります (Content-Transfer-Encoding8bit の subpart も含めて、 MIME エンコードされた subpart を持っているかもしれません)。

オプション引数 headersonly はヘッダを読み終えた後にパースを止めるかを指定するフラグです。デフォルトは False で、ファイルの内容全体をパースします。

parsebytes(text, headersonly=False)

parse() メソッドに似ていますが、 file-like オブジェクトの代わりに bytes-like オブジェクト を引数として取るところが違います。このメソッドを呼び出すことは、 textBytesIO インスタンスでラップしてから parse() を呼び出すのと等価です。

オプション引数 headersonlyparse() メソッドものものと同様です。

バージョン 3.2 で追加.

ファイルや文字列からメッセージオブジェクト構造を作成するのはかなりよくおこなわれる作業なので、便宜上次のような 4つの関数が提供されています。これらは email パッケージのトップレベルの名前空間で使用できます。

email.message_from_string(s, _class=email.message.Message, *, policy=policy.compat32)

文字列からメッセージオブジェクト構造を作成して返します。これは Parser().parsestr(s) と完全に等価です。 _class および policyParser クラスのコンストラクタと同様に解釈されます。

バージョン 3.3 で変更: strict 引数の削除。キーワード引数 policy の追加。

email.message_from_bytes(s, _class=email.message.Message, *, policy=policy.compat32)

bytes-like オブジェクト からメッセージオブジェクト構造を作成して返します。これは BytesParser().parsebytes(s) と完全に等価です。オプション引数 _class および strictParser クラスのコンストラクタと同様に解釈されます。

バージョン 3.2 で追加.

バージョン 3.3 で変更: strict 引数の削除。キーワード引数 policy の追加。

email.message_from_file(fp, _class=email.message.Message, *, policy=policy.compat32)

オープンされた file object からメッセージオブジェクト構造を作成して返します。これは Parser().parse(fp) と完全に等価です。 _class および policyParser クラスのコンストラクタと同様に解釈されます。

バージョン 3.3 で変更: strict 引数の削除。キーワード引数 policy の追加。

email.message_from_binary_file(fp, _class=email.message.Message, *, policy=policy.compat32)

オープンされたバイナリ file object からメッセージオブジェクト構造を作成して返します。これは BytesParser().parse(fp) と完全に等価です。 _class および policyParser クラスのコンストラクタと同様に解釈されます。

バージョン 3.2 で追加.

バージョン 3.3 で変更: strict 引数の削除。キーワード引数 policy の追加。

対話的な Python プロンプトでこの関数を使用するとすれば、このようになります:

>>> import email
>>> msg = email.message_from_string(myString)  

19.1.2.3. 追加事項

以下はテキスト解析の際に適用されるいくつかの規約です:

  • ほとんどの非 multipart 形式のメッセージは単一の文字列ペイロードをもつ単一のメッセージオブジェクトとして解析されます。このオブジェクトは is_multipart() に対して False を返します。このオブジェクトに対する get_payload() メソッドは文字列オブジェクトを返します。

  • multipart 形式のメッセージはすべてメッセージ内コンポーネント (sub-message object) のリストとして解析されます。外側のコンテナメッセージオブジェクトは is_multipart() に対して True を返し、このオブジェクトに対する get_payload() メソッドは Message subpart のリストを返します。

  • message/* の Content-Type をもつほとんどのメッセージ (例: message/delivery-statusmessage/rfc822 など) もコンテナメッセージオブジェクトとして解析されますが、ペイロードのリストの長さは 1 になります。このオブジェクトは is_multipart() メソッドに対して True を返し、リスト内にあるひとつだけの要素がメッセージ内のコンポーネントオブジェクトになります。

  • いくつかの標準的でないメッセージは、 multipart の使い方に統一がとれていない場合があります。このようなメッセージは Content-Type ヘッダに multipart を指定しているものの、その is_multipart() メソッドは False を返すことがあります。もしこのようなメッセージが FeedParser によって解析されると、その defects 属性のリスト中には MultipartInvariantViolationDefect クラスのインスタンスが現れます。詳しい情報については email.errors を参照してください。

脚注

[1]

Python 2.4 から導入された email パッケージバージョン 3.0 では、旧式の ParserFeedParser によって書き直されました。そのためパーザの意味論と得られる結果は 2 つのパーザで同一のものになります。