将大量查询写入文本文件

时间:2013-01-14 05:12:48

标签: python rest data-mining

我有一个大约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个小时左右,所以我猜想在将其写入文件之前会出现错误。 我想正确的方法是将它写成块,但不确定如何实现它。有任何想法吗? 另外,我不能用数据库来做这件事。

3 个答案:

答案 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应理解生成的文件。