Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Знаменитость тегов Програ: какие тренды в постах?

Anna | 16.06.2014 | нет комментариев
Доброго времени суток!

Сегодня речь пойдёт о том, как дозволено испробовать проследить склонности. Глядя на то, как это делаетgoogle возникло желание сделать сходственные тренды на основе тегов Програ. Допустимо, не все пользователи добросовестно расставляют теги, но допустив это как истину, дозволено получить недурные итоги. Выходит, давайте испробуем.

1. Изложение

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

Сразу оговорюсь, что дальше по тексту встречается слово «тег» — это тоже самое что и тэг, таг, метка. Писать будем под python 3.3.2, так как в нём у меня не появлялось задач с юникодом.

2. Конструкция и подготовка

Для этой задачи нам нужна база данных. Она будет содержать 2 таблицы post_tags и tags. Поля первой pid — ид поста, tid — ид тега, date — дата поста с тегом. Поля 2-й rowid — ид тега, tag_title — заголовок тега. Здесь всё предельно легко, следственно сотворим класс для работы с базой.

import sqlite3

class Base:
    def __init__(self,dbname):
        self.con=sqlite3.connect(dbname)

    def __del__(self):
        self.con.close( )

    def maketables(self):
        """
        Для создания таблицы
        """
        self.con.execute('create table post_tags(pid,tid,date)')
        self.con.execute('create table tags(tag_title)')
        self.con.commit( )

Так как всё сохранено в файл tags.py, испробуем исполнить:

import tags
extend = tags.Base('tags.db')
extend.maketables()

Сейчас у нас есть пустая база, пойдёмте заполним её.

3. Парсинг тегов

Нужно собрать все теги и записать коллективно с датой публикации поста. Для этого используем beautifulsoupи urllib.request.

Добавим в сделанный класс функции:

  • get_tag — для приобретения тега из базы
  • add_tag — для добавления тега
  • add_post — для просмотра и добавления поста
Код функций для парсинга

    def get_tag(self, name, added = True):
        """
        Поиск тега, если есть воротить tid, напротив сделать новейший
        cur - нынешний тег
        res - будет получен если тег теснее в базе
        added - Флаг/ если установлен то будет дописывать, если нет то возвращать false
        """
        cur=self.con.execute("select rowid from %s where %s='%s'" % ('tags','tag_title',name))
        res=cur.fetchone( )
        if res==None:
            if added:
                cur=self.con.execute("insert into %s (%s) values ('%s')" % ('tags','tag_title',name))
                self.con.commit( )
                return cur.lastrowid
            else:
                return False
        else:
            return res[0]

    def add_tag(self, pid, name, date):
        """
        Добавление нового тега
        pid - ид поста
        name - сам тег
        date - дата поста
        """
        rowid = self.get_tag(name);
        print(pid,rowid ,name, date)
        self.con.execute("insert into %s (%s,%s,%s) values (%d,%d,'%s')" % ('post_tags','pid','tid','date',pid,rowid,date))
        self.con.commit( )

    def add_post(self, pid):
        """
        Просмотр поста (выборка даты и тегов)
        """
        if (self.get_post(pid)):
            return

        print('-'*10,'http://habrahabr.ru/post/' str(pid),'-'*10)
        cur=self.con.execute("select pid from %s where %s=%d" % ('post_tags','pid',pid))
        res=cur.fetchone( )
        if res==None:
            try:
                soup=BeautifulSoup(urllib.request.urlopen('http://habrahabr.ru/post/' str(pid)).read( ))
            except (urllib.request.HTTPError):
                self.add_tag(pid,"parse_error_404","")
                print("error 404")
            else:
                published = soup.find("div", { "class" : "published" })
                tags = soup.find("ul", { "class" : "tags" })
                if tags:
                    for tag in tags.findAll("a"):
                        self.add_tag(pid, tag.string, get_date(published.string))
                else:
                    self.add_tag(pid,"parse_access_denied","")
                    print("access denied")
        else:
            print("post has already")

Дабы наполнить базу довольно запустить цикл по каждому постам. Ограничимся 196000 он попадает на 1 октября 2013.

for pid in range(196000):
    extend.add_post(pid 1)

Знаю, что метод не совершенный и с мегабитным интернетом работал бы около 170 часов.

Для убыстрения процесса было решено добавить ещё одну таблицу post, в которой будет храниться обособленный ид поста, не связанный с логической частью, и применять его как флаг. Так огромную часть времени программа «висит». В ожидании загрузки страницы дозволено запустить ещё пару таких программ и натравив на одну базу заполнять её параллельно. Происхождение коллизий безусловно допустимо и в конце концов потеря либо частичная запись составила 3% постов, что не так много. Позже таких действий выяснилось, что параллельно не может трудиться огромнее 8 программ, т.к. при запуске 9 Прогр отдает 503 ошибку, что абсолютно разумно. Следственно, запустив 6 экземпляров (именно такое число не вызывало ошибок и не конфликтовало между собой), была получена база(17 Mb).

4. Обработка данных

Собственно, сейчас основная часть — обработать полученные данные.
Добавим в сделанный класс функции:

  • get_count_byid — число по ид тега
  • get_graph — получить список кортежей (дата, число)
  • get_image — показать изображение
  • get_all_tags_sorted — сортировать по убыванию
  • get_all_tag_count — получить список кортежей (ид, тег, число)
Код функций для обработки данных

    def get_count_byname(self, name, date = ''):
        """
        Обнаружить name по tid и получить число по имени из get_count_byid
        name - имя тега
        date - (*)дата Формат mm_yyyy
        """
        tid = self.get_tag(name, False)
        return self.get_count_byid(tid, date)

    def get_count_byid(self, tid, date = ''):
        """
        Воротить число за каждый период либо за указанную дату
        """
        if tid:
            if date:
                count=self.con.execute("select count(pid) from %s where %s=%d and %s='%s'" % ('post_tags','tid',tid,'date',date))
            else:
                count=self.con.execute("select count(pid) from %s where %s=%d" % ('post_tags','tid',tid))
            res=count.fetchone( )
            return res[0]
        else:
            return False

    def get_graph(self, name):
        """
        Образование списка дата - число
        """
        month = ('01','января'),('02','февраля'),('03','марта'),('04','апреля'),('05','мая'),('06','июня'),('07','июля'),('08','августа'),('09','сентября'),('10','октября'),('11','ноября'),('12','декабря')
        years = [2006,2007,2008,2009,2010,2011,2012,2013]

        graph = []
        for Y in years:
            for M,M_str in month:
                date = str(M) '_' str(Y)
                graph.append((date, self.get_count_byname(name, date)))
        return graph

    def get_image(self, name):
        """
        Построение графика
        m_x - масштаб по X
        m_y - масштаб по Y
        img_x - ширина рисунка
        img_y - высота рисунка
        """
        img_x = 960
        img_y = 600

        img=Image.new('RGB',(img_x,img_y),(255,255,255))
        draw=ImageDraw.Draw(img)

        graph = self.get_graph(name)
        max_y = max(graph,key=lambda item:item[1])[1]
        if max_y == 0:
            print('tag not found')
            return False
        m_x, m_y = int(img_x/(len(graph))), int(img_y/max_y)

        draw.text((10, 10), str(max_y), (0,0,0))
        draw.text((10, 20), name, (0,0,0))

        x,prev_y = 0,-1
for x_str, y in graph:
            x  = 1
            if (x%12 == 1): draw.text((x*m_x, img_y - 30), str(x_str[3:]),(0,0,0))
            if prev_y >= 0: draw.line(((x-1)*m_x, img_y-prev_y*m_y-1, x*m_x, img_y-y*m_y-1), fill=(255,0,0))
            prev_y = y

        img.save('graph.png','PNG')
        Image.open('graph.png').show()

    def get_all_tags_sorted(self, tags):
        """
        По убыванию
        """
        return sorted(tags, key=lambda tag:tag[2], reverse=True)

    def get_all_tag_count(self):
        count=self.con.execute("select count(rowid) from %s" % ('tags'))
        res=count.fetchone( )
        alltag_count = res[0]
        tags = []
        for tag_id in range(alltag_count-1):
            tags.append((tag_id 1,self.get_tag_name(tag_id 1),self.get_count_byid(tag_id 1)))
            print (tag_id 1,self.get_tag_name(tag_id 1),self.get_count_byid(tag_id 1))
        return tags

5. Вывод (для любителей статистики)

Top 25 «тегов лидеров»:

  1. google — 2611
  2. android — 2188
  3. Google — 2024
  4. linux — 1978
  5. php — 1947
  6. javascript — 1877
  7. microsoft — 1801
  8. apple — 1668
  9. общественные сети — 1509
  10. стартапы — 1484
  11. стартап — 1317
  12. программирование — 1229
  13. Microsoft — 1220
  14. java — 1180
  15. игры — 1141
  16. Apple — 1135
  17. iphone — 1122
  18. дизайн — 1110
  19. python — 1108
  20. юмор — 1061
  21. интернет — 1040
  22. програпрогр — 983
  23. видео — 970
  24. реклама — 968
  25. Android — 876

Полный список дозволено посмотреть здесь (формат id, title_tag, число упоминаний).

Сейчас, собственно, возвращаемся к мысли о трендах. Ниже приведены пару примеров (кликабельно):

python javascript android

Получить такие изображения дозволено из функции get_image(tag_title).

Я уверен, что есть ещё лучшие способы приобретения сходственного итога, но «я не кудесник, а только учусь». Умоляю всех в комментарии для обсуждений и совершенствований.
В тезисе, моя цель была достигнута. Правда теснее возникают мысли о добавлении автономности регистра и морфологии, а также вероятности передавать сразу список тегов.

Полный код выложен на github.

Каждому спасибо за внимание, буду рад критике.

Источник: programmingmaster.ru
Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB