Чем ближе дата закрытия Google Reader тем более насущным становится вопрос не только переноса подписок в аналогичный сервис, но и сохранения всех текущих записей.
Найденные решения, в том числе и на хабре(здесь и здесь), не подошли в основном по двум причинам: нет возможности сохранения в БД и медленная скорость работы. Пришлось собрать свой велосипед — grbackup, который
grbackup -e fake@gmail.com -p password -ba -o mongodb://localhost:27017 -w 20
за 20 минут сохранил 328250 записей из 102 подписок в локальную БД MongoDB.
Основные возможности:
- сохранение всех записей, которые можно получить при помощи Google Reader API
- сохранение записей в различные хранилища
- использование формата идентичного тому, что отдает Google Reader через API
- параллельное получение/сохранение записей
- расширяемость: возможность добавить новый вид хранилища
Доступные виды хранилищ определяются расширениями(плагинами) и задаются при помощи опции (-o, --output) вида type:uri.
На момент написания статьи доступны следующие расширения:
- simple: вывод в терминал (используется только в режиме просмотра)
- json: запись в json-файл (json:/path/to/file.json)
- mongodb: запись в MongoDB (mongodb://[username:password@]hostN[:portN]]][/[db][?opts]])
- redis: запись в Redis (redis://username:password@host[:port]/dbindex)
Работоспособность проверена на Ubuntu(64) и Win7(64).
Книгу предложений и замечаний можно найти здесь.
Ниже находится подробное описание утилиты.
Установка
pip install grbackup
или
easy_install grbackup
или
pip install git+git://github.com/wistful/grbackup.git
Опций командной строки
Авторизация
- -e, --email: почтовый адрес используемый для входа в Google Reader
- -p, --password: пароль
grbackup поддерживает два действия:
- просмотр:-l, --list
- сохранение: -b, --backup
Действие можно выполнить над одним из четырех типов данных:
- подписки; -s, --subscriptions
- записи: -t, --topics
- отмеченные записи: -x, --starred
- все предыдущие вместе взятые: -a, --all
Дополнительные опции:
- -w, --workers: максимальное количество одновременно обрабатываемых подписок (по умолчанию 1)
- -o, --output: URI показывающий куда сохранять записи
- -n, --count: количество записей получаемых от Google Reader за один запрос (по умолчанию 200). Слишком большое число может привести к получению данных более мелкими порциями (ох уж этот google).
Все опции, а также описание плагинов можно просмотреть используя опцию -h, --help:
grbackup -h
Usage: grbackup [options] [args]
Examples:
list subscriptions: grbackup -e email@gmail.com -p password -ls
list topics: grbackup -e email@gmail.com -p password -lt http://feed.com
list starred: grbackup -e email@gmail.com -p password -lx
list all items: grbackup -e email@gmail.com -p password -la
backup subscriptions: grbackup -e email@gmail.com -p password -bs -o json:/tmp/subscriptions.json
backup topics: grbackup -e email@gmail.com -p password -bt http://myfeed.com -o json:/tmp/myfeed.json
backup starred into MongoDB: grbackup -e email@gmail.com -p password -bx -o mongodb://localhost:27017
backup all items into Redis: grbackup -e email@gmail.com -p password -ba -o redis://localhost:6379/3
Available plugins:
mongodb: save items into MongoDB
output scheme: mongodb://[username:password@]hostN[:portN]]][/[db][?opts]]
output examples: mongodb://localhost:27017
mongodb://user:pwd@localhost,localhost:27018/?replicaSet=grbackup
json: save items into file
output scheme: json:/path/to/file.json
output examples: json:/home/grbackup/grbackup.json
json:/tmp/grbackup/grbackup.json
redis: save items into Redis
output scheme: redis://username:password@host[:port]/dbindex
output examples: redis://localhost:6379/3
redis://user:password@localhost:6379/0
Options:
Auth Options:
-e EMAIL, --email=EMAIL
gmail account
-p PWD, --password=PWD
account password
Command Options:
-b, --backup backup items
-l, --list list items
Scope Options:
-a, --all processing all items
-s, --subscriptions
processing subscriptions only
-t, --topics processing topics only
-x, --starred processing starred topics only
MongoDB Options:
--mongodb-db=MONGODB_DB
the name of the database[default: greader]
--mongodb-scol=MONGODB_SUBSCRIPTIONS
collection name for subscriptions[default:
subscriptions]
--mongodb-tcol=MONGODB_TOPICS
collection name for topics[default: topics]
--mongodb-tstar=MONGODB_STARRED
collection name for starred items[default: starred]
--mongodb-w=MONGODB_W
<int> Write operations will block until they have been
replicated to the specified number [default: 1]
--mongodb-j block until write operations have been committed to
the journal [default: False]
Redis Options:
--redis-scol-prefix=REDIS_SUBS
subscriptions key prefix[default: subscription]
--redis-tcol-prefix=REDIS_TOPICS
topics key prefix[default: topic]
--redis-xcol-prefix=REDIS_STARRED
starred key prefix[default: starred]
Other Options:
-w WORKERS, --workers=WORKERS
number of workers [default: 1]
-o OUTPUT, --output=OUTPUT
output uri
-n COUNT, --count=COUNT
the number of topics that can be read at once
[default: 200]
-c CODING, --coding=CODING
output coding [default: utf8]
-v, --verbose verbose output
-h, --help
Использование
список подписок:
grbackup -e email@gmail.com -p password -ls
список записей конкретной подписки:
grbackup -e email@gmail.com -p password -lt http://habrahabr.ru/rss/hub/python/
список всех отмеченных записей:
grbackup -e email@gmail.com -p password -lx
список всех записей:
grbackup -e email@gmail.com -p password -la
сохранение подписок в json-файл:
grbackup -e email@gmail.com -p password -bs -o json:/tmp/subscriptions.json
сохранение всех записей конкретной подписки в json-файл:
grbackup -e email@gmail.com -p password -bt http://habrahabr.ru/rss/hub/python/ -o json:/tmp/python.json
сохранение всех отмеченных записей в MongoDB:
grbackup -e email@gmail.com -p password -bx -o mongodb://localhost:27017
сохранение всех записей в Redis с использованием 20 потоков:
grbackup -e email@gmail.com -p password -ba -o redis://localhost:6379/3 -w 20
Плагины
JSON
Общий формат URI: json:/path/to/file.json
Поддержка многопоточности: да
Пример использования:
grbackup -e email@gmail.com -p password -ba -o json:/home/grbackup/grbackup.json
Записи сохраняются в отдельный файл в виде списка объектов.
Существует три типа объектов:
- подписки: {«type»: «subscription», «value»: subscription}
- отмеченные записи: {«type»: «starred», «value»: record}
- обычная запись: {«type»: «topic», «subscription»: subscription_url, «value»: record}
MongoDB
Общий формат URI: mongodb://[username:password@]hostN[:portN]]][/[db][?opts]]
Поддержка многопоточности: да
Пример использования:
grbackup -e email@gmail.com -p password -ba -o mongodb://localhost:27017 -w 20
Записи раскладываются по трем коллекциям: subscriptions, topics, starred.
Имена коллекций можно изменять.
Redis
Общий формат URI: redis://username:password@host[:port]/dbindex
Поддержка многопоточности: да
Пример использования:
grbackup -e email@gmail.com -p password -ba -o redis://user:password@localhost:6379/0 -w 20
Для хранения записей используется тип данных Hashes.
Ключи могут быть трех типов: «subscription:record_id», «starred:record_id», «topic:record_id», где record_id — уникальный идентификатор записи.
Префиксы ключей можно изменять.
Свой плагин
Модуль должен находится в пакете grb_plugins, имя модуля не имеет значения.
Структура модуля:
- plugin_type — строковый тип, содержит имя плагина
- support_threads — логический тип, указывает на возможность использования нескольких потоков
- descriptions — строковый тип, содержит описание плагина
- add_option_group — функция добавляющая дополнительные опции комадной строки
- writer — контекстный менеджер возвращающий объект с методами:
- put_subscription(subscription) — вызывается при сохранение подписки, subscription — словарь
- put_starred(topic) — вызывается при сохранении отмеченной записи, topic — словарь
- put_topic(self, subscription_url, topic) — вызывается при сохранении записи (опция '-t', '--topics'),
topic — словарь, subscription_url — строковый адрес подписки - put_all(subscription, topic) — вызывается при сохранении записи с использованием опции '-a', '--all',
subscription и topic — словари
#!/usr/bin/env python
# coding=utf-8
from optparse import OptionGroup
import logging
plugin_type = "myplugin"
support_threads = True
description = """save items using logging
output scheme: myplugin:/path/to/logfile.log
output examples: myplugin:/tmp/storage.log
"""
def add_option_group(parser):
# Plugin Options
myplugin_group = OptionGroup(parser, "myplugin Options")
myplugin_group.add_option("--myplugin-format",
dest="format",
type="str",
default="%(asctime)s %(message)s",
help="record format"
"[default: %default]")
myplugin_group.add_option("--myplugin-datefmt",
dest="datefmt",
type="str",
default="%m/%d/%Y %I:%M:%S %p",
help="date format"
"[default: %default]")
parser.add_option_group(myplugin_group)
class WriteMyPlugin(object):
def __init__(self, logger):
self.logger = logger
def put_subscription(self, subscription):
subscription_url = subscription['id'][5:]
self.logger.warning("write subscription: %s", subscription_url)
def put_all(self, subscription, topic):
subscription_url = subscription['id'][5:]
self.put_subscription(subscription)
self.put_topic(subscription_url, topic)
def put_starred(self, topic):
self.logger.warning("write starred: %s", topic.get('title', ''))
def put_topic(self, subscription_url, topic):
self.logger.warning("write topic: %s %s",
subscription_url,
topic.get('title', ''))
class writer(object):
def __init__(self, opt):
path = opt.output[opt.output.index(":") + 1:]
self._logger = logging.getLogger("myplugin")
handler = logging.FileHandler(path)
handler.setFormatter(logging.Formatter(opt.format, opt.datefmt))
self._logger.addHandler(handler)
def __enter__(self):
return WriteMyPlugin(self._logger)
def __exit__(self, *exc_info):
pass
Автор: wistful