18.1.2. email.parser: 電子メールメッセージを解析(パース)する

メッセージオブジェクト構造体をつくるには 2つの方法があります。ひとつはまったくのスクラッチから Message を生成して、これを attach()set_payload() 呼び出しを介してつなげていく方法で、もうひとつは電子メールメッセージのフラットなテキスト表現を解析 (parse、パーズ) する方法です。

email パッケージでは、MIME 文書をふくむ、ほとんどの電子メールの文書構造に対応できる標準的なパーザ (解析器) を提供しています。このパーザに文字列あるいはファイルオブジェクトを渡せば、パーザはそのオブジェクト構造の基底となる (root の) Message インスタンスを返します。簡単な非MIMEメッセージであれば、この基底オブジェクトのペイロードはたんにメッセージのテキストを格納する文字列になるでしょう。 MIMEメッセージであれば、基底オブジェクトはその is_multipart() メソッドに対して True を返します。そして、その各 subpart に get_payload() メソッドおよび walk() メソッドを介してアクセスすることができます。

実際には 2つのパーザインターフェイスが使用可能です。ひとつは旧式の Parser API であり、もうひとつはインクリメンタルな FeedParser API です。旧式の Parser API はメッセージ全体のテキストが文字列としてすでにメモリ上にあるか、それがローカルなファイルシステム上に存在しているときには問題ありません。 FeedParser はメッセージを読み込むときに、そのストリームが入力待ちのためにブロックされるような場合 (ソケットから email メッセージを読み込む時など) に、より有効です。 FeedParser はインクリメンタルにメッセージを読み込み、解析します。パーザを close したときには根っこ (root) のオブジェクトのみが返されます [1]

このパーザは、ある制限された方法で拡張できます。また、もちろん自分でご自分のパーザを完全に無から実装することもできます。 email パッケージについているパーザと Message クラスの間に隠された秘密の関係はなにもありませんので、ご自分で実装されたパーザも、それが必要とするやりかたでメッセージオブジェクトツリーを作成することができます。

18.1.2.1. FeedParser API

バージョン 2.4 で追加.

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])

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

feed(data)

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

close()

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

18.1.2.2. Parser クラス API

email.parser モジュールからインポートされる Parser クラスは、メッセージを表すテキストが文字列またはファイルの形で完全に使用可能なときメッセージを解析するのに使われる API を提供します。 email.Parser モジュールはまた、 HeaderParser と呼ばれる2番目のクラスも提供しています。これはメッセージのヘッダのみを処理したい場合に使うことができ、ずっと高速な処理がおこなえます。なぜならこれはメッセージ本体を解析しようとはしないからです。かわりに、そのペイロードにはメッセージ本体の生の文字列が格納されます。 HeaderParser クラスは Parser クラスと同じ API をもっています。

class email.parser.Parser([_class])

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

オプション引数 strict は無視されます。

バージョン 2.4 で非推奨: Parser は Python 2.4 で新しく導入された FeedParser の後方互換性のための API ラッパで、 すべての 解析が事実上 non-strict です。 Parser コンストラクタに strict フラグを渡す必要はありません。

バージョン 2.2.2 で変更: strict フラグが追加されました.

バージョン 2.4 で変更: strict フラグは推奨されなくなりました.

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

parse(fp[, headersonly])

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

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

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

バージョン 2.2.2 で変更: headersonly フラグが追加されました.

parsestr(text[, headersonly])

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

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

バージョン 2.2.2 で変更: headersonly フラグが追加されました.

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

email.message_from_string(s[, _class[, strict]])

文字列からメッセージオブジェクト構造を作成し返します。これは Parser().parsestr(s) とまったく同じです。オプション引数 _class および strictParser クラスのコンストラクタと同様に解釈されます。

バージョン 2.2.2 で変更: strict フラグが追加されました.

email.message_from_file(fp[, _class[, strict]])

Open されたファイルオブジェクトからメッセージオブジェクト構造を作成し返します。これは Parser().parse(fp) とまったく同じです。オプション引数 _class および strictParser クラスのコンストラクタと同様に解釈されます。

バージョン 2.2.2 で変更: strict フラグが追加されました.

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

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

18.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 つのパーザで同一のものになります。