web-dev-qa-db-ja.com

引用された返信からメールの内容を解析する

私はそれが含むかもしれない引用された返信テキストからメールのテキストを解析する方法を理解しようとしています。私は通常、電子メールクライアントが「そのような日付にそのように書いた」または行の先頭に山かっこを付けることに気づきました。残念ながら、誰もがこれを行うわけではありません。プログラムで返信テキストを検出する方法について何か考えはありますか?このパーサーを作成するためにC#を使用しています。

84
VanOrman

私はこれについてより多くの検索を行いました、そしてここに私が見つけたものがあります。これを行う状況は基本的に2つあります。スレッド全体がある場合とそうでない場合です。それを次の2つのカテゴリに分けます。

スレッドがある場合:

一連の電子メール全体がある場合、削除するものが実際に引用されたテキストであることを非常に高いレベルで保証できます。これを行うには2つの方法があります。 1つは、メッセージのMessage-ID、In-Reply-To ID、およびThread-Indexを使用して、個々のメッセージ、その親、およびメッセージが属するスレッドを判別することです。これの詳細については、 RFC822RFC2822スレッドに関するこの興味深い記事 、または スレッドに関するこの記事 を参照してください。 。スレッドを再構成したら、外部テキスト(To、From、CCなど)を削除して完了です。

処理中のメッセージにヘッダーがない場合は、類似性マッチングを使用して、メールのどの部分が返信テキストかを判別することもできます。この場合、類似するマッチングを実行して、繰り返されるテキストを決定することに行き詰まっています。この場合、 Levenshtein Distance algorithm を調べたい場合があります。たとえば、 Code Projectのthis one または this one などです。

何があっても、スレッドプロセスに興味がある場合は、チェックしてください this great PDF on reassembleing email threads

スレッドがない場合:

スレッドからのメッセージが1つしかない場合は、引用が何であるかを推測する必要があります。その場合、私が見たさまざまな見積方法は次のとおりです。

  1. 行(Outlookに表示されます)。
  2. 角括弧
  3. " - -オリジナルメッセージ - -"
  4. 「そのような日に、次のように書いた:」

下からテキストを削除すれば完了です。これらのいずれかの欠点は、送信者が引用されたテキストの上に返信を置き、それをインターリーブしなかったとすべてが想定されていることです(インターネット上の古いスタイルの場合と同様)。それが起こった場合、幸運を祈ります。これがあなた方の何人かを助けることを願っています!

60
VanOrman

まず、これはトリッキーな作業です。

さまざまな電子メールクライアントからの典型的な応答を収集し、それらを解析するための正しい正規表現(または何でも)を準備する必要があります。 Outlook、Thunderbird、gmail、Apple mail and mail.ruからの応答を収集しました。

正規表現を使用して、次の方法で応答を解析しています。表現が一致しなかった場合は、次の表現を使用してみます。

new Regex("From:\\s*" + Regex.Escape(_mail), RegexOptions.IgnoreCase);
new Regex("<" + Regex.Escape(_mail) + ">", RegexOptions.IgnoreCase);
new Regex(Regex.Escape(_mail) + "\\s+wrote:", RegexOptions.IgnoreCase);
new Regex("\\n.*On.*(\\r\\n)?wrote:\\r\\n", RegexOptions.IgnoreCase | RegexOptions.Multiline);
new Regex("-+original\\s+message-+\\s*$", RegexOptions.IgnoreCase);
new Regex("from:\\s*$", RegexOptions.IgnoreCase);

最後に引用を削除するには:

new Regex("^>.*$", RegexOptions.IgnoreCase | RegexOptions.Multiline);

これが私のテスト応答の小さなコレクションです(サンプルを---で割ったもの):

From: [email protected] [mailto:[email protected]] 
Sent: Tuesday, January 13, 2009 1:27 PM
----
2008/12/26 <[email protected]>

>  text
----
[email protected] wrote:
> text
----
      [email protected] wrote:         text
text
----
2009/1/13 <[email protected]>

>  text
----
 [email protected] wrote:         text
 text
----
2009/1/13 <[email protected]>

> text
> text
----
2009/1/13 <[email protected]>

> text
> text
----
[email protected] wrote:
> text
> text
<response here>
----
--- On Fri, 23/1/09, [email protected] <[email protected]> wrote:

> text
> text

よろしく、Oleg Yaroshevych

30

正規表現をありがとう、Goleg!本当に役に立ちました。これはC#ではありませんが、世のグーグルのために、ここに私のRuby解析スクリプトがあります:

def extract_reply(text, address)
    regex_arr = [
      Regexp.new("From:\s*" + Regexp.escape(address), Regexp::IGNORECASE),
      Regexp.new("<" + Regexp.escape(address) + ">", Regexp::IGNORECASE),
      Regexp.new(Regexp.escape(address) + "\s+wrote:", Regexp::IGNORECASE),
      Regexp.new("^.*On.*(\n)?wrote:$", Regexp::IGNORECASE),
      Regexp.new("-+original\s+message-+\s*$", Regexp::IGNORECASE),
      Regexp.new("from:\s*$", Regexp::IGNORECASE)
    ]

    text_length = text.length
    #calculates the matching regex closest to top of page
    index = regex_arr.inject(text_length) do |min, regex|
        [(text.index(regex) || text_length), min].min
    end

    text[0, index].strip
end

これまでのところ、かなりうまくいきます。

24
hurshagrawal

これを行う最も簡単な方法は、次のようなマーカーをコンテンツに配置することです。

---この行の上に返信してください---

お気づきのように、引用されたテキストを解析することは、異なるメールクライアントが異なる方法でテキストを引用するため、簡単な作業ではありません。この問題を適切に解決するには、すべての電子メールクライアントを考慮してテストする必要があります。

Facebookはこれを行うことができますが、プロジェクトに大きな予算がない限り、おそらくできません。

Olegは正規表現を使用して問題を解決し、「2012年7月13日13:09にxxxが書き込んだ:」というテキストを見つけました。ただし、ユーザーがこのテキストを削除したり、電子メールの下部で返信したりすると、多くの人がそうするように、このソリューションは機能しません。

同様に、電子メールクライアントが別の日付文字列を使用する場合、または日付文字列を含まない場合、正規表現は失敗します。

11
superluminary

電子メールの返信の普遍的なインジケータはありません。あなたができる最善のことは、最も一般的なパターンを見つけて、それらに遭遇したときに新しいパターンを解析することです。

一部の人々は引用されたテキスト内に返信を挿入することを覚えておいてください(たとえば、私の上司は私が彼らに尋ねたのと同じ行で質問に回答します)。

6
3Doubloons

@hurshagrawalのRubyコードの私のC#バージョンです。Rubyわからないので、オフになっている可能性がありますが、正しく理解できたと思います。

public string ExtractReply(string text, string address)
{
    var regexes = new List<Regex>() { new Regex("From:\\s*" + Regex.Escape(address), RegexOptions.IgnoreCase),
                        new Regex("<" + Regex.Escape(address) + ">", RegexOptions.IgnoreCase),
                        new Regex(Regex.Escape(address) + "\\s+wrote:", RegexOptions.IgnoreCase),
                        new Regex("\\n.*On.*(\\r\\n)?wrote:\\r\\n", RegexOptions.IgnoreCase | RegexOptions.Multiline),
                        new Regex("-+original\\s+message-+\\s*$", RegexOptions.IgnoreCase),
                        new Regex("from:\\s*$", RegexOptions.IgnoreCase),
                        new Regex("^>.*$", RegexOptions.IgnoreCase | RegexOptions.Multiline)
                    };

    var index = text.Length;

    foreach(var regex in regexes){
        var match = regex.Match(text);

        if(match.Success && match.Index < index)
            index = match.Index;
    }

    return text.Substring(0, index).Trim();
}
5
Austin

元のメッセージ(Webアプリケーションからの通知など)を制御する場合は、識別可能な識別可能なヘッダーを配置して、元の投稿の区切り文字として使用できます。

3
Eric R. Rath

SigParser.com のAPIを使用すると、単一の電子メールテキスト文字列からの返信チェーン内のすべての分割された電子メールの配列が得られます。したがって、10通のメールがある場合、10通すべてのメールのテキストが表示されます。

enter image description here

詳細なAPI仕様はこちらで確認できます。

https://api.sigparser.com/

enter image description here

0
Paul Mendoza

古い投稿ですが、githubが a Ruby lib 返信を抽出していることを知っているかどうかはわかりません。NETを使用している場合は、.NETを使用しています https://github.com/EricJWHuang/EmailReplyParser

0
Eric Huang

これは良い解決策です。そんなに探してみて見つけた。

上記のように、これは大文字と小文字の区別があるため、上記の式はGmailとOutlook(2010)の応答を正しく解析しませんでした。そのため、次の2つのRegexを追加しました。問題があれば教えてください。

//Works for Gmail
new Regex("\\n.*On.*<(\\r\\n)?" + Regex.Escape(address) + "(\\r\\n)?>", RegexOptions.IgnoreCase),
//Works for Outlook 2010
new Regex("From:.*" + Regex.Escape(address), RegexOptions.IgnoreCase),

乾杯

0
Amit M