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

    オプション引数 headersonly は `parse()` メソッドと同じです。

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

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

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

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

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

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

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

  バージョン 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-status や message/rfc822 など) もコンテナメッセージオブジェクトとして解析されますが、ペイロードのリストの長さは 1 になります。このオブジェクトは `is_multipart()` メソッドに対して `True` を返し、リスト内にあるひとつだけの要素がメッセージ内のコンポーネントオブジェクトになります。
- いくつかの標準的でないメッセージは、 multipart の使い方に統一がとれていない場合があります。このようなメッセージは Content-Type ヘッダに multipart を指定しているものの、その `is_multipart()` メソッドは `False` を返すことがあります。もしこのようなメッセージが `FeedParser` によって解析されると、その defects 属性のリスト中には `MultipartInvariantViolationDefect` クラスのインスタンスが現れます。詳しい情報については `email.errors` を参照してください。

脚注

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