Скрипт накрутки рейтинга (голосов)

Скрипт накрутки рейтинга (голосов)

Продолжим написание постов по использованию Python и UrlLib. Может, плохо рассказывать о таких вещах как накрутке, но это весьма интересно. Опять же не забуду напомнить, что всё это исключительно в учёбных целях!

Что я рассмотрю в этой статье:

Авторизация на сайте через UrlLib

Работа с Cookies в UrlLib

Составление POST-запросов

Разбор html через Beautiful Soup

Загрязнение совести

Вступление

Все тесты я буду проводить на хорошем форуме http://kubuntu. ru. Честно признаться, я действительно раньше писал такой скрипт, использовал его временно, сожалею, что использовал, но не поленюсь описать проделанную работу.

К любому комментарию и посту там существует система оценок: можно нажать + и -. К моему облегчению, не было абсолютно никаких ограничений по времени. Первая сложность — адреса тем на форуме, id не возрастали, а были сгенерированы случайно (зависит от удалённых тем, повышенных тем и т. д.), поэтому простым перебором с генерацией адреса тут не обойтись, нужно парсить страницы форумов на адреса.

Авторизация

Уверен, эта тема интересует многих. Сегодня я расскажу базовые вещи, чтобы вы смогли авторизироваться на любом более-менее стандартном сайте. Для работы я использую Firefox+Firebug. Второе — инструменты вебмастера. Я это называю mini-IDE for web.

Что же такое авторизация, как она происходит? Всё достаточно просто, на странице есть элементы формы. После их заполнения формируется post-запрос, который отправляется какому-то скрипту. Там уже проверяется правильность введённых данных и, если нужно, назначается, например, cookie авторизации. А теперь к практике: откройте адрес http://kubuntu. ru, активируйте Firebug. Справа можете увидеть форму авторизации. Заполните её и отправьте запрос. Чтобы просмотреть, что же было действительно сделано, переключитесь на вкладку Net в Firebug.

Для подробной информации нужно выбрать пункт в списке, вкладка Post. Здесь вы сможете найти всю информацию по отправленному запросу, именно её нам нужно будет вручную повторить в скрипте.

Недавно мне знакомый вопрос задавал: “А какие поля брать для post-запроса, ведь в Firebug мы их видим много”. Конечно, здесь нет определённого ответа. Часто в формах используют скрытые (hidden) поля для передачи каких-то сгенерированных ключей. Без них авторизация может просто не сработать. Я делаю так: сразу беру исключительно логин/пароль, если ничего не работает, добавляю по одному остальные поля. Практичный код по авторизации можно прочитать ниже.

Работа с cookies в UrlLib

Cookies существуют для хранения какой-то временной информации. Их часто используют для хранения сессии авторизации, поэтому мы должны уметь использовать всё это, для поддерживания корректной работы.

В Python нужно подключить, как минимум, 2 библиотеки, чтобы работать с cookies: import urllib, cookielib

Как же верно использовать все классы из cookielib, ведь на сайте с документацией их так много? Идея довольно простая: мы создаём обобщённый контейнер, который будет хранить все cookies, полученный от страниц. Все последующие мы должны вызывать через этот контейнер.

CookieStore = cookielib. CookieJar() cookieOpener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookieStore)) somePageData = cookieOpener. open(‘httр://sоmesitе. cоm’).read()

Кодом выше мы создаём хранилище для cookies, handle-объект, который будет работать с хранилищем и открывать другие страницы. Последняя строка — пример использования. Полученная страница будет использовать cookies, по возможности, из нашего хранилища.

Составление POST-запросов

Приступим к авторизации на http://kubuntu. ru. Всё, что от нас требуется — составить верный post-запрос, а это всего-лишь ассоциативный массив. Вот что у меня получилось в итоге: loginData = urllib. urlencode({

‘name’ : ‘Ockonal’,

‘pass’ : ‘MyPass~’

})

Теперь нужно узнать, куда же слать наш POST-запрос. Это нам подскажет Firebug. В списке запросов наведите на последний: Post destination? node, в итоге появится строка: http://kubuntu. ru/node? destination=node. Именно сюда и нужно слать информацию об авторизации. Попробуем сделать то, что я описал: authPageData = cookieOpener. open(‘httр://kubuntu. гu/nоde? destionation=node’, loginData).read() fileHandle = open(‘auth. html’, ‘w’) fileHandle. write(authMainPageData) fileHandle. close()

Откройте получившийся auth. html. Нас ожидаёт крушение планов, да. Полей post-запроса не хватило. Чтобы не засорять статью, я взял всё, что там было. Но обратите внимание на параметр form_build_id form-531f740522f8c290ead9b88f3da026d2

Я не вникал в подробности, но, думаю, именно из-за отсутствия этого поля нас не допускает к авторизации. Где можно взять значение поля form_build_id? Давайте посмотрим исходный код страницы возле формы авторизации.

<input id="form-531f740522f8c290ead9b88f3da026d2" name="form_build_id" type="hidden" value="form-531f740522f8c290ead9b88f3da026d2" />

Теперь нам нужно просто получить значение этого скрытого параметра. На помощь приходит библиотека Beautiful Soup.

From BeautifulSoup import BeautifulSoup mainPageData = cookieOpener. open(‘httр://kubuntu. гu’).read() soupHandler = BeautifulSoup(mainPageData) buildArea = soupHandler. find(‘input’, attrs={‘name’ : ‘form_build_id’, ‘type’ : ‘hidden’}) print buildArea['value']

Первой строкой подключаем библиотеку. Далее нам нужно загрузить стартовую страницу, чистую. Предпоследняя строка самая интересная. У нас есть объект библиотеки Beautiful Soup. С помощью него можно работать в dom указанного html-кода. Есть функция find (и расширенная findAll). Среди параметров — тип поля/селектор, по которому искать. Может принимать значения: input, a, p, … . Следующий параметр — attrs. О нём я сам узнал совсем недавно, здесь мы можем задавать дополнительные поля искомого объекта (title, value, alt, href, …). В нашем случае укажем имя hidden-поля и то, что это поле hidden-типа.

[ockonal@wincode ~]$ python test. py form-0313646e721af1f91b3653be18eb22da

Работает, отлично. Ещё немного пошаманив над post-запросом, я получил такой финальный вариант (вместе с авторизацией): loginData = urllib. urlencode({

‘form_build_id’: buildArea['value'],

‘form_id’: ‘user_login_block’,

‘op’: ‘Войти в систему’,

‘openid_identifier’: ”,

‘openid. return_to’: ‘httр://kubuntu. гu/openid/authenticate? destination=node’,

‘name’ : ‘Ockonal’,

‘pass’ : ‘MyPass~’

}) authMainPageData = cookieOpener. open(‘httр://kubuntu. гu/nоde? destionation=node’, loginData).read()

Вы можете сохранить вывод в какой-то файл и просмотреть его. Не сомневайтесь, в коде вы обнаружите приветствие для авторизированного пользователя. Половина работы сделана! У нас есть cookie сессии, теперь можно сделать всё, что душе угодно.

Получаем адреса подфорумов

Если вы пройдёте по ссылке “Форум” с главной страницы, то увидите там список подразделов форума. Для скрипта нам понадобится получить все их адреса, чтобы потом в цикле обработать содержимое. Откройте html-код и посмотрите на структуру вывода подфорумов.

<td class="container">

<div class="name"><a href="/forum/14">Установка и настройка</a></div>

</td>

Если бы все форумы шли по порядку, было бы проще. Просто в цикле генерировать адреса ‘/forum/’ + счётчик. Но мы не боимся трудностей вместе с Beautiful Soup.

Def getForumsUrl(): forumPageData = cookieOpener. open(‘httр://kubuntu. гu/forum’).read() forumSoup = BeautifulSoup(forumPageData) forumDivs = forumSoup. findAll(‘div’, attrs={‘class’ : ‘name’ }) forumsList = [] for div in forumDivs: forumsList. append(div. find(‘a’)['href']) return forumsList

Создаём новый объект BeautifulSoup под страницу после авторизации. Ищем все div-объекты, которые содержат аттрибут class, который равен name. А далее просто получаем дочерние href-аттрибуты a-объектов.

Если вывести массив forumList, то мы получим:

/forum/14

/forum/1

/forum/2

/forum/4

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

Получаем адреса тем в подфорумах

Единственная проблема — разбивка на страницы. Первое, что приходит в голову: находим в странице количество страниц и циклом подгружаем каждую подстраницу.

Опять мы нуждаемся в просмотре html-кода. Давайте договоримся, что временно для примера будем экспериментировать в форуме с адресом /forum/1. Где же можно получить количество страниц, на которые разбиты все темы в подфоруме? Для этого я использую кнопку Последняя >>.

<li class="pager-last last"><a href="/forum/1?page=40" title="На последнюю страницу" class="active">последняя »</a></li>

Следующая функция получает адрес форума, находит количество страниц, проходится по ним и получает адреса всех тем: def getForumTopicsUrl(forumUrl): forumData = cookieOpener. open(‘httр://kubuntu. гu’ + forumUrl).read() subforumSoup = BeautifulSoup(forumData) lastPageLi = subforumSoup. find(‘li’, attrs={‘class’ : ‘pager-last last’}) lastPageBlock = lastPageLi. find(‘a’)['href'] print lastPageBlock

Можно было бы, конечно, указать конкретно a-объект, передать title и class. Но в title содержится кириллица, не хочу тратить время на перекодировку в юникод. Единственное, после запуска этой функции с параметром /forum/1 мы получим вывод от скрипта: forum/1?page=40

Не поленимся затронуть тему регулярных выражений: numPages = int(re. findall(forumUrl + ‘\?page=([0-9]*)’, lastPageBlock)[0])

Теперь нам нужно организовать цикл по всем страницам внутри предыдущей функции: topicUrlList = [] for i in xrange(0, numPages): print ‘Parsing page: ‘ + str(i) topicUrlList += parseTopicsFromForum(forumUrl, i)

Здесь я вызвал какую-то неопределённую ещё функцию parseTopicsFromForum. Она получает адреса тем указанного форума и страницы этого форума. Следующий код без объяснений, потому что он похож на предыдущую работу.

<td class="title"><a href="/node/6159">Обновление до 10.04 [РЕШЕНО]</a></td> def parseTopicsFromForum(forumUrl, forumPage): forumExactPageData = cookieOpener. open(‘httр://kubuntu. гu’ + forumUrl + ‘?page=’ + str(forumPage)) pageSoup = BeautifulSoup(forumExactPageData) listTopics = [] pageTdList = pageSoup. findAll(‘td’, attrs={‘class’ : ‘title’}) for topic in pageTdList: print topic. find(‘a’)['href'] listTopics. append(topic. find(‘a’)['href']) return listTopics

Вывод скрипта примерно такой (вместе с циклом по всем подфорумам):

Parsing page: 0

/node/501

/node/6273

/node/631

Parsing page: 1

/node/6229

/node/6160

/node/6215

Parsing page: 2

/node/5335

/node/5166

/node/6134

Накручивание рейтинга

Добрались до финала. Осталось последнее действие — голосование за абсолютно все комментарии во всех постах. К предыдущей функции parseTopicFromForum добавьте в самый конец код: for topicId in topicUrlList: votePlus(topicId)

Мы просто проходимся циклом по всем топикам и плюсуем там всё. Функцию votePlus разберём ниже.

Итак, как же происходит реакция голосования на любом сайте. Всё это тоже post-запрос! Нам его даже сочинять не нужно, просто вытягиваем ссылки на кнопках голосования и проходим по ним.

<span id="vote_up_54304" class="vote-up-inact" title="/vud/comment/54304/1/1"><a href="/vud/comment/54304/1?destination=node%2F6273&token=ee14278c5eb224678225f3aa63520154" class="vote-up-inact" title="Vote up"> def votePlus(topicId): topicPageData = cookieOpener. open(‘httр://kubuntu. гu/node/’ + topicId).read() voteSoup = BeautifulSoup(topicPageData) voteUpLinks = voteSoup. findAll(‘span’, attrs={‘class’ : ‘vote-up-inact’}) for vote in voteUpLinks: cookieOpener. open(‘http://kubuntu. ru’ + vote. find(‘a’)['href'])

Это всё! Теперь просто свяжем все наши функции в одну логическую цепочку: forumsUrlList = getForumsUrl() for forumUrl in forumsUrlList: getForumTopicsUrl(forumUrl)

Скрипт будет перебирать все подфорумы, там все страницы, на всех страницах все топики, в каждом топике плюсовать абсолютно все комментарии.

Заключение

Хочу ещё раз принести свои извинения перед админом ресурса kubuntu. ru. Я действительно создавал и запускал скрипт с добрыми намерениями (:D). Надеюсь, никому в голову не придёт испробовать всё это опять на этом же форуме.

Удачи в познавании программирования!


Карта сайта


Информационный сайт Webavtocat.ru