簡體   English   中英

數據傳入時更新 json 文件的最佳方法

[英]Best way to update a json file as data is coming in

我正在運行一個循環,數據傳入並將數據寫入 json 文件。 這是一個最小的可驗證具體示例中的樣子。

import json
import random
import string

dc_master= {}
for i in range(100):
    # Below mimics an API call that returns new data.
    name = ''.join(random.choice(string.ascii_uppercase) for _ in range(15))
    dc_info= {}
    dc_info['Height'] = 'NA'

    dc_master[name] = dc_info

    with open("myfile.json", "w") as filehandle:
        filehandle.write(json.dumps(dc_master))

從上面可以看出,每次循環時,它都會創建一個新的 dc_info。 這成為寫入 json 文件的新鍵值對(鍵是名稱)的值。

上面的一個缺點是,當它失敗並重新啟動時,我必須從頭開始。 我是否應該打開以將 json 文件讀取到 dc_master,然后在字典中添加一個名稱:dc_info,然后在循環的每一輪將 dc_master 寫回 json 文件? 我是否應該只附加到 json 文件,即使它是重復的,並且當我需要使用它時,我會將它加載回字典並自動處理重復項?

附加信息:偶爾會出現超時,所以如果需要,我希望能夠在中間的某個地方開始。 dc_info中key value對的數量大約是30個,整體name:dc_info對的數量大約是1000個。所以不是很大。 把它讀出來再寫回去並不麻煩。 但我確實想知道是否有更有效的方法。

我認為你很好,我會遍歷整個事情並繼續寫入文件,因為它很便宜。

至於重試,您必須檢查超時,然后查看 JSON 文件是否已經存在,加載它,計算您的鍵,然后獲取丟失的條目數。

此外,您的示例可以簡化一些。

import json
import random
import string


dc_master = {}
for _ in range(100):
    name = ''.join(random.choice(string.ascii_uppercase) for _ in range(15))
    dc_master.update({name: {"Height": "NA"}})

with open("myfile.json", "w") as jf:
     json.dump(dc_master, jf, sort_keys=True, indent=4)

編輯:

再想一想,您可能希望使用 JSON 列表而不是字典作為頂級元素,這樣更容易檢查您已經擁有了多少。

import json
import os
import random
import string


output_file = "myfile.json"
max_entries = 100
dc_master = []


def do_your_stuff(data_container, n_entries=max_entries):
    for _ in range(n_entries):
        name = ''.join(random.choice(string.ascii_uppercase) for _ in range(15))
        data_container.append({name: {"Height": "NA"}})
    return data_container


def dump_data(data, file_name):
    with open(file_name, "w") as jf:
        json.dump(data, jf, sort_keys=True, indent=4)


if not os.path.isfile(output_file):
    dump_data(do_your_stuff(dc_master), output_file)
else:
    with open(output_file) as f:
        data = json.load(f)
        if len(data) < max_entries:
            new_entries = max_entries - len(data)
            dump_data(do_your_stuff(data, new_entries), output_file)
            print(f"Added {new_entries} entries.")
        else:
            print("Nothing to update.")

我認為獲取和存儲 API 結果的完整腳本應該類似於下面的示例代碼。 至少我總是為長時間處理的任務集做相同的代碼。

我將 API 調用的每個結果作為單獨的 JSON 單行放在結果文件中。

腳本可以在中間例如停止由於異常,文件將被正確地關閉和沖洗感謝with經理。 然后重新啟動腳本將從文件中讀取已處理的結果行。

只有那些尚未處理的結果(如果它們的 id 不在processed_ids )應該並且將從 API 中獲取。 id-field 可以是唯一標識每個 API 調用結果的任何內容。

每下一個結果將被附加到JSON線文件由於a模式(附加模式)。 buffering以字節為單位指定寫入緩沖區大小,文件將以此大小的塊刷新和寫入,這不是為了給磁盤帶來后續一行 100 字節的寫入壓力。 采用大buffering是完全正常的,因為Python的with塊正確刷新並寫出所有的字節塊時退出因異常或其它原因,所以你永遠不會失去甚至一個小的結果或字節已經已經被寫入由f.write(...)

最終結果將打印到控制台。

因為你的任務很有趣也很重要(至少我有很多次類似的任務),我決定也實現位於下面的單線程代碼的多線程版本,它是從獲取數據的情況下特別需要的Internet,因為通常需要在多個並行線程中下載數據。 多線程版本可以在這里這里找到並運行 通過使用我的另一個答案中的想法,這種多線程也可以擴展到多處理以提高效率。

接下來是單線程版本的代碼:

在這里在線嘗試下一個代碼!

import json, os, random, string

fname = 'myfile.json'
enc = 'utf-8'
id_field = 'id'

def ReadResults():
    results = []
    processed_ids = set()
    if os.path.exists(fname):
        with open(fname, 'r', encoding = enc) as f:
            data = f.read()
        results = [json.loads(line) for line in data.splitlines() if line.strip()]
        processed_ids = {r[id_field] for r in results}
    return (results, processed_ids)

# First read already processed elements
results, processed_ids = ReadResults()

with open(fname, 'a', buffering = 1 << 20, encoding = enc) as f:
    for id_ in range(100):
        # !!! Only process ids that are not in processed_ids !!!
        if id_ in processed_ids:
            continue        
        
        # Below mimics an API call that returns new data.
        # Should fetch only those objects that correspond to id_.
        name = ''.join(random.choice(string.ascii_uppercase) for _ in range(15))
        
        # Fill necessary result fields
        result = {}
        result['id'] = id_
        result['name'] = name
        result['field0'] = 'value0'
        result['field1'] = 'value1'
        
        cid = result[id_field] # There should be some unique id field
        
        assert cid not in processed_ids, f'Processed {cid} twice!'

        f.write(json.dumps(result, ensure_ascii = False) + '\n')
        
        results.append(result)
        processed_ids.add(cid)

print(ReadResults()[0])

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM