Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР 124, АПРЕЛЬ 2009 г.

Атака на Мистера Твиттера. Пишем скрипты для спама Twitter на Python’e

Роман «Spirit» Хоменко (http://tutamc.com)




Недавно мне попался на глаза пост на хабре с располагающим названием «Коммерческий инструмент для спама в твиттере – TweetTornado». Я заглянул на официальный сайт этого чуда-юда (tweettornado.com) и чуть со стула не упал. Оказалось, что он стоит целых 100 долларов!

«За любовь не платят», - процитировал я исконный девиз ex-USSR хакеров и тут же взялся за написание соответствующей программы. Утаивать тонкости данного процесса от читателей ][ я не планирую, поэтому из статьи ты узнаешь, как злые кодеры создают рабочий инструмент для спама в твиттере. Он будет представлять собой 4 скрипта:

  • для отсылки одиночного сообщения;
  • бота для имитации активности аккаунта;
  • для добавления друзей;
  • для удаления «не друзей».

Ну что же, приступим.

Twitter

Прежде всего – немного теории для олдскульных зомби, которые до этого момента ничего не знали о микроблоггинге. Если ты не из их числа – перескакивай к следующему разделу. Итак, Twitter (twitter.com) - это микроблогинговый сервис, максимальный размер сообщения в рамках которого не должен превышать 140 символов. Суть сервиса проста: у тебя есть лента сообщений, в которой отображаются записи, сделанные тобою и твоими друзьями (или на языке твиттера - following). Все люди, следящие за блогом (followers), будут получать твои сообщения.

Твиттер родился в 2006 году, но популярность стал набирать где-то в начале 2007. Сейчас в нем около 5 миллионов пользователей. С одной стороны, вроде бы и маловато (по сравнению с социальными сетями), но, учитывая неплохую положительную динамику в плане роста и тот факт, что одно отправленное сообщение тут же будет приходить всем зафолловленным пользователям – «реклама» в твиттере будет иметь очень радужные перспективы. Сразу оговорюсь, мы не будем работать с русскоговорящей аудиторией твиттера (в связи с тем, что ее представляют, в основном, наши с тобой братья – компьютерные специалисты), предпочтя ей злых буржуинов - в Штатах твиттер уже выбрался в массы, к «домохозяйкам».

Подготовка рабочего места

Скрипты мы будем писать на Python – в Linux он есть в большинстве дистрибутивов по умолчанию, а для работы с ним в Windows нужно установить интерпретатор версии 2.5 и библиотеку pyCurl. Все это хозяйство ты можешь найти на нашем диске. Для экспериментов со скриптами рекомендую заранее зарегистрировать новый аккаунт на твиттере вручную. Этот этап автоматизировать мы не будем в связи с наличием хорошей капчи, но много аккаунтов и не нужно, для спама хватит одного.

Твиттер API

Twitter всегда был приветлив к рекламным агентам. Его авторы создали очень простое API, позволяющее одним POST- или GET-запросом производить любые действия над твиттером. Все запросы отлично описаны в документации (apiwiki.twitter.com). Для каждого действия приводится:

  • URL запроса;
  • формат ответа, который вернет сервер;
  • метод запроса (POST или GET);
  • параметры и их описание;
  • пример запроса.

Некоторые примеры, взятые из документации, ты сможешь увидеть на врезках. Кстати, когда будешь производить действия, требующие авторизации, помни, что в запросе нужно посылать логин и пароль с помощью HTTP Basic Authentication.

При выборе формата результата мы всегда будем выбирать xml - мне он больше нравится, да и обрабатывать его очень просто. Теперь немного отвлечемся от API твиттера и научимся посылать запросы с помощью Python.

Python и cURL

В случае возникновения необходимости выполнения HTTP-запросов я всегда выбираю мощный, универсальный инструмент, который работает во многих языках программирования - cURL. Для его использования подключим библиотеку pycURL:

import pycurl

Чтобы принять данные после запроса и сохранить их, библиотека pycURL требует указать функцию, которая будет принимать данные. Можно, конечно, и самим написать ее, но лучше воспользуемся модулем для работы со строками StringIO. В объекте StringIO есть метод write, идеально для этой цели подходящий. Подключим модуль и объявим объект такими строчками:

import StringIO
data = StringIO.StringIO()

Теперь можно объявить объект pycURL и настроить параметры, например, для осуществления запроса главной странички xakep.ru. Полученный результат выведем на экран:

curl = pycurl.Curl()
#устанавливаем параметры запроса
curl.setopt(pycurl.URL, 'xakep.ru')
curl.setopt(pycurl.WRITEFUNCTION, data.write)
#исполним запрос
curl.perform()
#освободим память
curl.close()
#получим результат и выведем на экран
print data.getvalue()

Далее, манипулируя методом setopt, мы по желанию сможем изменять наши запросы. Например, если добавить нижеуказанный код, то запрос будет осуществлен через socks с IP 192.168.1.1, размещенным на 2222 порту.

curl.setopt(pycurl.PROXYTYPE,
pycurl.PROXYTYPE_SOCKS5)
curl.setopt(pycurl.HTTPPROXYTUNNEL,1)
curl.setopt(pycurl.PROXY, '192.168.1.1:2222')

Постинг сообщений

Вернемся к горячо любимому твиттеру и попробуем применить наши знания API твиттера путем создания первого скрипта, который будет отсылать сообщения:

import pycurl, StringIO
data = StringIO.StringIO()
curl = pycurl.Curl()
curl.setopt(pycurl.URL,
'http://twitter.com/statuses/update.xml')
curl.setopt(pycurl.WRITEFUNCTION, data.write)
curl.setopt(pycurl.USERPWD,'spiritua:password')
curl.setopt(pycurl.POSTFIELDS,'status=TEXT')
curl.setopt(pycurl.POST,1)
curl.perform()
curl.close()
print data.getvalue()

В нем, как видно, ничего сложного нет. Главное - не забыть «spiritua:password» заменить на свой логин и пароль, а вместо «TEXT», соответственно, написать нужное сообщение. Чтобы увеличить удобство использования и каждый раз при постинге сообщений не изменять в исходнике текст, заюзаем библиотеку sys для получения аргументов из командной строки:

import sys

– и изменим наш «TEXT» на «sys.argv[1]». Вызывать скрипт мы теперь будем так:

sender.py "I love XAKEP"

Обрати внимание, кавычки обязательны, так как потом скрипт берет только первый аргумент. Без них будет отослано лишь одно слово.

Имитация активности

Чтобы пользователи добавили тебя в друзья и долго не удаляли, нужно имитировать активность, вести себя как обычный твиттерянин, лишь иногда публикуя рекламные посты. А не слишком ли тяжело будет придумывать сообщения? Может, проще подсмотреть их у соседа? Сделаем хитро - выберем проявляющего сетевую активность человека и утащим его посты к себе. После написания подобного скриптика для автопостинга, его можно поставить на cron (программа для запуска заданий по расписанию) с ежечасным вызовом. Для реализации этой задачи научимся читать посты юзеров. В твиттер API указано, что нужно использовать GET-запрос следующего вида - http://twitter.com/statuses/user_timeline/spiritua.xml. Здесь вместо 'spiritua' мы вставим имя выбранного донора сообщений. По расширению xml становится понятно, в каком формате придет результат :). Для дальнейшего парсинга xml можно использовать библиотеки, но в нашем случае от этого усложнения помогут избавиться регулярные выражения. Если мы посмотрим на структуру xml-ответа, то увидим, что сообщения всегда находятся между тегами <text>. Исходя из этого, заюзаем следующую регулярку:

<text>(.*)</text>.

Для использования регулярных выражений в Python подключим модуль с названием re и используем его метод findall, который возвратит массив. Получим следующее:

import re
rez = re.findall("<text>(.*)</text>",data)

Алгоритм скрипта активности будет такой:

  • берем у донора самое последнее сообщение;
  • проверяем, есть ли это сообщение среди наших, и, если нет - публикуем.

Думаю, теперь ты и сам сможешь его написать… или подглядеть на врезке и нашем диске. Осталось разобрать еще два скрипта, которые связаны с ростом рекламной сети, - то есть, расширением списка друзей.

Твиттерянин, где ты?

Для поиска друзей я знаю три способа, которые нужно использовать в зависимости от желаемого результата:

  1. Поиск по имени.
  2. Поиск по списку друзей.
  3. Случайный выбор.

Поиск по имени почти не интересен, и я не знаю, где он был бы в нашей деятельности полезен. Поиск по списку друзей - наиболее продвинут и совершенно незаменим в случае, если нужно искать людей определенного круга. Например, мы хотим продавать маечки с нарисованной Бритни Спирс. Ясное дело, нужно найти ее твиттер и достать список тех, кто его читает. Разумеется, на первых порах такой основательный таргетинг мы рассматривать не будем, поэтому в рамках статьи рассмотрим третий вариант - случайный выбор. Для этого лезем в API и видим, что, послав Get-запрос на URL http://twitter.com/statuses/public_timeline.xml, мы получим 20 последних сообщений вместе с данными об авторах. Кстати, третий вариант удобен еще и тем, что, используя его, мы получим лишь активных пользователей. Имя в ответе от твиттера будет находиться между тегами <screen_name>, поэтому для последующего парсинга результата подойдет вот эта регулярка:

<screen_name>(.*)</screen_name>.

Получив список из 20 пользователей, нужно попросить их добавить нас в друзья. Благо, существует такая наука психология, и она говорит, что, если добавить пользователя к себе в друзья, то он с большой вероятностью добавит нас и будет читать наши сообщения :).

Опять обратимся к API, в котором указано, что для добавления в друзья необходимо послать Post-запрос на URL http://twitter.com/friendships/create/spirit.xml, где вместо spirit придется вставить имя нужного аккаунта.

Переводя все это на Python, получим основные строчки для добавления в друзья (остальное можно подсмотреть на диске):

curl.setopt(pycurl.URL,
'http://twitter.com/friendships/create/'+
name+'.xml')
curl.setopt(pycurl.USERPWD,'spiritua:passwd')
curl.setopt(pycurl.POST,1)

Теперь берем и соединяем «поиск 20 случайных юзеров» и «добавление в друзья». В результате получаем супер скрипт, который, опять же, ставим на Cron с определенным интервалом, и наблюдаем, как бодро и молодцевато (так говорят у нас в милиции) растет рекламная сеть (блин, не зря мне показалось подозрительной его фотография в погонах – Прим. Лозовского).

«Редиска» - плохой человек

И было бы все хорошо, если бы не существовали люди, не желающие ответным жестом добавлять нас с тобой в виртуальные товарищи. Эти люди портят статистику, поэтому я советую периодически от них избавляться. Для этого нужно получить список людей, которых ты добавил друзья, и список тех, кто добавил тебя. Сравниваем списки и кикаем лишних… Для получения списков нужно послать запросы на URL:

http://twitter.com/statuses/friends.xml
http://twitter.com/statuses/followers.xml

В итоге, получим следующий код:

#настроим cURL на получение списка друзей
curl.setopt(pycurl.URL,
'http://twitter.com/statuses/friends.xml')
curl.setopt(pycurl.USERPWD,'spiritua:passwd')
curl.setopt(pycurl.WRITEFUNCTION, data.write)
#запустим запрос
curl.perform()
#сохраним результаты в переменную friends
friends = data.getvalue()
#очистим буфер для следующего запроса
data.truncate(0)
#получим список людей, которые читают нас
curl.setopt(pycurl.URL,
'http://twitter.com/statuses/followers.xml')
curl.perform()
followers = data.getvalue()

В переменных friends, followers будет храниться результат в формате xml, полученный от твиттера. Обработав их уже знакомой нам регуляркой на поиск тегов <screen_name>, получим массивы:

friends = re.findall("<screen_name>(.*)</screen_name>",friends)
followers = re.findall("<screen_name>(.*)</screen_name>",followers)

А теперь можно приступать к удалению, посылая запросы на URL http://twitter.com/friendships/destroy/spirit.xml. В результате должен получиться примерно следующий код (полный исходник рекомендуется курить с нашего диска):

#подключим библиотеку time для команды sleep
import time
curl.setopt(pycurl.POST, 1)
#цикл по всему списку друзей
for friend in friends:
#проверка - читает ли друг нас
if friend not in followers:
#если не читает - то для нас он не друг
curl.setopt(pycurl.URL,
'http://twitter.com/friendships/destroy/'+
friend+'.xml')
curl.perform()
#ждем 2 секунды
time.sleep(2)

В путь!

Вроде бы мы рассмотрели основные тезисы создания рекламной сети в твиттере. Это – базовый набор, которого вполне хватит на первое время. А дальше ты уже сам сможешь их расширять, исходя из своих потребностей, например, добавляя скрипту возможность работать с несколькими аккаунтами или использовать прямые текстовые сообщения, таргетинг и другие занимательные вещи.

Напоследок предлагаю ознакомиться с примером из частной практики одного злого хакера. Итак, после регистрации аккаунта он устанавливает фотографию и заполняет все поля в профиле. Далее настраивает крон на запуск имитации активности где-то с периодичностью в 3 часа. Ждет неделю и затем - добавляет в крон запись, позволяющую раз в 3 дня запускать скрипт сначала на очистку от «редисок», а затем - на приглашение 100 новых юзеров. Об аккаунте он забывает до тех пор, пока у него не соберется где-то 2000 фаловеров. На этом он останавливает скрипты роста, оставляя лишь имитацию активности. Приблизительно раз в неделю размещается рекламное объявление.

Скрипт имитации активности

#подлючения нужных библиотек
import re,sys,pycurl,StringIO
#инициализация обьектов для посылки запросов
data = StringIO.StringIO()
curl = pycurl.Curl()

#вместо donor нужно написать имя пользователя
#которого нужно копировать
curl.setopt(pycurl.URL,
'twitter.com/statuses/user_timeline/donor.xml')
curl.setopt(pycurl.WRITEFUNCTION, data.write)
#запуск запроса на получения сообщений юзера
curl.perform()
#применения регулярки для выделения из текста
#лишь сообщений и сохранения их в масив donor
donor = re.findall("<text>(.*)</text>",
data.getvalue())

#освобождаем буфер для следующего запроса
data.truncate(0)
#вместо user пишем свое имя твиттера
curl.setopt(pycurl.URL,
'twitter.com/statuses/user_timeline/user.xml')
curl.perform()
#поиск всех сообщений и сохранение их в масив my
my = re.findall("<text>(.*)</text>",data.getvalue())
#если последнего сообщения донора нет у нас
if donor[0] not in my:
#настраиваем запрос для отсылки сообщения
curl.setopt(pycurl.URL,
'twitter.com/statuses/update.xml')
#не забываем поменять имя и пароль
curl.setopt(pycurl.USERPWD,'name:passwd')
curl.setopt(pycurl.POSTFIELDS,
'status='+donor[0])
curl.setopt(pycurl.POST,1)
curl.perform()
print 'one update posted'
else:
print 'no new updates'

#не забываем освободить память
curl.close()

Обновление статуса (отсылка сообщения) пользователя

  • URL: http://twitter.com/statuses/update.format
  • Форматы результатов (format): xml, json
  • Метод запроса: POST
  • Параметры: status - обязательный параметр, который содержит текст нового статуса. Используйте URL-кодирование при необходимости. Длина текста должна быть не более 140 символов

Добавление в друзья

  • Добавить в друзья пользователя, зная ID или логин
  • URL: http://twitter.com/friendships/create/id.format
  • Форматы результатов (format): xml, json
  • Метод запроса: POST
  • Параметры: id - обязательной параметр, содержит ID или логин пользователя, которого добавляем в друзья
  • Пример запроса: http://twitter.com/friendships/create/bob.xml

DVD

На диске, кроме исходников и необходимых установочных файлов, ты найдешь также видео автора об установке и настройке среды программирования

WARNING

Реклама полностью законна! Но – незаконно копирование в свой твит чужого контента. Этого мы тебе делать не советуем.

WWW

Содержание
ttfb: 7.3940753936768 ms