关于python:电子邮件解析器处理个人数据;

Email parser work on individual data; breaks when used in loops list comprehensions, then breaks on original data as well… then works with map

这里有一些奇怪的神秘行为。

编辑这个已经很长很纠结了,我已经编辑了10次了。TL/DR是在处理某些文本的过程中,我设法编写了一个函数:

  • 处理列表中的单个字符串

  • 当我试图用列表理解将它应用到整个列表时,会抛出各种错误

  • 当我试图用循环将其应用于整个列表时,会引发类似的错误

  • 在抛出这些错误之后,停止处理单个字符串,直到我重新运行函数定义并向其提供一些示例数据,然后它再次开始工作,最后

  • 当我用map()将它应用到整个列表时,结果是有效的。

有一个保存为html的ipython笔记本显示了这里的混乱:http://paul-gowder.com/wtf.html---我在上面放了一个链接,可以跳过一些不相关的东西。我还做了一个[另一个]要点,只是有问题代码和一些示例数据,但由于这个问题似乎以某种方式抛出了一堆状态,我不能保证它可以从中复制:https://gist.github.com/paultopia/402891d05dd8c05995d2

结束TL/DR,开始混乱

我正在对旧的安然数据集进行一些玩具文本挖掘,在加载了nltk stopwords等之后,我有以下一组功能来清理准备将其转换为文档术语矩阵的电子邮件。下面使用了python 2.7中的电子邮件库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def parseEmail(document):
    # strip unnecessary headers, header text, etc.
    theMessage = email.message_from_string(document)
    tofield = theMessage['to']
    fromfield = theMessage['from']
    subjectfield = theMessage['subject']
    bodyfield = theMessage.get_payload()
    wholeMsgList = [tofield, fromfield, subjectfield, bodyfield]
    # get rid of any fields that don't exist in the email
    cleanMsgList = [x for x in wholeMsgList if x is not None]
    # now return a string with all that stuff run together
    return ' '.join(cleanMsgList)

def lettersOnly(document):
    return re.sub("[^a-zA-Z]","", document)

def wordBag(document):
    return lettersOnly(parseEmail(document)).lower().split()

def cleanDoc(document):
    dasbag = wordBag(document)
    # get rid of"enron" for obvious reasons, also the .com
    bagB = [word for word in dasbag if not word in ['enron','com']]
    unstemmed =[word for word in bagB if not word in stopwords.words("english")]
    return [stemmer.stem(word) for word in unstemmed]

print enronEmails[0][1]

print cleanDoc(enronEmails[0][1])

在用Unicode字符串表示的电子邮件上运行这个命令的第一个(t减去半小时)产生了预期的结果:print cleanDoc(enronEmails[0][1])产生了一个词干单词列表。要清楚,基础数据登记表是一个[Label,Message]列表的列表,其中Label是整数0或1,Message是Unicode字符串。(在python 2.7中。)

然后在T-10,我添加了几行代码(由于删除和丢失,不幸的是……但见下文),其中包含一些列表理解,以便从Enrolemail中提取消息,对它们运行清除功能,然后将它们重新连接到字符串中,以便通过sklearn方便地转换为文档术语矩阵。但是函数开始抛出错误。所以我戴上了调试帽…

首先,我尝试重新运行原始定义和测试单元。但当我重新运行该单元格时,我的电子邮件解析函数突然开始在消息中抛出错误"from_string method:

1
AttributeError: 'list' object has no attribute 'message_from_string'

所以这很奇怪。这是完全相同的函数,对完全相同的数据调用:cleanDoc(enronEmails[0][1])。这个函数在同一个数据上运行,我没有更改它。

所以检查了以确保我没有改变数据。enronEmails[0][1]仍然是一个字符串。不是清单。我不知道为什么TraceBack认为我正在向CleanDoc()传递一个列表。我没有。

但情节越来越复杂

然后我去了一个"制造要点"来创建一个完全可复制的例子来发布这个问题。我从工作部分开始。要点:https://gist.github.com/paultopia/c8c3e066c39336e5f3c2。

为了确保它能正常工作,首先我把它放在一个普通的.py文件中,然后从命令行运行它。它起作用了。

然后我把它和其他东西一起放在ipython笔记本底部的一个手机里。这也起作用了。

然后我在enronEmails[0][1]上尝试了parsemail函数。这又起作用了。然后我一路返回到五分钟前抛出错误的原始单元格,并重新运行它(包括从sklearn导入的内容,以及所有函数的原始定义)。这真的很管用。

但是那时然后我又回到里面,用清单上的理解之类的东西再试了一次。这一次,我更仔细地跟踪发生的事情。添加以下单元格:

1。

1
2
3
def atLeastThreeString(cleandoc):
    return ' '.join([w for w in cleandoc if len(w)>2])
print atLeastThreeString(cleanDoc(enronEmails[0][1]))

这可以工作,并产生预期的输出:单词超过2个字母的字符串。但接下来:2。

1
2
justEmails = [email[1] for email in enronEmails]
bigEmailsList = [atLeastThreeString(cleanDoc(email)) for email in justEmails]

突然间,它开始抛出一个全新的错误,在回溯中的相同位置:

1
AttributeError: 'unicode' object has no attribute 'message_from_string'

这很有趣,因为我一分钟前给它传递了unicode字符串,它做得很好。而且,只是为了加强情节,然后返回并重新运行cleanDoc(enronEmails[0][1])会抛出相同的错误。

这让我发疯了。如何可能创建一个新列表,然后尝试在该列表上运行函数A,不仅会在新列表上引发错误,还会导致函数A在以前处理的数据上引发错误?我知道我不会改变原来的名单…

如果有人想看到完整的代码和回溯,我已经在这里以HTML格式发布了整个笔记本:http://paul-gowder.com/wtf.html相关部分从下面的2/3开始,在24-5号单元格,在那里工作,然后26号单元格,wh


我认为问题在于这些阶段:

1
2
justEmails = [email[1] for email in enronEmails]
bigEmailsList = [atLeastThreeString(cleanDoc(email)) for email in justEmails]

在python 2中,虚拟变量email会泄漏到名称空间中,因此您要重写email模块的名称,然后尝试在python字符串上从该模块调用一个方法。我在python 2中没有ntlk,所以我无法测试它,但我认为这一定是它。