Пишем бота для сайта на javascript

(Не смотря на то, что в тексте присутствуют примеры кода и рабочие функции, эта статья служит скорее наводкой, для людей которым надо написать пожую программу, а не конкретным примером действия. Я описал возникшие у меня трудности и их решение, но думаю этого не достаточно)

Волею судьбы мне понадобилось написать бота для сайта, навигация по которому полностью построена на ajax. Я уже сталкивался с такой проблемой и прекрастно понимаю, что php-шная библиотека curl вполне справляется со своей задачей и в этом случае с ajax-ом, но, видать, я был не первым таким умником и разработчики сайта защитились от ботов постоянно меняя ajax-запросы, да еще и генерируя их с помощью javascript.


Все статьи цикла
Промучившись три ночи, я осознал, что нужно писать интерпретатор javascript, вот только занятие это, мягко говоря, не благодарное, покопавшись в интернете я нашел уже 2 готовых (весь остальной абзац к делу фактически не относится):

Для java. (пользуясь случаем хочу отметить, что вопреки распространенному мнению, java и javascript – два совершенно разных языка, а приставку «java» javascript приобрел в последний момент и только в маркетинговых целях). Благодаря случайно попавшейся месяц назад на глаза батиной книжке Питера Нотона за 1996 год я понял, что java – язык без сомнения интересный и позволяющий решить много специфических задач язык. Вот только я, как и большинство, думаю, не смогу взять и написать бота на нем, java требует не малых усилий по изучению, и если и изучать его, то не для написания одной маленькой программки.
Для python. Наверно в начале весны от одного человека (руководителя одной из конторок по производству сайтов, как он сам себя называет) слышал восторженный отзыв об этом языке программирования, в котором обсирались все известные мне серверные языки и прославлялся великий и могучий python. Из этого монолога я также узнал, что для конторы, по заказу на python-е сейчас пишется CMS и почти завершена. Вот только:
говорил он не своими словами, а словами программиста, который эту CMS пишет, сам же он даже не знает, как этот самый python выглядит.
в программировании этот человек не идет дальше лабороторок в институте и даже плохо в нем соображает, все его программирование ограничивается навигацией в интерактивных flesh мультиках,
даже после полутора лет своей работы контора убыточна,
возможно из-за предыдущего пункта она, по сути, не легальна и представляет собой просто N-ое количество друзей работающих за идею,
CMS все еще не доработана даже до beta-версии.

Исходя из всего этого, я составил обратное мнение об этом языке программирования. Может, конечно, и зря, но этот вариант рассматривать не буду вообще.


После еще одной бессонной ночи впустую, мой засыпающий мозг вспомнил о Delphi. О той старенькой 7-ой версии, которую машинально устанавливаю после каждого форматирования диска, но с которой, по сути, не работал уже лет пять или даже семь. (Кстати, думаю, в C++Builder все те же самые огурцы лежат, но лично не проверял.)

Там, на вкладке internet притаился компонент TWebBrowser, он является встроенным в Delphi экземпляром Internet Explorer-а, в котором уже есть так нужный мне интерпретатор javascript. При этом мы получаем почти безграничную власть над этим творением Microsoft-а.

Это, конечно, хорошо, но в начале нужно научится WebBrowser-ом пользоватся, а с этим у меня возникли проблемы, отвык знаете ли от делфовскиъх мануалов. По этому я ограничился всего парой приемчиков, которые помогли мне перевести работу от самого компонента на непосредственно страницу загруженную в него.

Дальше пойдут примеры кода, которые собирались еще в лохматые времена для Delphi 6 и я не уверен, что они будут работать на новых версиях. (в 7-ой версии работают точно)

Итак, перво-наперво, после размещения компонента TWebBrowser на форме, нам нужно загрузить в него страницу, для этого служит метод Navigate:
WebBrowser.Navigate('http://vvy.me');


Также присутствует и метод отправки post-данных, но он нам совершенно не нужен, отправлять все формы будет сам WebBrowser, нам же нужна лишь страница с которой и начнет гулять по сайту бот, а ее адрес не может содержать post-запросов.

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

добавляем в uses модуть MSHTML
добавляем переменные:
  WB_Doc : IHTMLDocument2; // В этой переменной будет хранится интерфейс взаимодействия со всем WebBrowser-ом
    WB_HTMLWindow : IHTMLWindow2; // А тут уже наше окно, с которым и работает javascript


и конечно кусок кода в процедуре onDocumentComplete
WebBrowser1.Document.QueryInterface(IHTMLDocument2, WB_Doc);
    WB_HTMLWindow := WB_Doc.parentWindow;
    if not Assigned(WB_HTMLWindow)
      then showmessage('авот фих тебе')
      else showmessage(' все OK');




Но на самом деле все немного сложнее, событие onDocumentComplete возникает при:

загрузке html-кода страницы,
загрузке каждого фрейма в отдельности,
в случаях работы javascript на странице, когда происходит какое-то изменение в содержимом тегов,
и еще куча всего.


При этом во время возникновения события объект браузера может быть не доступен, но мы можем убедится в возможности взаимодействия с WebBrowser-ом по его свойству ReadyState, если все хорошо, то оно должно быть равно 4 или READYSTATE_COMPLETE, кому как нравится. Все остальные значения от 0 до 3 в нашем контексте мало интересны и лишь обозначают степень готовности WebBrowser-а к работе с ним.

Что бы добраться до html-кода страницы мы можем применить вот такую функцию:
function GetHtml(doc: IHtmlDocument2): string;
var
   iDisp: IDispatch;
   iCollection: IHTMLElementCollection;
begin
   result := '';
   iCollection := doc.all.Tags('HTML') as IHTMLElementCollection;
   iDisp := iCollection.item(0, 0);
   result := (iDisp as IHTMLElement).outerHTML;
end;


Хоть она и вернула все содержимое в виде одной строки, кодовые символы
в наличии, и все прекрасно загружается например в TMemo с переносом строк:
Memo.Lines.Add(GetHtml(WB_Doc));


Смертельный номер! Функция GetHtml вернет нам не просто html-код страницы, а код, в котором выполнились уже все скрипты, т.е вместо
<script language="javascript">
document.write('Я работаю!');
</script>


Мы получим
<script language="javascript">
document.write('Я работаю!');
</script>
Я работаю!



И даже больше: мы можем обращаться к html-элементам страницы, например заполнять input-ты или отправлять формы средствами Delphi, но что б не курить мануалы еще и по этому вопросу я поступаю хитрее, а точнее выполняю javascript, например:
WB_HTMLWindow.execScript(‘alert(“Тадам!”)’, 'javascript');


Схема работы получается предельно простая (с примерами как получилось у меня):

Загружаем первую страницу
Например, код ссылки, в том виде как ее получил WebBrowser
 <a>Далее</a>


Получаем цункцией GetHtml ее html-код с уже отработавшими скриптами
Наша ссылка получилась вот такой
<a onclick="qwerty()">Далее</a>


И малого того, что событие onclick задается в самом javascript, так еще и функцию qwerty явно прогнали через абсфукатор, но это нам не помешает
Регулярками вытаскиваем код из события onclick
qwerty()

С помощью .execScript запускаем событие onclick, вернее код из него
Из многочисленных событий onDocumentComplete ифом(if) выбираем то, в котором WebBrowser.ReadyState равно 4 и только тогда продолжаем работу. Лично я, для верности, еще и потом включаю TTimer, в любом случае он создаст задержку в выполнении программы, и наш будет больше похож на человека.
Регулярками вынимаем нужную информацию
Находим следующую ссылку «Далее»


И так по кругу

Кстати, в TWebBrowser частенько не выполняются правильно написанные скрипты, которые выполняются в обычном IE, в этом случае на экран выскакивает стандартное окошко об ошибке в javascript, с предложением запустить отладку. И, кроме того, что они мельтешат перед глазами, они еще иступарят нам весь компонент делая его не доступным до момента загрузки нового url-а, да и всю программу, подобно процедуре showmessage(). Это можно решить, назначив свойству Silent значение true. Оно поможет нам отключить все ошибки javascript в TWebBrowser, но оставит нам сообщения об ошибках взаимодействия с самим компонентом, что жизненно важно при отладке.

У TWebBrowser еще куча плюшек, но лично мне хватило перечисленных, а еще все это работает не только в компонентах WebBrowser, но и окнах Internet Explorer-а, достаточно получить указатель на окно программы и вперед!

В конце хочу сказать, что если для работы бота лучше использовать регулярные выражения в самом Delphi, а не в javascrip. Для этого нужно будет импортировать отдельную библиотеку, но это уже совсем другая история.
Добавлено: 14 Августа 2013 05:06:13 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...