Рейтинг постов хаба

в 18:35, , рубрики: grab, python, spider, sqlite3, Хабрахабр API, метки: , , ,

Рейтинг постов хаба
Привет!

Решил посмотреть лучшие посты своего любимого хаба и с ужасом обнаружил, что такой фичи нет.
Когда это нас останавливало!

Прошу под кат всех желающих посмотреть на самые рейтинговые посты для каждого хаба + пару слов о реализации скрипта.

Скрипт написан на питоне при помощи отличной библиотеки grab, а точнее её модуля Spider. Спасибо автору(и по совместительству хаброюзеру) itforge за подробную документации.
В качестве БД использовалась sqlite3. Парсились только страницы хаба, т.е. в сам пост паук не залазил, что отразилось на скорости скрипта: получена информация о 111690 постах менее чем за час, и это в 1 поток.

from grab.spider import Spider, Task
import sqlite3 as lite

class HabraParser(Spider):
    # отсюда начинаем парсить
    initial_urls = ['http://habrahabr.ru/hubs/']

    def prepare(self):
        # сюда будем складывать
        self.post = []
        self.con = lite.connect('files/habra_hubs.db')

    def task_initial(self, grab, task):
        # стрелка, указывающая на следующую страницу списка хабов
        nav = grab.doc.select('//ul[@class="next-prev"]/li/a[@class="next"]')
        # парсим хабы и передаём ссылки в новое задание hub
        for elem in grab.doc.select('//div[@class="info"]/div[@class="stat"]/a[2]'):
            self.add_task(Task(name='hub', url=elem.attr('href')))
        # если стрелка существует - переходим на следующую страницу
        if nav.exists():
            self.add_task(Task(name='initial', url=nav.attr('href')))

    def task_hub(self, grab, task):
        nav = grab.doc.select('//a[@class="next" and @id="next_page"]')
        # парсим пост
        for elem in grab.doc.select('//div[@class="post shortcuts_item"]'):
            comments = ''
            score = ''
            post_url = elem.node.find('h1[@class="title"]/a').get('href')
            post_title = elem.node.find('h1[@class="title"]/a').text
            # Страховка от факапов
            try:
                comments = int(elem.node.find('.//span[@class="all"]').text)
            except:
                comments = 0

            try:
                score = int(elem.node.find('.//span[@class="score"]').text)
            except:
                score = 0

            self.post.append([
                score,
                comments,
                post_url,
                post_title
            ])

        if nav.exists():
            self.add_task(Task(name='hub', url=nav.attr('href')))
        else:
            # сохраняем в бд
            hub = task.url.split('/')[4] # название для таблицы
            self.save_data(hub) 

    def save_data(self, hub):
        with self.con:
            self.cur = self.con.cursor()
            self.cur.execute("DROP TABLE IF EXISTS %s"%hub)
            self.cur.execute("CREATE TABLE %s(Score INT, Comments INT, Url TEXT, PostTitle TEXT)"%hub)

            self.cur.executemany("INSERT INTO %s VALUES(?, ?, ?, ?)"%hub, self.post)

        self.post = []

Дальше остаётся только задавать вопросы бд вида:

("SELECT * FROM %s ORDER BY Score DESC LIMIT 10" % hub)

Для этого был тоже написан простенький скриптик, который и сгенерировал большую часть этой статьи.
Первоначально была идея поместить ещё самые комментируемые посты(и для этого всё реализовано), но уж слишком толстой получается эта статья!

Хабы брались из условия:

habraindex > 100.0 and posts_number > 200

Бд, исходники и проч на гитхабе.

Ну а теперь самое интересное. Я думаю каждый найдёт себе что-нибудь по вкусу.

Автор: ErhoSen

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js