使用JSONlines刮取大量数据时保持正确的JSON结构

时间:2018-03-20 18:39:20

标签: python json scrapy jsonlines

最近,我不得不刮掉大量数据,并将使用Feed格式'json'改为'jsonlines',以避免全部乱码和重复。问题是,现在我的程序都没有将导出的文件识别为JSON,因为它删除了每个项目后的开始和结束方括号以及逗号。第一个例子显示了数据的样子,第二个例子显示了我想要实现的内容。

    {"name": "Color TV", "price": "1200"}
    {"name": "DVD player", "price": "200"}

    ---------------------------------------

    {"data" : [
    {"name": "Color TV", "price": "1200"},
    {"name": "DVD player", "price": "200"},
    {"name": "Color TV", "price": "1200"}
    ]}

有没有办法在仍然使用JsonLinesItemExporter时手动添加逗号并使其成为数组?

我想象的唯一一段代码是我的yield关键字,但我很高兴能够显示完整的代码。我没有使用PHP或MySQL。

非常感谢你。

    yield {
            "name": name,
            "old_price": old_price,
            "discount_price": discount_price
        }

1 个答案:

答案 0 :(得分:1)

首先是逗号。

最好的解决方案是将JsonLinesItemExporter换行,以便在每个项目的末尾添加逗号。

如果没有以可以覆盖它的方式公开适当的方法super,并添加逗号,则可能必须重新实现子类中的方法,甚至是monkeypatch导出器类。不太好。

或者,您可以将传递给导出器的文件挂钩,以使写入执行replace('\n', ',\n')。这很hacky,所以如果你可以挂钩导出器,我不会这样做,但它确实具有简单的优点。

现在,文件开头和结尾的括号。如果不知道您正在使用的库或您使用它的方式,这将非常模糊。

如果你在每个文件中使用导出器的单个“会话” - 也就是说,你在启动时打开它,写一堆项目,然后关闭它,永远不要重新打开它并附加到它,这很简单。让我们假设您通过继承导出器类来挂钩其写入来解决第一个问题,如下所示:

class JsonArrayExporter(JsonLinesItemExporter):
    def _write_bytes(self, encoded_bytes):
        encoded_bytes = _encoded_bytes.replace(b'\n', b',\n')
        returns super()._write_bytes(encoded_bytes)

我猜测实现的样子,但你已经发现了正确的事情,所以你应该能够将我的猜测转化为现实。现在,您需要添加两个这样的方法:

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._writebytes(b'[\n')

    def close(self):
        if not self.closed():
            self._writebytes(b']\n')
        super().close()

如果导出器类中有自己的缓冲区,那么在flush之前某处可能需要_writebytes,但这是我期望看到的唯一额外复杂性。

如果您要重新打开文件并在每个会话中附加到它们,这显然不起作用。你可以__init__中执行类似伪代码的操作:

if file is empty:
    write('[\n')
else:
    seek to end of file
    if last two bytes are ']\n':
        seek back 2 bytes

这样做的好处是对您的客户端代码透明,但它有点hacky。如果您的客户端代码知道它何时打开一个新文件而不是附加到旧文件,并知道它何时完成了一个好文件,那么添加addStartMarkeraddEndMarker方法并调用它们可能更简洁,或者只是让客户端在初始化/关闭导出器之前手动将括号写入文件。