Как прочитать строки из json-файла в scrapy

У меня есть файл json, в котором хранится некоторая информация о пользователе, включая id, name и url. JSON-файл выглядит так:

{"link": "https://www.example.com/user1", "id": 1, "name": "user1"}
{"link": "https://www.example.com/user1", "id": 2, "name": "user2"}

Этот файл был написан скрап-пауком. Теперь я хочу прочитать URL-адреса из файла json и очистить веб-страницу каждого пользователя. Но я не могу загрузить данные из файла json.

В настоящее время я понятия не имею, как получить эти URL-адреса. Я думаю, что я должен сначала прочитать строки из файла json. Я попробовал следующий код в оболочке Python:

import json    
f = open('links.jl')    
line = json.load(f)

Я получил следующее сообщение об ошибке:

Raise ValueError(errmsg("Extra data", s, end, len(s)))
ValueError: Extra data: line 2 column 1- line 138 column 497(char498-67908)

Я сделал несколько поисков в Интернете. Поиск показал, что файл json может иметь некоторые проблемы с форматированием. Но файл json был создан и заполнен элементами с использованием конвейера scrapy. Кто-нибудь знает, что вызвало ошибку? И как это решить? Любые предложения по чтению URL-адресов?

Большое спасибо.


person Olivia    schedule 24.12.2012    source источник
comment
Вы уверены, что между каждым объектом json есть пустая строка? можете ли вы подтвердить, вместе со скрап-версией.   -  person Shane Evans    schedule 24.12.2012


Ответы (5)


Это строки json, как следует из названия экспортера.

Загляните в scrapy.contrib.exporter и увидите разницу между JsonItemExporter и JsonLinesItemExporter

Это должно помочь:

import json

lines = []

with open('links.jl', 'r') as f:
    for line in f:
        lines.append(json.loads(line))
person marius_5    schedule 16.04.2013

Хм... это исключение интересно... Я просто... оставлю это здесь (без гарантии и без совести).

import json
import re

parse_err = re.compile(
    r'Extra data: line \d+ column \d+'
    r' - line \d+ column \d+'
    r' \(char (\d*).*')

def recover_bad_json(data):
    while data:
        try:
            yield json.loads(data)
            return
        except ValueError, e:
            char = parse_err.match(e.args[0]).group(1)
            maybe_data, data = data[:int(char)], data[int(char):]
            yield json.loads(maybe_data)

CORPUS = r'''{"link": "https://www.domain.com/user1", "id": 1, "name": "user1"}

{"link": "https://www.domain.com/user1", "id": 2, "name": "user2"}
'''

gen_recovered = recover_bad_json(CORPUS)

print gen_recovered.next()
print gen_recovered.next()
print gen_recovered.next()
person SingleNegationElimination    schedule 24.12.2012

Если вы подозреваете, что документ JSON может иметь неверный формат, я рекомендую отправить его на JSONLint. Инструмент улучшит форматирование документа и выделит любые проблемы со структурой или стилем, возникшие во время синтаксического анализа. Я использовал этот инструмент в прошлом, чтобы найти лишние запятые и неработающие кавычки в генераторах документов JSON.

person Willi Ballenthin    schedule 24.12.2012
comment
эм... приведенный пример явно искажен: json.loads('{}{}') выдает аналогичную ошибку. - person SingleNegationElimination; 24.12.2012

Я уже встречал такое форматирование с плохо спроектированными JSON API. Возможно, это не лучшее решение, но это небольшая функция, которую я использую для преобразования такого рода вывода в словарь, содержащий все объекты результатов в списке.

def json_parse(data):
    d = data.strip().replace("\n\n", ",")
    d = '{"result":[' + d + ']}'
    return json.loads(d)

Возможно, вам придется немного повозиться с этим в зависимости от количества разделяющих их новых строк и тому подобного. Прочитайте файл с помощью .read() и вызовите json_parse для данных, и вы сможете перебрать все, обратившись к data["results"].

Было бы лучше, если бы ваши результаты парсинга предоставляли действительный JSON, но в то же время что-то вроде этого может работать.

person Anorov    schedule 24.12.2012

Насколько я знаю, файл JSON должен содержать один объект. В вашем случае у вас есть несколько:

{"link": "https://www.domain.com/user1", "id": 1, "name": "user1"}

{"link": "https://www.domain.com/user1", "id": 2, "name": "user2"}

Я бы сделал что-то вроде:

Python 2.7.3 (default, Sep 26 2012, 21:51:14) 
>>> import json
>>> inpt_json = """{"link": "https://www.domain.com/user1", "id": 1, "name": "user1"}
...     
...     {"link": "https://www.domain.com/user1", "id": 2, "name": "user2"}"""

>>> for line in inpt_json.splitlines():
...     line = line.strip()
...     if line:
...             print json.loads(line)
... 
{u'link': u'https://www.domain.com/user1', u'id': 1, u'name': u'user1'}
{u'link': u'https://www.domain.com/user1', u'id': 2, u'name': u'user2'}
>>> 

Таким образом, сказать: «У меня есть файл json, в котором хранится некоторая информация о пользователе…», — неправильно. Scrapy сохраняет вывод как «файл с закодированными строками json».

person warvariuc    schedule 24.12.2012