Python 脚本:如何将输出缩短为有限的行大小?

Python 脚本:如何将输出缩短为有限的行大小?

我正在使用 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)

笔记:

  • 所有电子邮件都保存到同一个文件中
  • 具有相同域的线不一定相邻

分块迭代列表的最“pythonic”的方法是什么?

相关内容