
У меня в избранном находится достаточно много постов (300+). Многие из них я просто добавил, чтобы прочитать позже. Но в этой статье я бы не хотел рассуждать на тему, почему так произошло и как с этим бороться, а хотел бы рассказать о том, как Python может помочь выбрать статью для просмотра или удаления.
Все просто. Найдем самые лучшие/худшие статьи. Критериями могут быть, например, рейтинг или общее количество добавлений в избранное.
Если Вы не хотите читать статью, а хотите сразу получить файл, как на картинке, переходите к разделу «Установка и использование».
Scrapy
Для начала, соскрапим себе список всех постов из избранного. Для скрапинга я выбрал Scrapy. На Хабре уже есть вводная статья, да и документация очень полная, но я позволю себе немного повториться и дополнить некоторые моменты.
Мне очень нравится в Scrapy его структурированность. Скелет проекта очень похож на структуру Django. Поэтому при описании будут возникать некоторые сопоставления.
Item
Первым делом объявим класс Item. Item — это описание элементов, которые мы будет сохранять. Если говорить в терминах Django, то это Model, не связанная с БД (хотя это можно добавить).
Вернемся к критериям сортировки. Это как раз и будут поля нашего Item — числовые данные, которые мы можем получить у поста. Не забудем название с адресом:
class FavoriteItem(Item):
id_ = Field()
ref = Field()
title = Field()
# Критерии
rating = Field() # рейтинг
rating_all = Field() # кол-во всех голосов
rating_up = Field() # кол-во голосов "за"
rating_down = Field() # кол-во голосов "против"
views = Field() # кол-во просмотров
count = Field() # кол-во добавлений
comments = Field() # кол-во комментариев
Loader
Для упрощения обработки элементов существуют Loader'ы. Loader применяет функции для обработки данных и создает Item. Напоминает обработку форм в Django. Где-то просто приводим к числу, где-то пишем что-то своё:
class FavoriteItemLoader(ItemLoader):
default_item_class = FavoriteItem
default_output_processor = TakeFirst()
id__in = MapCompose(process_id_)
rating_in = MapCompose(process_rating)
rating_all_in = MapCompose(process_rating_all(1))
rating_up_in = MapCompose(process_rating_all(2))
rating_down_in = MapCompose(process_rating_all(3))
views_in = MapCompose(int)
count_in = MapCompose(int)
comments_in = MapCompose(process_comments)
Spider
Переходим к самому пауку. Нужно сохранить посты с текущей странички и перейти на следующую, если она есть. Немного упрощенный код:
def parse(self, response):
sel = Selector(response)
next_urls = sel.xpath('//a[@id="next_page"]/@href').extract()
for url in next_urls:
yield Request(self.base_url + url, self.parse)
posts = sel.xpath('//div[contains(concat(" ", @class, " "), " post ")]')
for post in posts:
l = FavoriteItemLoader(selector=post)
l.add_xpath('id_', '@id')
l.add_xpath('ref', './/a[@class="post_title"]/@href')
l.add_xpath('title', './/a[@class="post_title"]/text()')
l.add_xpath('rating', './/span[@class="score"]/text()')
l.add_xpath('rating_all', './/span[@class="score"]/@title')
l.add_xpath('rating_up', './/span[@class="score"]/@title')
l.add_xpath('rating_down', './/span[@class="score"]/@title')
l.add_xpath('views', './/div[@class="pageviews"]/text()')
l.add_xpath('count', './/div[@class="favs_count"]/text()')
l.add_xpath('comments', './/div[@class="comments"]//span[@class="all"]/text()')
yield l.load_item()
Pipeline
Есть еще уровень обработки — Pipeline'ы. Сюда поступают уже созданные Item'ы. В принципе, делать можно что угодно. Именно здесь обычно происходит сохранение в базу и проверка полей. Последним вариантом и воспользуемся: если у поста нет рейтинга — выставим None.
class FavoriteItemPipeline(object):
RATING_FIELDS = ['rating', 'rating_all', 'rating_up', 'rating_down']
def process_item(self, item, spider):
for field in self.RATING_FIELDS:
if field not in item:
item[field] = None
return item
Exporter
Теперь нужно все данные собрать в файл. В Scrapy по умолчанию есть возможность сохранения данных в JSON, XML и СSV, которая реализуется с помощью классов Exporter. Но я решил, что HTML страничка, где видно все посты со ссылками и можно все отсортировать, лишь кликнув мышкой, будет более удобным вариантом. Для этого был написан новый Exporter.
Установка и использование
Исходный код на GitHub. Проект доступен для установки с PyPI.
Кратко:
- Устанавливаете дополнительные библиотеки для работы Scrapy
$ pip install habra_favorites
$ habra_favorites <habra_username> # имя пользователя (не обязательно своё)
$ firefox favorites.html # google-chrome favorites.html
- # Hint: на названия столбцов можно нажимать 2 раза.
Подробно:
GitHub