0

最近,我不得不抓取大量数据,并从使用提要格式“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
        }
4

1 回答 1

2

首先,逗号。

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

如果适当的方法没有以您可以覆盖它的方式公开,super它并添加逗号,您可能必须在子类中重新实现该方法,甚至对导出器类进行猴子补丁。不太好看。

或者,您可以将传递给导出器的文件挂钩以使写入执行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方法并调用它们可能会更干净,或者只是让客户端手动编写括号在初始化之前/关闭导出器之后到文件。

于 2018-03-20T20:28:50.110 回答