我正在使用 Python 脚本将域名与相应的电子邮件分离,然后根据各自的域名对电子邮件进行分组。以下脚本对我有用:
#!/usr/bin/env python3
from operator import itemgetter
from itertools import groupby
import os
import sys
dr = sys.argv[1]
for f in os.listdir(dr):
write = []
file = os.path.join(dr, f)
lines = [[l.strip(), l.split("@")[-1].strip()] for l in open(file).readlines()]
lines.sort(key=itemgetter(1))
for item, occurrence in groupby(lines, itemgetter(1)):
func = [s[0] for s in list(occurrence)]
write.append(item+","+",".join(func))
open(os.path.join(dr, "grouped_"+f), "wt").write("\n".join(write))
我使用了:python3 script.py /path/to/input files
我输入的是电子邮件列表,得到的结果如下:
domain1.com,[email protected],[email protected]
domain2.com,[email protected],[email protected],[email protected]
但是我面临的问题是由于 MongoDB 的限制。由于 MongoDB 的文档大小限制为 16 MB,因此 MongoDB 将输出文件中的单行视为 1 个文档,行大小不应超过 16 MB。
因此,我希望结果应限制为每个域 21 封电子邮件,如果域有更多电子邮件,则应将其与其余电子邮件一起打印在新行上(同样,如果电子邮件超过 21 封,则使用相同域名的换行符)。我可以将重复数据存储在 mongoDB 中。
所以最终的输出应该类似以下内容:
domain1.com,[email protected],[email protected],... [email protected]
domain1.com,[email protected],.....
domain2.com,[email protected],....
上面例子中的点 (.) 代表很多文本,我将其截断以使其简单易懂。
希望这能澄清我的问题并希望得到解决方案。
答案1
新版本
您发布的脚本确实按域对电子邮件进行分组,数量没有限制。下面的版本将按域对电子邮件进行分组,但将找到的列表拆分为任意块。每个块都将打印成一行,从相应的域开始。
剧本
#!/usr/bin/env python3
from operator import itemgetter
from itertools import groupby, islice
import os
import sys
dr = sys.argv[1]
size = 3
def chunk(it, size):
it = iter(it); return iter(lambda: tuple(islice(it, size)), ())
for f in os.listdir(dr):
# list the files
with open(os.path.join(dr, "chunked_"+f), "wt") as report:
file = os.path.join(dr, f)
# create a list of email addresses and domains, sort by domain
lines = [[l.strip(), l.split("@")[-1].strip()] for l in open(file).readlines()]
lines.sort(key=itemgetter(1))
# group by domain, split into chunks
for domain, occurrence in groupby(lines, itemgetter(1)):
adr = list(chunk([s[0] for s in occurrence], size))
# write lines to output file
for a in adr:
report.write(domain+","+",".join(a)+"\n")
使用
- 将脚本复制到一个空文件中,另存为
chunked_list.py
在头部部分,设置块大小:
size = 5
使用目录作为参数运行脚本:
python3 /path/to/chunked_list.py /path/to/files
然后,它将为每个文件创建一个编辑文件,名为
chunked_filename
,其中包含(分块的)分组电子邮件。
它能做什么
该脚本将包含如下文件的目录作为输入:
email1@domain1
email2@domain1
email3@domain2
email4@domain1
email5@domain1
email6@domain2
email7@domain1
email8@domain2
email9@domain1
email10@domain2
email11@domain1
对于每个文件,它会创建一个副本,例如:
domain1,email1@domain1,email2@domain1,email4@domain1
domain1,email5@domain1,email7@domain1,email9@domain1
domain1,email11@domain1
domain2,email3@domain2,email6@domain2,email8@domain2
domain2,email10@domain2
(设置 cunksize = 3)
答案2
为了支持任意大的目录和文件,你可以使用os.scandir()
逐个接收文件并逐行处理文件:
#!/usr/bin/env python3
import os
def emails_with_domain(dirpath):
for entry in os.scandir(dirpath):
if not entry.is_file():
continue # skip non-files
with open(entry.path) as file:
for line in file:
email = line.strip()
if email: # skip blank lines
yield email.rpartition('@')[-1], email # domain, email
要按域名对电子邮件地址进行分组,每行不超过 21 个电子邮件,您可以使用collections.defaultdict()
:
import sys
from collections import defaultdict
dirpath = sys.argv[1]
with open('grouped_emails.txt', 'w') as output_file:
emails = defaultdict(list) # domain -> emails
for domain, email in emails_with_domain(dirpath):
domain_emails = emails[domain]
domain_emails.append(email)
if len(domain_emails) == 21:
print(domain, *domain_emails, sep=',', file=output_file)
del domain_emails[:] # clear
for domain, domain_emails in emails.items():
print(domain, *domain_emails, sep=',', file=output_file)
笔记:
- 所有电子邮件都保存到同一个文件中
- 具有相同域的线不一定相邻