我有一个大约200,000个实体的列表,我需要为每个实体查询特定的RESTful API,最后得到txt文件中以JSON格式保存的所有200,000个实体。 这种天真的方式是通过200,000个实体的列表并逐个查询,将返回的JSON添加到列表中,当它完成后,将所有内容添加到文本文件中。类似的东西:
from apiWrapper import api
from entities import listEntities #list of the 200,000 entities
a=api()
fullEntityList=[]
for entity in listEntities:
fullEntityList.append(a.getFullEntity(entity))
with open("fullEntities.txt","w") as f:
simplejson.dump(fullEntityList,f)
显然这不可靠,因为对API的200,000次查询大约需要10个小时左右,所以我猜想在将其写入文件之前会出现错误。 我想正确的方法是将它写成块,但不确定如何实现它。有任何想法吗? 另外,我不能用数据库来做这件事。
答案 0 :(得分:2)
我建议将它们写入SQLite数据库。这是他为我自己的小蜘蛛应用程序做的方式。因为您可以非常轻松地查询密钥,并检查您已检索到的密钥。这样,您的应用程序可以轻松地从中断处继续。特别是如果你下周增加了1000个新条目。
从一开始就将“恢复”设计到您的应用程序中。如果存在一些意外异常(例如,由于网络拥塞导致的超时),您不希望必须从头开始重新启动,而只需要那些尚未成功检索的查询。在200,000次查询时,99.9%的正常运行时间意味着您必须预期200次失败!
对于空间效率和性能,使用压缩格式可能会有所收获,例如在将json压缩到数据库blob之前用zlib压缩json。
SQLite是一个不错的选择,除非您的蜘蛛同时在多个主机上运行。对于单个应用程序,sqlite是完美的。
答案 1 :(得分:1)
简单的方法是以'a'
(追加)模式打开文件,并在它们进来时逐一编写。
更好的方法是使用作业队列。这将允许您将a.getFullEntity
调用生成到工作线程中,并根据需要处理结果,如果它们返回,或计划重试失败等。
请参阅Queue
。
答案 2 :(得分:0)
我还使用一个单独的Thread进行文件写入,并使用Queue
来记录所有实体。当我开始时,我认为这将在5分钟内完成,但后来结果有点困难。 simplejson
以及我所知道的所有其他此类库不支持部分写入,因此您无法先写入列表中的一个元素,稍后再添加其他等等。所以,我试图解决这是手动,将[
,,
和]
分别写入文件,然后单独转储每个实体。
无法检查(因为我没有你的api),你可以尝试:
import threading
import Queue
import simplejson
from apiWrapper import api
from entities import listEntities #list of the 200,000 entities
CHUNK_SIZE = 1000
class EntityWriter(threading.Thread):
lines_written = False
_filename = "fullEntities.txt"
def __init__(self, queue):
super(EntityWriter, self).__init()
self._q = queue
self.running = False
def run(self):
self.running = True
with open(self._filename,"a") as f:
while True:
try:
entity = self._q.get(block=False)
if not EntityWriter.lines_written:
EntityWriter.lines_written = True
f.write("[")
simplejson.dump(entity,f)
else:
f.write(",\n")
simplejson.dump(entity,f)
except Queue.Empty:
break
self.running = False
def finish_file(self):
with open(self._filename,"a") as f:
f.write("]")
a=api()
fullEntityQueue=Queue.Queue(2*CHUNK_SIZE)
n_entities = len(listEntities)
writer = None
for i, entity in listEntities:
fullEntityQueue.append(a.getFullEntity(entity))
if (i+1) % CHUNK_SIZE == 0 or i == n_entities-1:
if writer is None or not writer.running:
writer = EntityWriter(fullEntityQueue)
writer.start()
writer.join()
writer.finish_file()
此脚本执行的操作
主循环仍然遍历实体列表,获取每个实体的完整信息。之后,每个实体现在都被放入一个队列中。每1000个实体(以及列表的末尾)正在启动一个与主线程并行运行的EntityWriter-Thread。来自get
的EntityWriter Queue
并将其转储到所需的输出文件。
如上所述,我需要一些额外的逻辑来使JSON成为一个列表,我手动编写[
,,
和]
。原则上,当您重新加载时,simplejson
应理解生成的文件。