В последнее время на глаза стали часто попадаться программы для полуавтоматического постинга анонсов новостей блога в разного рода закладки, социальные сети и т.д. В принципе, подобный софт облегчает жизнь блоггера – нет необходимости держать в голове (или в отдельном файле) десятки адресов и паролей к аккаунтам – занес данные в программу, заполнил необходимые поля, ткнул кнопку и сиди вводи каптчи, а анонсы сами добавляются куда нужно.
Может кто-то и покупает подобный софт за *дцать зеленых енотов, но мне, как любителю программировать что-либо (не обязательно для кого-то), узнать что-то новое из области программирования, как-то жалко тратить деньги на то, что вполне можно собрать самому и при этом бесплатно. Вот и решил я сегодня поделиться с Вами информацией по поводу того как могут работать программы подобного рода. Сразу оговорюсь, что предоставляемая информация не может охватить всех возможных способов авторизации и автоматического постинга, т.к. практически к каждому сервису придётся подходить индивидуально. Более того, то, что я сегодня буду рассказывать я делаю исключительно для своих нужд и продавать ничего не собираюсь – берите бесплатно то что есть и как есть. Рассмотрим сегодня два случая:
- Отправка данных в социальную сеть без регистрации
- Отправка данных в сервис социальных закладок с предварительной авторизацией на сайте.
Инструментарий, которым я пользовался при написании этой статьи довольно прост:
- Lazarus (можно использовать и Delphi)
- Библиотека Synapse для работы с запросами GET, POST
- Плагин к FireFox Live Headers для просмотра HTTP-заголовков
и никаких WebBrowser’ов, Indy, OLE-объектов и пр.
1. Отправка данных в социальную сеть без регистрации.
Социальные сети, которые не требуют предварительной регистрации аккаунта – редкость для Рунета. Но тем не менее встречаются. Социальная сеть в том понятии, в котором она применяется в данном посте – это не то место где копирайтеры свое мастерство в написании и переписывании статей и не ВКонтакте. Соц.сеть сегодгя для нас – это сайт на котором нам самим выгодно и необходимо разместить бесплатный анонс своего поста в блоге.
Отсутствие регистрации ни коим образом не говорит о том, что пост можно закидывать в сеть “не глядя”. Обычно отсутствие регистрации компенсируется наличием каптчи различной сложности. Поэтому для отправки анонса статьи нам необходимо предварительно решить вопрос о том, как эту самую каптчу загрузить, показать пользователю и отправить ответ обратно на сервер.
Про Captcha в виде картинок, генерируемых php или js-скриптами я уже говорил по-моему достаточно, поэтому сегодня рассмотрим вопрос о том как работать с каптчей вот такого вида:
В принципе назвать чекбокс каптчей как-то язык не поворачивается, но, тем не менее подобный способ защиты от ботов в Рунете набирает обороты, несмотря на то, что степень защиты практически нулевая. Отличительной чертой подобной защиты может являться то, что практически каждая каптча такого вида имеет свой уникальный код, несмотря на то, что принцип работы один и тот же – если чекбокс отмечен, то сообщение отправляет человек.
Итак, берем подопытную социальную сеть (назовем её – сеть X), и начинаем анализировать.
1.1. Работа с Captcha в виде чекбокса
Первое, что сразу хочется сделать – это заглянуть в HTML-код страницы. Открываем исходник странички и ищем элемент формы, который отвечает за каптчу, т.е. чекбокс. В моем случае HTML-код выглядит вот так (картинка кликабельна):
Из приведенного отрывка исходника странички можно сделать вывод, что при клике по чекбоксу срабатывает некая функция sp_captcha_change(), которая по логике вещей должна менять код каптчи для отправки на сервер. Ищем как выглядит функция:
Как видите, у функции нет никаких генераторов случайных чисел и пр. только присвоение элементу какого либо значения в зависимости от состояния чекбокса. Следовательно, сделать что-либо подобное как с каптчей в Яндекс.Файлах нам не получится. Будем анализировать HTTP-заголовки.
Для чистоты эксперимента необходимо удалить все куки, относящиеся к исследуемому сайту. Чистим, запускаем Live Headers и обновляем исследуемую страничку. Заголовок ответа сервера у меня получился вот таким:
Смотрим, что твориться в исходнике в части скрипта:
Как видите, никакого кода нет. А быть долежен. Но зато при первом запросе страницы нам сервер установил куку и, видимо это неспроста
Обновляем страницу ещё раз (без очистки Cookies) и видим:
При наличии куки код генерируется при каждом обновлении страницы. Следовательно, напрашивается вполне, на мой взгляд, простой вывод: для того, чтобы получить код каптчи необходимо:
1. Запросить страницу и сохранить полученные куки
2. Найти в коде страницы скрипт и вытащить оттуда код. Для этого можно воспользоваться, например, регулярными выражениями.
Попробуем реализовать всё вышесказанное на примере. Открываем Lazarus/Delphi и начинаем программировать. Итак, для отправки и получения данных по HTTP-протоколу в Synapse есть объект THTTPSend в модуле httpsend. Подключаем модуль и пишем функцию получения кода Captcha. Часть функции, отвечающая за получение HTTP-кода с каптчей выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 | [...] HTTPSend:=THTTPSend.Create; HTTPSend.KeepAlive:=true; HTTPSend.KeepAliveTimeout:=300; HTTPSend.TargetHost:='host.domain'; HTTPSend.TargetPort:='80'; HTTPSend.Protocol:='1.1'; HTTPSend.HTTPMethod('get','http://адрес.страницы');//после этого действия получим куку HTTPSend.Headers.Clear;//чистим заголовки HTTPSend.HTTPMethod('get','http://адрес.страницы');//теперь скрипт должен содержать код каптчи [...] |
Исходный код страницы содержится в потоке TMemoryStream, прочитать который можно, обратившись к свойству HTTPSend.Document.
Если Вы хотите удостовериться в том, что необходимые кукисы установлены, то можете обратиться к свойству HTTPSend.Cookies. Все Cookies сохраняются автоматически.
Теперь, когда у нас есть документ, можно смело искать код каптчи. Так как я сейчас работаю в Lazarus под Ubuntu, то, соответственно, не имею доступа к TRegEx, на зато могу воспользоваться “родным” модулем Лазаря RegExpr для работы с регулярными выражениями. Модуль сам по себе довольно мелковат для более менее серьёзного парсинга, но для решения нашей задачи его вполне хватит. Часть кода для чтения Captcha у меня выглядит вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | [...] var RegExpr: TRegExprEngine; RegText: string; index,len:integer; begin RegText:='document.getElementById(.*).value = (.{9,})'; if GenerateRegExprEngine(PChar(RegText),[ref_caseinsensitive,ref_multiline],RegExpr) then begin RegExprPos(RegExpr,PChar(cText.DataString),index,len); RegText:=copy(cText.DataString,index,len); Result:=copy(RegText,length(RegText)-9,9); end else Result:=''; [...] |
Если Вы используете Delphi, то советую воспользоваться объектов TRegEx – код получиться более красивым и будет возможность получить сразу необходимый результат через объект Matches без использования всяких copy.
Итак, первая часть работы выполнена – у нас есть код Captcha для отправки на сервер. Переходим ко второй части – отправки анонса в социальную сеть.
1.2. Постим сообщение в соц.сеть
Если Вы достаточно хорошо знакомы с работой методов GET и POST, то в отправки сообщения у Вас сложностей возникнуть не должно. Однако для порядка, приведу пример.
Работать будем теперь только с заголовками. Заполняем все необходимые поля на странице, жмем кнопку отпраки сообщения и анализируем заголовок у POST. Я не буду приводить и расписывать его содержимое целиком (нам это сегодня не важно), а ограничусь только основными элементами:
1 2 3 4 5 | [...] Cookie: PHPSESSID=fa1b473c2c232549c52dc181d28a30cf Content-Type: application/x-www-form-urlencoded Content-Length: 145 [...] |
При этом контент отправляемого документа выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 | url=[адрес страницы с анонсом]
&title=[заголовок страницы]
&content=[описание анонса]
&addcats[]=1 {номер категории в которую необходимо поместить анонс}
&name=[имя автора]
&email=[почта автора]
&site=[сайт автора]
&sp2-captcha_sess=[код каптчи]
&submit=Добавить
&post_id=secret |
Всё, что нам остаётся – это сформировать правильный документ и отправить его на сервер методом POST. Используя Synapse это можно сделать например вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | [...] Data:=TStringStream.Create(''); Data.WriteString('url='+Edit1.Text+'&'); Data.WriteString('title='+Edit2.Text+'&'); Data.WriteString('content='+Memo1.Text+'&'); Data.WriteString('addcats[]={новер категории}&'); Data.WriteString('name='+Edit3.Text+'&'); Data.WriteString('email='+Edit4.Text+'&'); Data.WriteString('site='+Edit5.Text+'&'); Data.WriteString('sp2-captcha_sess='+{код Captcha}); Data.WriteString('&submit=Добавить&'); Data.WriteString('post_id=secret'); HTTPSend.MimeType:='application/x-www-form-urlencoded'; if HTTPSend.HTTPMethod('post','http://адрес.страницы') then begin //анализируем ответ, выводим сообщение и т.д. end [...] |
Если запрос отправлен успешно, то это не говорит Вам о том, что новость была опубликована, поэтому дополнительно необходимо проанализировать заголовки ответа сервера. В зависимости от того как устроена обработка формы Вам вернется ответ, который может сказать о результате операции. Например, в моем случае об успешной отправке анонса говорит заголовок с кодом 302 с редиректом на страницу анонса, а при неудачной отправке – код 200. Как будет выглядеть проверка у Вас скажет анализ заголовков.
В целом, задачу отправки анонса в социальную сеть можно считать решенной. Если у Вас остались какие-либо вопросы – задавайте. А пока переходим к следующему вопросу.
Отправка данных в сервис социальных закладок с предварительной авторизацией на сайте.
В данном случае работа с сервисом с одной стороны упрощается, т.к. обчно достаточно авторизоваться без ввода Captcha, а с другой стороны, дополнительно необходимо удостовериться в том, что сайт нас всё-таки запустил к себе и мы отправляем данные не в пустоту.
Примеров сервисов социальных закладок в Рунете море – это и toodoo.ru, и МоёМесто и БобрДобр и пр. Каждый из сервисов имеет свой уникальный дизайн, равно как и своё описание элементов форм, поэтому, повторюсь ещё раз – подход в каждом отдельном случае индивидуальный, но алгоритм практически один и тот же. А заключается он (алгоритм) в следующем:
1. Отправляем данные авторизации на сайт
2. Анализируем HTTP-заголовок ответа сервера
3. Если анализ заголовка показывает, что авторизация прошла успешно, то формируем документ с анонсом и отправляем новый запрос на сервер
4. Анализируем HTTP-заголовок ответа сервера, чтобы убедиться, что анонс опубликован
5. Сообщаем результаты пользователю.
По-моему алгоритм прост как три копейки. Попробуем реализовать его на примере. В качестве примера я выбрал сервис toodoo.ru. Во-первых, потому что сам частенько заглядываю туда и читаю, интересующие меня новости. Во-вторых, часто отправляю туда новые ссылки (не только анонсов своего блога). Начнем.
2.1. Авторизуемся на сайте
Вход на сайт осуществляется со страницы http://toodoo.ru/account/signup. С неё и начнем. Заносим в форму свои регистрационные данные, жмем кнопку отправки и анализируем запросы. Вначале анализируем, что и, главное, куда мы собственно отправляем:
Как видите, несмотря на то, что данные заполнялись на странице signup, запрос отправляется на адрес http://toodoo.ru/account/login. Данные формы в теле отправляемого документа выглядят следующим образом:
1 2 3 4 5 | email=[адрес эл.почты] &email_confirm=[адрес эл.почты] &password=[пароль] &x=41 &y=22 |
Последние два параметра можно не отправлять, т.к. роли они никакой особо не играют – авторизация пройдет и без них. Теперь смотрим, что возвращает нам сервер при успешной авторизации:
То есть, при успешной авторизации нас перенаправляют на персональную страницу, предварительно навтыкав нам гору кукисов. Если просматривать заголовки дальше, то можно увидеть, что после успешной авторизации нас бросает сначала на персональную страницу, а потом обратно на главную, но на главной мы уже видим ссылки для управления аккаунтом. Но нас это сильно интересовать не должно, главное – мы знаем как определить, что мы авторизованы – по полю Location в Headers.
Если же авторизация не проходит, то нас оставляют на странице авторизации.
Двигаемся дальше. Проверяем отправку нового сообщения (ссылки) в журнал. Тело документа будет выглядеть следующим образом:
1 2 3 4 5 6 | dig_url=[адрес ссылки] &dig_title=[заголовок] &comment=[комментарий к ссылке] &where=friends &submit.x=38 &submit.y=26 |
Используя объект THTTPSend отправку ссылки в сервис можно осуществить например так (без проверок операции авторизации и отправки):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | var Data: TStringStream; HTTPSend: THTTPSend; begin HTTPSend:=THTTPSend.Create; HTTPSend.KeepAlive:=true; HTTPSend.KeepAliveTimeout:=300; HTTPSend.TargetHost:='toodoo.ru'; HTTPSend.TargetPort:='80'; HTTPSend.Protocol:='1.1'; HTTPSend.MimeType:='application/x-www-form-urlencoded'; Data:=TStringStream.Create(''); Data.WriteString('email='+{адрес почты}); Data.WriteString('&email_confirm='+{адрес почты}); Data.WriteString('&password='+{пароль}'); //авторизуемся на сайте HTTPSend.Document.LoadFromStream(Data); HTTPSend.HTTPMethod('post','http://toodoo.ru/account/login'); {тут необходимо проанализировать ответ сервера} [...] //очищаем заголовки и тело документа HTTPSend.Document.Clear; HTTPSend.Headers.Clear; {отправляем данные ссылки на сервер} HTTPSend.MimeType:='application/x-www-form-urlencoded'; Data.Free; Data:=TStringStream.Create(''); Data.WriteString('dig_url='+Edit1.Text); Data.WriteString('&dig_title='+Edit2.Text); Data.WriteString('&comment='+Memo1.Text); Data.WriteString('&where=friends'); Data.WriteString('&submit.x=63'); Data.WriteString('&submit.y=13'); //отправляем данные на сервер HTTPSend.Document.LoadFromStream(Data); HTTPSend.HTTPMethod('post','http://toodoo.ru/digest/discuss'); {анализируем ответ сервера} |
Вот пожалуй и вся работа по отправке анонсов в соц.сети и закладки. По-моему ничего сверхсложного нет. Главное провести анализ HTTP-заголовков и правильно сформировать запрос.
Кстати, в заключение, хотел бы немного сказать про библиотеку Synapse. Сегодня я её использовал впервые в своей работе и надо сказать библиотека отличная. Не требуется лишний раз задумываться над куками и, как в Indy, использовать отдельный компонент, куки автоматом сохраняются в списке откуда их всегда можно вытащить. Сама работа с библиотекой также проста. При работе с теми же HTTP-запросами я использовал метод объекта THTTPSend, а вообще в модуле есть достаточно удобные функции:
1 2 3 4 5 | function HttpGetText(const URL: string; const Response: TStrings): Boolean; function HttpGetBinary(const URL: string; const Response: TStream): Boolean; function HttpPostBinary(const URL: string; const Data: TStream): Boolean; function HttpPostURL(const URL, URLData: string; const Data: TStream): Boolean; function HttpPostFile(const URL, FieldName, FileName: string; const Data: TStream; const ResultData: TStrings): Boolean; |
По-моему, функции достаточно “говорящие”, чтобы расписывать их назначение, тем более, что в модуле дается их описание. Например, можно было бы при отправке данных на сервер пользоваться функцией HttpPostURL, а для получения содержимого страницы функцией HttpGetText.
Также просто можно работать и с протоколами ftp, ssl, pop3 и т.д. Вобщем, библиотеку пока показывает себя с наилучшей стороны как в быстродействии и удобстве использования так и в отсутсвии глюков в работе.
Похожие записи:







20 Jan 2010 в 4:44 am
Спасибо за замечательную статью. По Synapse как-то маловато в сети..
Пробую аналогичным образом сделать авторизацию на https://partner.r01.ru/. А именно беру get’ом, получаю кукис, с ним уже делаю post, получаю заветные 302, но без новых кукис (т.е. остаются те же) и без location. Что делать – не знаю. Мучаю в различных вариантах, но ничего не получается. Буду благодарна за любую помощь.
data:=tstringstream.create('');
httpsend:=thttpsend.Create;
httpsend.KeepAlive:=true;
httpsend.TargetHost:='partner.r01.ru';
httpsend.TargetPort:='80';
httpsend.Protocol:='1.1';
httpsend.HTTPMethod('get','https://partner.r01.ru/');
httpsend.Headers.Clear;
httpsend.Document.Clear;
data.Writestring('wizard_domain2=');
data.Writestring('&auid=');
data.Writestring('&htid=');
data.Writestring('&action=1');
data.Writestring('&login=(здесь логин)');
data.Writestring('&passwd=(здесь пароль)');
httpsend.MimeType:='application/x-www-form-urlencoded';
httpsend.Document.LoadFromStream(Data);
if httpsend.HTTPMethod('post','https://partner.r01.ru/?' ) then begin
stmp:=httpsend.Headers.GetText;
end
20 Jan 2010 в 12:04 pm
В приведенном коде:
httpsend.TargetHost:='partner.r01.ru';httpsend.TargetPort:='80';
httpsend.Protocol:='1.1';
можно не писать – эти данные заполнятся автоматически при вызове:
httpsend.HTTPMethod('get','https://partner.r01.ru/');Эти данные:
httpsend.Headers.Clear;httpsend.Document.Clear;
Тоже можно не вычищать.
if httpsend.HTTPMethod('post','https://partner.r01.ru/?' ) then beginотправьте так:
if httpsend.HTTPMethod('post','https://partner.r01.ru/' ) then beginИ последнее. Пишите в data все данные как есть, т.е. без
&и пр. Потом подключаете модуль synacrypt и Методом EncodeURLElement уже приводите данные к виду в котором они должны отсылаться на сервер. Авторизация должна сработать20 Jan 2010 в 11:16 pm
Все сделала (при https порт автоматически, кстати, меняется же..), EncodeURLElement сделала (он в synacode). Уже во всех возможных вариантах – и со знаком вопроса, и без..
Если не делаю httpsend.headers.clear, то в ответе получаю 400 Bad Request..Хм!
Need your help))
Заранее спасибо)
21 Jan 2010 в 9:38 am
400 ошибка…хм..Content-Length надеюсь ручками не прописываете?
Ок. Сегодня вечером после работы гляну, что там за авторизация и постараюсь помочь
21 Jan 2010 в 1:13 pm
в том-то и дело, что не прописываю)) странно)) весь код (до проблемного места) приведен..
заранее спасибо))
24 Jan 2010 в 6:21 pm
dkdk, вроде бы решил вашу проблему – авторизовался на сайте. Ответ в посте http://www.webdelphi.ru/2010/01/synapse-avtorizaciya-na-sajte-rabota-s-https/
надеюсь я не сильно задержался