在R中:按主题行搜索所有电子邮件,从正文中提取逗号分隔的值,然后将值保存在数据框中



每天,我都会收到一封电子邮件,里面有某一天售出的水果数量。电子邮件的结构如下:

Date of report:,04-JAN-2022
Time report produced:,5-JAN-2022 02:04
Apples,6
Pears,1
Lemons,4
Oranges,2
Grapes,7
Grapefruit,2

我正试图在R中构建一些代码,这些代码将搜索我的电子邮件,找到所有具有特定主题的电子邮件,遍历每封电子邮件以找到我要查找的变量,获取值并将它们放在带有"的数据框中;报告日期";放在日期栏中。

在社区人员的帮助下,我能够在Python中实现预期的结果。然而,随着我的项目的发展,如果可能的话,我现在需要在R中实现同样的结果。

不幸的是,我是R的新手,因此如果有人对如何推进这一进程有任何建议,我将不胜感激

对于那些感兴趣的人,我的Python代码如下:

#PREP THE STUFF
Fruit_1 = "Apples"
Fruit_2 = "Pears"
searchf = [
Fruit_1, 
Fruit_2
]
#DEF THE STUFF
def get_report_vals(report, searches):
dct = {}
for line in report:
term, *value = line
if term.casefold().startswith('date'):
dct['date'] = pd.to_datetime(value[0])
elif term in searches:
dct[term] = float(value[0])
if len(dct.keys()) != len(searches):
dct.update({x: None for x in searches if x not in dct})
return dct

#DO THE STUFF
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6) 
messages = inbox.Items
messages.Sort("[ReceivedTime]", True)
results = []
for message in messages:
if message.subject == 'FRUIT QUANTITIES':
if Fruit_1 in message.body and Fruit_2 in message.body:
data = [line.strip().split(",") for line in message.body.split('n')]
results.append(get_report_vals(data, searchf))
else:
pass
fruit_vals = pd.DataFrame(results)
fruit_vals.columns = map(str.upper, fruit_vals.columns)

我可能是错误的,但我试图使用Python中的步骤来在R中获得相同的结果。因此,例如,我创建了一些变量来保存我正在搜索的水果销售额,然后创建了一个向量来存储可搜索项,然后当我创建一个等效的"get_vals"函数时,我会创建一个空向量。

library(RDCOMClient)
Fruit_1 <- "Apples"
Fruit_2 <- "Pears"
##Create vector to store searchables
searchf <- c(Fruit_1, Fruit_2)
## create object for outlook
OutApp <- COMCreate("Outlook.Application")
outlookNameSpace = OutApp$GetNameSpace("MAPI")
search <- OutApp$AdvancedSearch("Inbox", "urn:schemas:httpmail:subject = 'FRUIT QUANTITIES'")
inbox <- outlookNameSpace$Folders(6)$Folders("Inbox")
vec <- c()
for (x in emails)
{
subject <- emails(i)$Subject(1)
if (grepl(search, subject)[1])
{
text <- emails(i)$Body()
print(text)
break
}
}

read.table可能是get_report_vals的一个良好开端
下面的代码以列表形式输出结果,仍需执行异常处理:

report <- "
Date of report:,04-JAN-2022
Apples,6
Pears,1
Lemons,4
Oranges,2
Grapes,7
Grapefruit,2
"

get_report_vals <- function(report,searches) {
data <- read.table(text=report,sep=",")
colnames(data) <- c('key','value')
# find date
date <- data[grepl("date",data$key,ignore.case=T),"value"]

# transform dataframe to list
lst <- split(data$value,data$key)

# output result as list
c(list(date=date),lst[searches])
}
get_report_vals(report,c('Lemons','Oranges'))
$date
[1] "04-JAN-2022"
$Lemons
[1] "4"
$Oranges
[1] "2"

然后可以使用rbind:将各种报告的结果连接到data.frame

rbind(get_report_vals(report,c('Lemons','Oranges')),get_report_vals(report,c('Lemons','Oranges')))
date          Lemons Oranges
[1,] "04-JAN-2022" "4"    "2"    
[2,] "04-JAN-2022" "4"    "2"

代码现在按预期运行。函数的编写与推荐的有很大不同:

get_vals <- function(email) {
body <- email$body()
date <- str_extract(body, "\d{2}-[:alpha:]{3}-\d{4}") %>% 
as.character()
data <- read.table(text = body, sep = ",", skip = 9, strip.white = T) %>% 
row_to_names(1) %>% 
mutate("Date" = date)
return(data)
}

此外,我写这篇文章是为了将行绑定在一起:

info <- sapply(results, get_vals, simplify = F) %>% 
bind_rows()

也许这不是你所期望的答案,但我必须在这里声明,以帮助其他读者在未来避免此类错误。

不幸的是,您的Python代码写得不好。例如,我注意到以下代码,其中您迭代文件夹中的所有项目,并检查Subject和消息体中的关键字:

for message in messages:
if message.subject == 'FRUIT QUANTITIES':
if Fruit_1 in message.body and Fruit_2 in message.body:

您需要使用Items类的Find/FindNextRestrict方法。因此,您不需要对文件夹中的所有项目进行迭代。相反,你只得到与你的条件相对应的项目。在以下文章中阅读更多关于这些方法的信息:

  • 如何:使用Find和FindNext方法从文件夹中检索Outlook邮件项目(C#、VB.NET(
  • 如何:使用Restrict方法从文件夹中检索Outlook邮件项目

您可以将所有搜索条件组合到一个查询中。因此,您只需要迭代找到的项并提取数据。

此外,您可能会发现AdvancedSearch方法很有用。在Outlook中使用AdvancedSearch方法的主要好处是:

  • 搜索在另一个线程中执行。您不需要手动运行另一个线程,因为AdvancedSearch方法会在后台自动运行它
  • 可以在任何位置搜索任何项目类型:邮件、约会、日历、笔记等,即超出某个文件夹的范围。RestrictFind/FindNext方法可以应用于特定的Items集合(请参阅Outlook中Folder类的Items属性(
  • 完全支持DASL查询(自定义属性也可用于搜索(。您可以在MSDN上的"筛选"文章中了解更多有关此方面的信息。为了提高搜索性能,如果为商店启用了即时搜索,则可以使用即时搜索关键字(请参阅Store类的IsInstantSearchEnabled属性(
  • 您可以随时使用Search类的Stop方法停止搜索过程

有关详细信息,请参阅Outlook中以编程方式进行的高级搜索:C#、VB.NET。

最新更新