Python CouchDB не может сохранить dict, созданный из записи feedparser? (без атрибута «чтение»)

У меня есть сценарий, в котором я хочу читать записи в RSS-канале и сохранять отдельные записи в формате JSON в базе данных CouchDB.

Интересная часть моего кода выглядит примерно так:

Feed = namedtuple('Feed', ['name', 'url'])

couch = couchdb.Server(COUCH_HOST)
couch.resource.credentials = (COUCH_USER, COUCH_PASS)

db = couch['raw_entries']

for feed in map(Feed._make, csv.reader(open("feeds.csv", "rb"))):
    d = feedparser.parse(feed.url)
    for item in d.entries:
        db.save(item)

Когда я пытаюсь запустить этот код, я получаю следующую ошибку от db.save(item):

AttributeError: object has no attribute 'read'

Итак, я немного отладил...

for feed in map(Feed._make, csv.reader(open("feeds.csv", "rb"))):
    d = feedparser.parse(feed.url)
    for item in d.entries:
        print(type(item))

приводит к <class 'feedparser.FeedParserDict'> -- ааа, значит, feedparser использует свой собственный тип dict... ну, а что, если я попытаюсь явно привести его к dict?

for feed in map(Feed._make, csv.reader(open("feeds.csv", "rb"))):
    d = feedparser.parse(feed.url)
    for item in d.entries:
        db.save(dict(item))

Traceback (most recent call last):
  File "./feedchomper.py", line 32, in <module>
    db.save(dict(item))
  File "/home/dealpref/lib/python2.7/couchdb/client.py", line 407, in save
_, _, data = func(body=doc, **options)
  File "/home/dealpref/lib/python2.7/couchdb/http.py", line 399, in post_json
status, headers, data = self.post(*a, **k)
  File "/home/dealpref/lib/python2.7/couchdb/http.py", line 381, in post
**params)
  File "/home/dealpref/lib/python2.7/couchdb/http.py", line 419, in _request
credentials=self.credentials)
  File "/home/dealpref/lib/python2.7/couchdb/http.py", line 239, in request
    resp = _try_request_with_retries(iter(self.retry_delays))
  File "/home/dealpref/lib/python2.7/couchdb/http.py", line 196, in _try_request_with_retries
    return _try_request()
  File "/home/dealpref/lib/python2.7/couchdb/http.py", line 222, in _try_request
    chunk = body.read(CHUNK_SIZE)
AttributeError: 'dict' object has no attribute 'read'

ч-что? Это не имеет смысла, потому что следующее работает отлично, а тип по-прежнему dict:

some_dict = dict({'foo': 'bar'})
print(type(some_dict))
db.save(some_dict)

Что мне здесь не хватает?


person ashgromnies    schedule 31.03.2011    source источник
comment
Можете ли вы опубликовать трассировку стека для этих ошибок? Возможно, ошибка где-то глубже в модуле CouchDB. Это правда, что у объектов dict нет метода read(), но это может быть отвлекающим маневром.   -  person kindall    schedule 01.04.2011
comment
@kindall - я опубликовал всю трассировку стека ... похоже, что CouchDB по какой-то причине пытается выполнить загрузку по частям (может быть, потому, что словарь большой)? Однако я не могу воспроизвести поведение, создав dict вручную (то есть он отлично сохраняется, если я пишу его вручную...).   -  person ashgromnies    schedule 01.04.2011
comment
Да, по какой-то причине кажется, что ваш dict - это файл. Очень странный.   -  person kindall    schedule 01.04.2011
comment
Очень любопытно, @kindall - пошагово я вижу, что для сохранения типа db.save({'a': 'b'}) он выполняет простой conn.sock.sendall(body) без разбиения на куски.   -  person ashgromnies    schedule 01.04.2011


Ответы (3)


Я нашел способ, сериализовав структуру в JSON, а затем обратно в словарь Python, который я передаю в CouchDB, который затем повторно сериализует его обратно в JSON для сохранения (да, странно и не выгодно, но это работает?)

Мне пришлось использовать собственный метод сериализации для дампов, потому что repr из time_struct не может быть eval.

Источник: http://diveintopython3.org/serializing.html

Код:

#!/usr/bin/env python2.7

from collections import namedtuple
import csv
import json
import time

import feedparser
import couchdb

def to_json(python_object):
    if isinstance(python_object, time.struct_time):
        return {'__class__': 'time.asctime',
                '__value__': time.asctime(python_object)}

    raise TypeError(repr(python_object) + ' is not JSON serializable')

Feed = namedtuple('Feed', ['name', 'url'])

COUCH_HOST = 'http://mycouch.com'
COUCH_USER = 'user'
COUCH_PASS = 'pass'

couch = couchdb.Server(COUCH_HOST)
couch.resource.credentials = (COUCH_USER, COUCH_PASS)

db = couch['raw_entries']

for feed in map(Feed._make, csv.reader(open("feeds.csv", "rb"))):
    d = feedparser.parse(feed.url)
    for item in d.entries:
        j = json.dumps(item, default=to_json)
        db.save(json.loads(j))
person ashgromnies    schedule 31.03.2011

Ответил в списке рассылки, но в основном это происходит потому, что запись feedbparser содержит данные, которые не могут быть без потерь сериализованы в JSON, например. экземпляры time.struct_time. К сожалению, Couchdb-python затем предполагает, что это файл, маскируя реальную ошибку.

person Matt Goodall    schedule 01.04.2011

Возможно в Python CouchDB есть ошибка. Можно сказать, что он недостаточно либерален в том, что он принимает.

Но, в основном, CouchDB хранит JSON. Вы должны работать с любым «JSON» на вашем языке. Очевидно, что с Python это означает dict объектов.

Вы можете получить лучший результат, если выясните, как преобразовать все ваши типы в простой словарь Python, прежде чем вызывать CouchDB. Возможно, это не самое "правильное" решение, но подозреваю, что самое быстрое.

Мой питон заржавел. Возможно ли, что dict(foo) когда-нибудь вернет не-дикт? Может быть, FeedParserDict создает подклассы dict, а затем использует метапрограммирование для возврата самого себя при вызове dict()? Можете ли вы подтвердить, что type(dict(item)) определенно является простым словарем Python?

Обычный трюк в мире Javascript — циклический обход сериализатора, такого как JSON. Что-то вроде pickle.loads(pickle.dumps(item)). Это в значительной степени гарантирует, что у вас будет простая копия основных данных.

person JasonSmith    schedule 01.04.2011
comment
Сообщите мне о результате type(dict(item)). Если моя гипотеза неверна, может быть, что-то еще прозвенит. Кстати отличный вопрос! - person JasonSmith; 01.04.2011