web-dev-qa-db-ja.com

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に制限されているため、出力ファイルの1行はMongoDBによって1つのドキュメントと見なされ、行サイズは16 MBを超えてはなりません。
したがって、結果はドメインごとに21通のメールに制限され、ドメインにさらにメールがある場合は、残りのメールと一緒に新しい行に印刷する必要があります(メールが21を超えている場合は、同じドメイン名の改行)。 mongoDBに重複データを保存します。

したがって、最終的な出力は次のようになります。

domain1.com,[email protected],[email protected],... [email protected]
domain1.com,[email protected],.....
domain2.com,[email protected],....

上記の例のドット(。)は多くのテキストを表していますが、理解しやすいように切り取りました。
これで私の問題が明確になり、解決策が得られることを期待してください。

1
Jaffer Wilson

新しいバージョン

あなたが投稿したスクリプトは、実際、メールをドメイン別にグループ化し、数に制限はありません。ドメインの下で電子メールをグループ化するが、見つかったリストを任意のチャンクに分割するバージョンの下。各チャンクは、対応するドメインで始まる行に印刷されます。

スクリプト

#!/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に設定)

1
Jacob Vlijm

任意の大きなディレクトリとファイルをサポートするには、 se os.scandir() ファイルを1つずつ受信し、ファイルを1行ずつ処理します。

#!/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

ドメインごとに電子メールアドレスをグループ化するには、1行に21を超えないようにします。 se 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)

注意:

  • すべてのメールは同じファイルに保存されます
  • 同じドメインの行は必ずしも隣接していない

チャンクのリストを反復する最も「Python」の方法は何ですか?

1
jfs