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