인용 된 답장에서 이메일 콘텐츠 구문 분석


88

포함 할 수있는 인용 된 답장 텍스트에서 이메일 텍스트를 구문 분석하는 방법을 알아 내려고합니다. 나는 보통 이메일 클라이언트가 "그런 날짜에 그렇게 썼다"를 붙이거나 줄 앞에 꺾쇠 괄호를 붙인다는 것을 알아 챘다. 불행히도 모든 사람이 이렇게하는 것은 아닙니다. 응답 텍스트를 프로그래밍 방식으로 감지하는 방법에 대한 아이디어가있는 사람이 있습니까? 이 파서를 작성하기 위해 C #을 사용하고 있습니다.


2
이것에 행운이 있었나요? 나는 똑같은 일을하려고합니다.
steve_c

전체 소스 코드 샘플이 작동하는 최종 솔루션이 있습니까?
Kiquenet 2013-06-18

Quotequail 은 Python 으로이 작업을 수행합니다
philfreo

누구든지 PHP 버전을 도울 수 있습니까?
user4271704 2015

답변:


60

나는 이것에 대해 더 많은 검색을했고 여기에 내가 찾은 것이 있습니다. 기본적으로이 작업을 수행하는 두 가지 상황이 있습니다. 전체 스레드가있는 경우와없는 경우입니다. 이 두 가지 범주로 나눌 것입니다.

스레드가있는 경우 :

일련의 전체 이메일이있는 경우 제거중인 항목이 실제로 인용 된 텍스트라는 매우 높은 수준의 확신을 얻을 수 있습니다. 이를 수행하는 두 가지 방법이 있습니다. 첫째, 메시지의 Message-ID, In-Reply-To ID 및 Thread-Index를 사용하여 개별 메시지, 상위 메시지 및 해당 스레드를 확인할 수 있습니다. 이에 대한 자세한 내용은 RFC822 , RFC2822 , 스레딩에 대한 흥미로운 기사 또는 스레딩에 대한이 기사를 참조하십시오. . 스레드를 다시 어셈블하면 외부 텍스트 (예 : To, From, CC 등 ... 행)를 제거 할 수 있으며 완료됩니다.

작업중인 메시지에 헤더가없는 경우 유사성 일치를 사용하여 이메일의 어떤 부분이 회신 텍스트인지 확인할 수도 있습니다. 이 경우 반복되는 텍스트를 결정하기 위해 유사성 일치를 수행해야합니다. 이 경우 코드 프로젝트 또는 알고리즘 과 같은 Levenshtein Distance 알고리즘 을 살펴볼 수 있습니다 .

스레딩 프로세스에 관심이 있다면 이메일 스레드 재 조립에 대한이 훌륭한 PDF를 확인하십시오 .

스레드가없는 경우 :

스레드에서 단 하나의 메시지 만 갇힌 경우 인용문이 무엇인지 추측해야합니다. 이 경우 내가 본 다른 견적 방법은 다음과 같습니다.

  1. 선 (Outlook에서 볼 수 있음).
  2. 꺾쇠 괄호
  3. "--- 원본 메시지 ---"
  4. "그런 날에 그렇게 썼다."

거기에서 텍스트를 제거하면 완료됩니다. 이들 중 하나의 단점은 모두 발신자가 인용 된 텍스트 위에 답장을 넣고 인터리브하지 않았다고 가정한다는 것입니다 (인터넷의 이전 스타일처럼). 그럴 경우 행운을 빕니다. 나는 이것이 당신 중 일부를 돕기를 바랍니다!


32

우선 이것은 까다로운 작업입니다.

다른 전자 메일 클라이언트에서 일반적인 응답을 수집하고이를 구문 분석 할 올바른 정규식 (또는 기타)을 준비해야합니다. Outlook, thunderbird, gmail, apple mail 및 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: test@test.com [mailto:test@test.com] 
Sent: Tuesday, January 13, 2009 1:27 PM
----
2008/12/26 <test@test.com>

>  text
----
test@test.com wrote:
> text
----
      test@test.com wrote:         text
text
----
2009/1/13 <test@test.com>

>  text
----
 test@test.com wrote:         text
 text
----
2009/1/13 <test@test.com>

> text
> text
----
2009/1/13 <test@test.com>

> text
> text
----
test@test.com wrote:
> text
> text
<response here>
----
--- On Fri, 23/1/09, test@test.com <test@test.com> wrote:

> text
> text

감사합니다, Oleg Yaroshevych


이메일 주소를 모르면 어떻게합니까?
harsimranb

Shyamal-Parikh @이는 일반적으로 일반 텍스트 메시지는 전자 메일 메시지하지 HTML 이메일에 대한 작업 만 포함됩니다
maembe

25

정규식에 대해 Goleg, 감사합니다! 정말 도움이되었습니다. 이것은 C #이 아니지만 Google 직원을위한 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

지금까지 꽤 잘 작동했습니다.


1
루비 질문을 만들고 ac # 질문에 게시하는 대신이 코드로 답해야합니다.
Matthieu 2011 년

6
@Matthieu, C # 질문이 아니라 이메일 및 이메일 구문 분석 질문입니다. 제 생각에는 완전히 관련이 있습니다.
Trent

@Trent : 그런 다음 C # 태그를 삭제해야합니다.
Matthieu

7
재미있는 점은 인터넷 검색에서 해당 주제 (언어가 아님)에 대한이 질문을 찾았고 실제로 Ruby로 구현해야했습니다. 그래서 건배!
bratsche 2012

2
이것은 지금까지 최고의 응답입니다. Regex는 언어에 구애받지 않습니다. 게시 주셔서 감사합니다
superluminary

11

이를 수행하는 가장 쉬운 방법은 다음과 같은 콘텐츠에 마커를 배치하는 것입니다.

---이 줄 위에 답장 해주세요 ---

의심의 여지없이, 인용 된 텍스트를 구문 분석하는 것은 다른 이메일 클라이언트가 다른 방식으로 텍스트를 인용하므로 사소한 작업이 아닙니다. 이 문제를 제대로 해결하려면 모든 이메일 클라이언트를 고려하고 테스트해야합니다.

Facebook은 이것을 할 수 있지만 프로젝트에 큰 예산이 없다면 아마 그렇게 할 수 없습니다.

Oleg는 정규식을 사용하여 "On 13 Jul 2012, at 13:09, xxx written :"텍스트를 찾아 문제를 해결했습니다. 그러나 사용자가이 텍스트를 삭제하거나 이메일 하단에 답장하면 많은 사람들이하는 것처럼이 솔루션은 작동하지 않습니다.

마찬가지로 이메일 클라이언트가 다른 날짜 문자열을 사용하거나 날짜 문자열을 포함하지 않으면 정규식이 실패합니다.


이 방법은 회신 할 때마다 해당 줄을 입력하지 않는 한 회신에 대한 회신으로 실패합니다.
jpw

1
예, 단점이 있습니다. 사용자가 행 문자열 위의 응답을 삭제하면 응답이 실패합니다. 이 경우를 파악하고 웹 앱을 통해 회신 할 수있는 링크와 함께 메시지가 실패했음을 알리는 직접 메시지를 사용자에게 보냅니다. 대부분의 사용자는 별다른 문제없이 사용할 수있는 것 같습니다.
슈퍼 루미

이것은 받아 들여진 대답이어야합니다. 그러나 줄이 제거되면 대답이 성공하지 못한다는 정보를 추가합니다.
Benni

@Benni-예, 줄이 제거되면 실패합니다. 안타깝게도 이메일 클라이언트에서 텍스트를 인용하는 표준 방법은 없습니다. 줄이 제거 된 경우 모든 텍스트를 회신으로 처리 할 수 ​​있습니다. 이 경우 완벽한 해결책은 불가능하다고 생각합니다.
superluminary

@superluminary 내 말은 라인에 추가 할 것입니다. 그래서 그것은 같은 것 -- Please reply above this line. DO NOT REMOVE IT! --입니다. 또한 내가 경험 한 것은 일부 이메일 클라이언트 xxx wrote on <datetime>:가 전체 견적 앞과 그 라인 앞에 한 줄을 추가하기 때문에 항상 작동하지 않는다는 것입니다 . 이 줄은 정규식으로 구문 분석 할 수 있지만 이메일 클라이언트가 다르기 때문에 언어와 형식이 다를 수 있습니다.
Benni

7

전자 메일에는 회신에 대한 보편적 인 표시가 없습니다. 할 수있는 최선의 방법은 가장 일반적인 패턴을 포착하고 새로운 패턴을 발견 할 때 파싱하는 것입니다.

어떤 사람들은 인용 된 텍스트 안에 답글을 삽입하므로 (예를 들어 내 상사가 내가 질문 한 것과 같은 줄에 질문에 답함) 어떤 일을하더라도 보관하고 싶었던 정보를 잃을 수도 있습니다.


Gmail은 그것을합니다 ... 적어도 그것을하는 것 같습니다. 내가 기억하는 것에서 원본과 응답 사이에 변경되지 않는 스레드 ID가 있습니다 ...
kenny

Gmail은 다른 이메일 클라이언트와 마찬가지로 '>'를 추가 할 수 있지만 이메일의 표준이 아니며 믿을 수있는 것이 아닙니다.
3Doubloons

5

다음은 @hurshagrawal의 Ruby 코드의 C # 버전입니다. 루비를 잘 모르기 때문에 꺼질 수 있지만 제대로 된 것 같습니다.

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();
}

3

원본 메시지 (예 : 웹 응용 프로그램의 알림)를 제어하는 ​​경우 고유하고 식별 가능한 헤더를 배치하고 원본 게시물의 구분자로 사용할 수 있습니다.


0

이것은 좋은 해결책입니다. 너무 오래 찾아서 찾았습니다.

위에서 언급했듯이 한 가지 추가 사항은 대소 문자가 현명하기 때문에 위의 표현은 내 gmail 및 outlook (2010) 응답을 올바르게 구문 분석하지 않았으므로 다음 두 개의 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),

건배


누구든지 PHP 버전을 도울 수 있습니까?
user4271704 2015


-1

그러나 github 에 답장을 추출 하는 Ruby lib 가 있다는 것을 알고 있는지 확실하지 않습니다 . .NET을 사용하는 경우 https://github.com/EricJWHuang/EmailReplyParser에 .NET이 있습니다.


1
외부 리소스에 대한 링크가 권장되지만 링크 주변에 컨텍스트를 추가하여 동료 사용자가 그것이 무엇이며 왜 거기에 있는지 알 수 있도록하십시오. 대상 사이트에 연결할 수 없거나 영구적으로 오프라인 상태가되는 경우를 대비하여 항상 중요한 링크에서 가장 관련성이 높은 부분을 인용하십시오.
pableiros

그 라이브러리를 최신 상태로 유지하고 있습니까? C # 라이브러리가 Office 365에서 Outlook의 간단한 전자 메일을 제대로 구문 분석하지 못하기 때문에 검색했습니다. 그런 다음 루비 소스 코드를 살펴본 결과 테스트 사례에 동일한 테스트 사례가 있다는 사실을 발견했습니다. 그것.
Greg Veres 19 년

-2

SigParser.com 의 API 를 사용 하는 경우 단일 이메일 텍스트 문자열에서 회신 체인에있는 모든 분리 된 이메일의 배열을 제공합니다. 따라서 10 개의 이메일이있는 경우 10 개의 이메일 모두에 대한 텍스트를 받게됩니다.

여기에 이미지 설명 입력

여기에서 자세한 API 사양을 볼 수 있습니다.

https://api.sigparser.com/

여기에 이미지 설명 입력

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.