Как стать автором
Поиск
Написать публикацию
Обновить

Сортируем статьи из избранного

Статистика избранного

У меня в избранном находится достаточно много постов (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.

Кратко:
  1. Устанавливаете дополнительные библиотеки для работы Scrapy
  2. $ pip install habra_favorites
  3. $ habra_favorites <habra_username> # имя пользователя (не обязательно своё)
  4. $ firefox favorites.html # google-chrome favorites.html
  5. # Hint: на названия столбцов можно нажимать 2 раза.

Подробно:
GitHub

Замечания


  • Пример моего файла.
  • У пользователя Levsha100 почти 2000 постов в избранном. Программа работает примерно 1,5 минуты.
  • Что-то подобное уже было, да. Теперь будет еще одно.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.