уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Собственно почему я вновь возвращаюсь к вопросу работы с Captcha? Во-первых, потому что способы защиты все время улучшаются и лишними знания по вопросам работы таких защит явно не будут. Во-вторых, используя полученные знания, можно будет немного сократить расходы трафика и нервов при работе со всякого рода файлообменниками прочими сайтами с защитой от ботов, которые пестрят уймой рекламы на каждой страницы — будем обходиться загрузкой всего одной картинки и целевой информации.

best_capcha

Ну, и в-третьих, в одном из постов рассматривался вопрос парсинга выдачи поисковиков. Там я никак не затрагивал вопрос «Что делать если всё-таки попал в бан при парсинге?» Ответ прост — вводим полуавтоматически предложенный поисковиком код (Captcha) и двигаемся дальше.

Статья не актуальна в связи с тем, что с момента её публикации прошло  достаточно много времени и рассматриваемый пример (файлообменник Яндекса уже давно «сдох», а механизмы Captcha значительно усложнились

Сегодня рассмотрим загрузку Captcha на примере работы с сервисами Яндекс, а именно: добавление сайта в индекс и скачивание файла с народного файлообменника. Ну, а обход каптчи при парсинге выдачи будет практически на 100% работать как и первый способ — добавление сайта в поиск. Его я не рассматриваю по двум причинам:

  1. Чтобы не повторять один и тот же алгоритм загрузки Captcha два раза
  2. Как-то лень специально влетать в бан хоть и краткосрочный. Для этого тоже надо умудрится составить «сверхбыстрый» алгоритм.

И первое, с чего следует начать рассмотрение вопроса — это немного информации на тему «Как все это устроено».

1. Как устроен обмен данными в Сети. HTTP-протокол.

Тема вопроса задана довольно обширная, но сегодня мы рассмотрим взаимодействие «клиент-сервер» применительно к нашему случаю — работа с Captcha.
HTTP (HyperText Transfer Protocol) изначально предназначенный для передачи гипертекста в настоящее время используется и для «транспорта» данных, например, того же XML-RPC протокола.
Подразумевается, что в Сети есть поставщики данных (серверы) и потребители (клиенты). В нашем случае сервер — сайт с Captcha, клиент — наша будущая программа или веб-браузер.
Как и любой другой протокол (не обязательно связанный с Интернет или компьютерами в принципе) HTTP-протокол подразумевает некоторые правила «общения» клиента и сервера. Правила эти достаточно просты — клиент отправляет на сервер запрос определенного формата, а сервер в свою очередь «отвечает» клиенту, передавая некую информацию, например HTML-код, который обрабатывается на стороне клиента.
Каждый запрос имеет чётко определенную структуру и состоит из следующих частей:

  1. Стартовая строка в которой указывается метод запроса и URI
  2. Заголовки сообщения (headers),  которые характеризуют сообщение, например в заголовках может указываться тип передаваемых данных, кодировка и т.д
  3. Тело сообщения, отделяемое от заголовков пустой строкой

Заголовки и тело запроса могут иногда отсутствовать, но стартовая строка должна быть обязательно.

Теперь, что касается методов. Все методы рассматривать в этом посте смысла нет, т.к. пригодятся нам сегодня всего два:  GET и POST. Их мы и рассмотрим.

Метод GET

Само название говорит за себя. Это метод на получение каких либо данных от сервера. Например стартовая строка вида:

GET /downloads/resource?id=1 HTTP/1.1

говорит, что клиент запрашивает ресурс с какого-либо узла. Причём для определения этого ресурса в URI включен дополнительный параметр id=1. Примерно так выглядят стартовые строки запросов при закачке файлов с нашего блога.

Метод POST

Используется для отправки данных на сервер. Например, если Вы захотите оставить комментарий к статье, то вы заполните соответствующие поля внизу страницы и нажмете кнопку «Комментировать». Вот по нажатию этой кнопки к нашему ресурсу и будет отправлен запрос методом POST и в этом запросе будут содержаться все ваши данные.

Заголовки (Headers)

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

GET /yandsearch?text=Пример_заголовка HTTP/1.1
Host: yandex.ru
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Referer: http://www.yandex.ru/

Попробуем его расшифровать.
GET — метод запроса на получение данных
URI — /yandsearch?text=Пример_заголовка
Host — ресурс к которому идёт обращение.
То есть URL получается выглядит вот так:

http://yandex.ru/yandsearch?text=Пример_заголовка

User-Agent — список названий и версий клиента и его компонентов с комментариями. Как видите, я осуществлял запрос из браузера FireFox 3.5.3
Accept — список допустимых форматов
Accept-Language, Accept-Encoding, Accept-Charset соответственно список поддерживаемых языков, список поддерживаемых способов кодирования содержимого и список поддерживаемых кодировок сообщения.
Referer— URI ресурса, после которого клиент сделал текущий запрос.

Как и полагается, сервер ответит нам сообщением с такими заголовками:

HTTP/1.x 200 OK
Date: Sat, 24 Oct 2009 06:21:59 GMT
Server: Apache/2.2.9 (Unix) mod_perl/2.0.4 Perl/v5.8.8
Cache-Control: private, max-age=3600
Content-Type: text/html; charset=utf-8
Expires: Sat, 24 Oct 2009 07:21:59 GMT
Vary: Accept-Encoding
Content-Encoding: gzip
Connection: close
Transfer-Encoding: chunked

Вначале идёт код состояния запроса, затем дата генерации отклика, список названий и версий сервера, директивы управления кэшированием, тип передаваемых данных и другая информация, в том числе и способ кодирование передаваемых данных.
То есть переводя всё это на простой бытовой человеческий уровень взаимодействие «клиент-сервер» можно описать такой ситуацией:
к пивному ларьку (host) подходит молодой человек (User-Agent) и просит у продавца (сервер):
— Дайте (GET) мне пожалуйста бутылку пива (URI), желательно (Accept) «Клинское Арива».
И дополнительно, на всякий случай добавляет, скромно потупив взор:
— А ещё я знаю английский язык (Accept-Language), могу писать в кодировке utf-8 и win-1251 (Accept-Charset) и вообще крестиком вышиваю (Accept-Charset). И прислал меня не кто-то там, а сам Вася Пупкин (Referer).
На что строгий продавец (Server), взглянув на часы (Date), спокойно отвечает:
— Ok (200 Ok), будет тебе «Клинское Арива» (Content-Type) и весьма замечательно, что ты умеешь вышивать крестиком (Vary) я тоже так могу, гляди чего навышивал (Content-Encoding). И протягивает молодому человеку бутылку пива (Content).
Диалог, конечно довольно несуразный и неестественный, но в таком виде информация по заголовкам и запросам должна запомниться на долго :) Есть даже такой метод запоминания больших чисел — каждая цифра представляется в виде слов, начинающихся с определенной буквы и из этих слов составляется рассказ. Получается полная хрень, но число даже из 20 цифр заполнить вполне реально на неделю-две. Но это всё лирика и флуд. На самом деле, зная информацию, отправляемую клиентом и ответы сервера, можно практически без проблем организовать «правильный» диалог с нужным ресурсом и, например, провести авторизацию или скачать файл, не заходя на сам ресурс. Осталось разобраться как прочитать заголовок и не спутать его с основным контентом. Переходи к следующему вопросу.

2. Чем просматривать заголовки (Headers)

Для просмотра заголовков сообщений есть специальные программы — снифферы (sniffer), которые проводят мониторинг сетевых подключений и «выдергивают» заголовки и/или какие-либо данные из отправляемых и получаемых данных.
Например, если вы используете браузер Firefox, то для просмотра заголовков разработан замечательный плагин под названием LiveHTTPheaders, позволяющий просматривать все заголовки, не выходя из браузера. Просто устанавливаете плагин и затем заходите в «Инструменты — Просмотр HTTP-заголовков«. В итоге откроется окно (или новая вкладка) плагина:

HTTP_headers

Все просматриваемые заголовки можно сохранять в отдельный файл, фильтровать с помощью выражений RegEx, а также отдельно просматривать стартовые строки (вкладка «Генератор»).
Вообще плагин достаточно функциональный и при работе с HTTP-протоколом практически не заменим.
Но это, что касается работы непосредственно в Firefox. А как быть, например, если Вам позарез надо мониторить заголовки. отправляемые Вашим приложением или другим браузером в целях отладки приложения? Можно использовать уже готовые решения, а можно написать сниффер самому. Я решил воспользоваться частично обоими возможностями и представить Вам сниффер с исходниками Delphi 7. Говорю сразу, что разработка это не моя. К сожалению, на момент написания поста программы был на реконструкции, поэтому показываю то, что было когда-то доступно.
AppSniff 1.1.
Программа способна «прицепляться» к любому процессу и мониторить HTTP-траффик, показывая Вам всю необходимую информацию, включая заголовки и данные в HEX-формате.
Работа с программой проста как три копейки. Запускаете AppSniff и выбираете процесс к которому необходимо прицепить сниффер

AppSniff

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

3. Разрабатываем приложение для работы с Captcha

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

3.1. Добавление сайта в поиск Яндекс

Первое, что я обычно делаю при работе с любым HTML-документом — это просматриваю интересующий меня исходный код в поисках необходимых данных. Открываем исходник и видим код, показывающий Captcha:

(Номер на картинке)

Естественно, что параметр key генерируется автоматически при каждом обращении к странице и это обстоятельство следует учитывать при парсинге страницы. На всякий случай посмотрим на заголовки сообщений при загрузке страницы:

GET /image?key=ee7ce95580d7fb56ac9e3816052281df HTTP/1.1
Host: captcha.yandex.net
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
Accept: image/png,image/*;q=0.8,*/*;q=0.5
Accept-Language: ru,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://webmaster.yandex.ru/

Как видите, что попросили у Яндекса, то он нам и вернул на страницу — картинку с параметром Key.
Теперь, убедившись в своей правоте о том, что необходимые данные присутствуют на странице, запускаем Delphi 2010 (ну или какая там у Вас стоит версия) и начинаем разрабатывать приложение.
Чтобы не разрабатывать два приложения для работы с сервисами Яндекс я сделал для своего приложения вот такой интерфейс:

prilojenie

В верхний edit заносим либо URL сайта на добавление в Яндекс либо URL файла для скачивания. Нажимаем необходимую кнопку, получаем и вводим во второй Edit Captcha и выводим результат работы в третий Edit. Все просто и не требует лишних усилий.
Теперь немного про «внутренности» программы. Чтобы сильно не перегружать Вас излишним кодом и комментариями к ним, я решил для примера воспользоваться компонентами Indy. А точнее одним из них — idHTTP. Работа будет осуществляться следующим образом:
1. Отправляем запрос на сервер
2. Получаем ответ и парсим полученные данные в поисках ссылки на каптчу
3. Загружаем Captcha в TImage и ждем ввода проверочного кода
4. При нажатии на кнопку «Отправить на сервер» делаем запрос методом POST и проверяем, что ответит нам сервер
5. Выводим результат работы в Edit.
Укладываем на форму idHTTP, подключаем в uses модуль VBScript_RegExp_55_TLB для работы с RegExp, а также два модуля для работы с графикой JPEG и Gifimg (второй модуль в Delphi 7 может отсутствовать) и в обработчике кнопки «Captcha регистрации сайта» пишем следующий код:

procedure TForm3.Button1Click(Sender: TObject);
var List: TStringList;
    R: TRegExp;
    mc: MatchCollection;
    m: Match;
    sm: SubMatches;
    Stream: TMemoryStream;
    JPEG: TJPEGImage;
begin
  List:=TStringList.Create;
  List.Text:=IdHTTP1.Get('http://webmaster.yandex.ru/');//получаем содержимое страницы в переменную
  R:=TRegExp.Create(self);
  R.Pattern:='src="(.*?key.*?)"'; //регулярное выражение для поиска Captcha
  R.Multiline:=true;
  R.IgnoreCase:=true;
  R.Global:=true;
  mc := R.Execute(List.Text) as MatchCollection;
    if mc.Count > 0 then //нашли совпадение
      begin
        m:= mc[0] as Match;
        sm:= m.SubMatches as SubMatches;
        Stream:=TMemoryStream.Create;
        IdHTTP1.Get(sm[0],Stream); //грузим капчу в поток.
        Stream.Position:=0; //устанавливаем ОБЯЗАТЕЛЬНО на ноль
        JPEG:=TJPEGImage.Create;   //создаем jpeg
        JPEG.LoadFromStream(Stream);//загружаем данные из потока
        Image1.Picture.Assign(JPEG);//выводим в Image
      end;
end;

Полдела сделано — каптча выводится в Image. Осталось только правильно её отправить на сервер.
Для этого рассмотрим запрос, который отправляется на сервер при нажатии кнопки «Добавить» формы добавления сайта:

POST /add.xml HTTP/1.1
Host: webmaster.yandex.ru
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://webmaster.yandex.ru/
Content-Type: application/x-www-form-urlencoded
Content-Length: 64
url=webdelphi.ru&key=3ab4f1280e4afc49181ee3438a5a5c1e&rep=033316

Обратите внимание на сроку после заголовка Content-Length. Как видите — это параметры запроса методом POST:
url — URL нашего файла;
key — ключ Captcha
rep — код Captcha, который мы ввели
Теперь всё, что от нас требуется — правильно сформировать и отправить запрос на сервер. Для примера я создам запрос один-к-одному как и представленный выше.

var ParamList: TStringList;
begin
  if webmaster then
    begin
      ParamList:=TStringList.Create;   //создаем список параметров для отправки
      ParamList.Add('url='+Edit1.Text);
      ParamList.Add('key='+CaptchaKey);
      ParamList.Add('rep='+Edit2.Text);
      ParamList.Add('');
      with IdHTTP1.Request do
        begin
          URL:='http://webmaster.yandex.ru/add.xml';
          Host:='webmaster.yandex.ru';
          UserAgent:='Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3';
          Accept:='text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
          AcceptLanguage:='ru,en-us;q=0.7,en;q=0.3';
          AcceptEncoding:='gzip,deflate';
          AcceptCharSet:='windows-1251,utf-8;q=0.7,*;q=0.7';
          Connection:='keep-alive';
          Referer:='http://webmaster.yandex.ru/';
          ContentLength:=Length(ParamList.Text);
          ContentType:='application/x-www-form-urlencoded';
        end;
        ParamList.Text:=IdHTTP1.Post('http://webmaster.yandex.ru/add.xml',ParamList);
    end
  else
    begin

    end;
end;

В случае, если сайт будет успешно добавлен в систему Вы должны получить в переменную ParamList новую страницу на которой должен быть текст вида:

Yandex_Goog

В остальных случаях, когда добавление сайта по каким-либо причинам невозможно, будет происходить редирект. По сути Вы будете получать ту же саму стартовую страницу добавления сайта, но с одним различием — URL будет содержать какой-либо параметр, свидетельствующий об ошибке.
Чтобы отслеживать ошибки во время добавления сайта в Яндекс, Вы можете написать обработчик события onRedirect у idHTTP и отлавливать там изменения в URL — для этого достаточно проверить переменную dest. Я немного потерзал Яндекс и намеренно попробовал добавить в поиск сайты, которые 100% не добавились бы и получил следующие URL с ошибками:

URL Расшифровка ошибки
/?err=CAPTCHA_FAILD&url=vash_domen Неправильно введен код Captcha
/?err=INVALID_URL&url=vash_domen Введен некорректный URL
/?err=URL_RETURNED_WRONG_CODE&url=vash_domen Сервер недоступен, либо возвращает код статуса http, отличный от 200
/?err=HOST_IS_MIRROR_MAIN_MIRROR_IS_INDEXED&url=vash_domen&alt=www.vash_domen&main-mirror-url=vash_domen Указанный вами сайт является неглавным зеркалом сайта vash_domen.
Главное зеркало сайта уже индексируеся
/?err=URL_IS_ALREADY_INDEXED&url=vash_domen Сайт уже индексируется

Вполне возможно, что это далеко не все виды ошибок при добавлении URL, но тем не менее, чтобы уловить суть дальнейшей работы с ними — этого хватит. Теперь можно сделать так:
1. Если был редирект на одну из страниц с ошибкой, то в результат выносим текст ошибки и перезагружаем Captcha
2. Иначе выводим в результат текст об успешном добавлении.
Пишем обработчик события onRedirect:

procedure TForm3.IdHTTP1Redirect(Sender: TObject; var dest: string;
  var NumRedirect: Integer; var Handled: Boolean; var VMethod: string);
begin
  // Если есть редирект, то 100% допущена ошибка
  Error := true;
  if pos('CAPTCHA_FAILD', dest) > 0 then
    ErrorText:='Неправильно введен код Captcha'
  else if pos('INVALID_URL', dest) > 0 then
    ErrorText := 'Введен некорректный URL'
  else if pos('URL_RETURNED_WRONG_CODE', dest) > 0 then
    ErrorText:=
      'Сервер недоступен, либо возвращает код статуса http, отличный от 200'
  else if pos('HOST_IS_MIRROR_MAIN_MIRROR_IS_INDEXED', dest) > 0 then
    ErrorText := 'Указанный вами сайт является неглавным зеркалом сайта. Главное зеркало сайта уже индексируеся'
  else if pos('URL_IS_ALREADY_INDEXED', dest) > 0 then
    ErrorText:='Сайт уже индексируется';
end;

И немного дописываем обработчик отправки данных на сервер, добавив следующие строки:

Error:=false;//в самом начале обработчика
.....
if Error then
  begin
    Edit3.Text:=ErrorText;
    Button1Click(self);
  end

Теперь все готово для добавления сайта в поисковик. В случае возникновения ошибки каптча автоматичски перегрузится в TImage.
Переходим ко второй части работы с Captcha — к загрузке каптчи с файлообменника Яндекс.

3.2. Работа с Captcha файлообменника Яндекс.

Несмотря на то, что вроде бы оба сервиса относятся к Яндекс, способы формирования и выдачи каптчи различны. Чтобы доказать это, достаточно зайти на любой URL файлообменника и посмотреть сведения о каптче. Для примера я загрузил один из файлов Хронометра и получил следующего вида URL для скачивания:

http://narod.ru/disk/14395347000/Chart.p1.html

Заходим на URL и видим Captcha со следующими свойствами картинки:

Yandex Captcha файлообменника
Казалось бы, чем эта каптча отличается от предыдущей? Пусть url картинки другой, но суть та же. Ничего подобного. Ищем html-код, отвечающий за вывод Captcha на форму:

Yandex_Captcha_html

Согласитесь, есть какая-то нестыковка с тем, что выдает Firefox в свойствах? :) Всё дело в том, что в исходнике странице нам приходит только прозрачная подложка для картинки, а сами цифры генерируются скриптом уже после загрузки страницы. Соответственно применить тот же способ загрузки каптчи в свою программу Вы никак не сможете — всё время будите грузить прозрачный gif без цифр.
Для того, чтобы всё получилось как надо, обратимся снова к заголовкам запросов и попробуем найти тот запрос, который и отвечает за формирование Captcha. Выглядит он следующим образом:

GET /disk/getcapchaxml/?rnd=107 HTTP/1.1
Host: narod.ru
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
Accept: */*
Accept-Language: ru,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
X-Requested-With: XMLHttpRequest
Referer: http://narod.ru/disk/14395347000/Chart.p1.html
Cookie: DL_Session=hd190436c386c03dc9fa880395c74e62e

То есть при формировании цифр для Captcha происходит обращение к скрипту при этом задается параметр rnd. В результате нам должен вернуться какой-либо контент, содержащий необходимые данные для каптчи. Для того, чтобы проверить правильность наших рассуждений попробуем отправить точно такой же запрос, но перед этим, для пущей важности, попробуем сформировать параметр rnd точно также как это делает сам Яндекс. Пробегаемся по исходнику страницы и находим скрипт следующего содержания:

script

Красным цветом выделен тот участок кода, который и генерирует параметр rnd. То есть, сам по себе этот параметр случайное число находящееся в определенных пределах. Но, раз решили использовать запрос один-к-одному, то поступим также как Яндекс.
Начинаем писать обработчик получения Captcha и делаем первый запрос к сервису:

procedure TForm3.Button2Click(Sender: TObject);
var ResultList: TStringList;
    rnd: integer;
begin
ResultList:=TStringList.Create;
//формируем параметр rnd
Randomize;
rnd:=round(777)*777;
//отправляем запрос
ResultList.Text:=IdHTTP1.Get('http://narod.ru/disk/getcapchaxml/?rnd=');
end;

В результате переменная ResyltList будет содержать XML-документ следующего содержания:

Captcha_u_yandex

Как видите, в документе приходит всё, что нам необходимо: URL картинки и параметр key. Получим эти данные в свое пользование, для этого снова возвращаемся к обработчику и пишем код, который будет вытаскивать из документа необходимые данные и сразу же выводить Captcha на TImage. Весь обработчик будет выглядеть следующим образом:

procedure TForm3.Button2Click(Sender: TObject);
var
  ResultList: TStringList;
  rnd: Integer;
  List: TStringList;
  R: TRegExp;
  mc: MatchCollection;
  m: Match;
  sm: SubMatches;
  Stream: TMemoryStream;
  Gif: TGIFImage;
begin
  ResultList := TStringList.Create;
  // формируем параметр rnd
  Randomize;
  rnd := round(777) * 777;
  // отправляем запрос
  ResultList.Text := IdHTTP1.Get('http://narod.ru/disk/getcapchaxml/?rnd=');
  R := TRegExp.Create(self);
  R.Pattern := 'url="(.*?)">(.*?)'; // регулярное выражение для поиска Captcha
  R.Multiline := true;
  R.IgnoreCase := true;
  R.Global := true;
  mc := R.Execute(ResultList.Text) as MatchCollection;
  if mc.Count > 0 then // нашли совпадение
  begin
    m := mc[0] as Match;
    sm := m.SubMatches as SubMatches;
    Stream := TMemoryStream.Create;
    ShowMessage(sm[0]);
    IdHTTP1.Get(sm[0], Stream); // грузим капчу в поток.
    CaptchaKey:= sm[1]; // запоминаем ключ Captcha
    R := nil;
    m := nil;
    mc := nil;
    Stream.Position := 0; // устанавливаем ОБЯЗАТЕЛЬНО на ноль
    //так как каптча - это gif, то работать будем так
    GIF:=TGIFImage.Create;
    Gif.LoadFromStream(Stream);
    Image1.Picture.Assign(Gif.Bitmap); // выводим в Image
  end;
end;

В результате мы должны получить в TImage gif-рисунок, содержащий код Captcha. Переходим ко второй части работы — отправке кода на сервер. Запрос на отправку кода сервису выглядит следующим образом:

POST /disk/14395347000/Chart.p1.html HTTP/1.1
Host: narod.ru
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://narod.ru/disk/14395347000/Chart.p1.html
Cookie: DL_Session=hd190436c386c03dc9fa880395c74e62e
Content-Type: application/x-www-form-urlencoded
Content-Length: 105
key=309Bg1EyTitP9YxLiwR1Mq5KejbTvu4F&action=sendcapcha&rep=383620&installYandexBar=on&installYandexBar=on

Опять же смотрим на содержимое запроса:
key — ключ Captcha
action — операция. В нашем случае — отправка кода (sendcapcha)
rep — проверочный код
и два дополнительных параметра, которые в принципе можно и не отправлять на сервер.
В результате отправки такого запроса возможны два случая:
1. В случае отправки ошибочного кода нас вернут на туже страницу, но с новой каптчей
2. Перенаправят на страницу с прямой ссылкой на скачивание
Попробуем отправить запрос.

...
ParamList.Add('key='+CaptchaKey);
    ParamList.Add('action=sendcapcha');
    ParamList.Add('rep='+Edit2.Text);
    ParamList.Text:=IdHTTP1.Post(Edit1.Text,ParamList);
    //анализируем на наличие ошибки
    if pos('http://img.yandex.net/i/z.gif',ParamList.Text)>0 then
      begin
        Error:=true;
        ErrorText:='Ошибка ввода Captcha';
        Button2Click(self);//перегружаем картинку с каптчей
      end
    else
      begin
        //раз ошибки не было, то можно парсть страницу в поисках прямой ссылки на файл
         ....
      end;

То есть, если после запроса мы получаем страницу где есть каптча, то это признак того, что мы ошиблись при вводе кода и требуется запросить новую каптчу. Если каптча принята, то мы получим в свое распоряжение страницу с прямой ссылкой на файл. Остаётся её обнаружить на странице и выдать в качестве результата в Edit3. Кстати, обращаю Ваше внимание — при работе с файлообменником никаких редиректов не происходит, именно поэтому ошибка ввода Captcha определяется сразу же после отправки запроса, а не в обработчике onRedirect.
Ссылка на скачивание файла в исходном коде страницы выглядит следующим образом:

directlink

Пишем код для парсинга:

...
        R := TRegExp.Create(self);
        R.Pattern := 'href="(.*?)">'+Edit1.text+''; // регулярное выражение для поиска Captcha
        R.Multiline := true;
        R.IgnoreCase := true;
        R.Global := true;
        mc := R.Execute(ParamList.Text) as MatchCollection;
        if mc.Count > 0 then // нашли совпадение
          begin
            m := mc[0] as Match;
            sm := m.SubMatches as SubMatches;
            Edit3.Text:='http://narod.ru'+sm[0];//выводим результат
            R := nil;
            m := nil;
            mc := nil;
          end;
      end;

В результате в Edit3 появится прямая ссылка на скачивание либо сообщение об ошибке. В принципе, можно организовать сразу же скачивание файла, например с помощью того же компонента TDownloader, но суть нашего поста не в том, чтобы научиться качать, а в том, чтобы правильно получать каптчу и соответственно — получать положительный результат.
Кстати, если всё-таки вздумаете писать свой клиент для работы с файлообменником Яндекс, то для того, чтобы получить прямую ссылку на файл, проводить все вышеперечисленные операции не обязательно. Достаточно в запросе Get отправить вот такой User-Agent:

User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 YB/4.1.0

и сразу же получить прямую ссылку на скачивание :). Ну, а мы пока резюмируем.
Итак, сегодня мы узнали, что ссылка на картинку с Captcha может: 1. содержаться непосредственно на странице
2. генерироваться скриптом уже после загрузки исходного кода страницы.
В первой случае, для получения Captcha достаточно выполнить простой запрос и методом Get скачать картинку в поток и затем показать пользователю.
Во втором случае, для получения Captcha необходимо провести следующие операции:
1. Найти тот запрос, который выполняется при выводе каптчи на страницу
2. Определить (хотя бы примерно) как работает скрипт генерации каптчи
3. Выполнить аналогичный запрос к сервису и получить либо ссылку на картинку, либо, как в нашем случае, получить XML-документ, содержащий все необходимые данные
4. Найти запрос, отвечающий за отправку данных на сервер и выполнить аналогичным образом свой.
На самом деле сегодня я представил Вам не самый сложный из всех существующих способов загрузки Captcha и, вполне возможно, что при работе с другими сайтами Вы столкнетесь с такой ситуацией, когда вместе с ответом на запрос сервер присылает свои Cookies на основании которых генерируется/изменяется Captcha. В этом случае можно использовать в связке с idHTTP компонент idCookieManager, который поможет Вам справиться с куками и правильно отобразить/отправить Captcha.
На всякий случай, прикрепляю к посту тестовой приложение (с исходниками) на основании которого и составлялась вся статья. Думаю это поможет в случае чего более досконально разобраться с вопросами загрузки Captcha.

0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
13 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Артём Дузь
Артём Дузь
30/11/2009 21:09

Огромное спасибо за столь внятный материал. Ждем продолжения.

Максим
Максим
18/01/2010 23:00

Молодец! Я такой внятной и чёткой информации не встречал ни на одном сайте…
По вашим примерам я уже 6 программок написал — огромное спасибо и моё уважение вам!

Arrividerchi
Arrividerchi
23/03/2010 01:38

Спасибо за полезный материал, вообще вы молодец, и я уверен вашими советами и примерами уже тысячи людей пользуются, жаль, что не отписываются…
Очень много интересного узнаю всегда из статей, в этой всё хорошо, а совсем понравилось например, про обманку яши (посылку «правильного» юзер-агента), оригинально, думал яшка как-то по особенному свой тулбар палит, ан-нет )
Вобщим, лично я (не знаю как другие) рад тому, что вы вообще Есть :) И, как говорят в интернете, «пишите ещё» :) И да прибудет с вами Сила и.. и вообще всех благ! )

Shaker
Shaker
06/04/2010 16:10

у меня такая проблема. Необходимо выполнить дествия: отметить чексбокс — нажать кнопку в приложение (чужом). Приложение — Флеш. Размещено на странице сайта. Кто знает как помогите.

Seo Ghost
14/09/2010 23:55

Здравствуйте. Хотелось бы увидеть ваш пост про парсинг каптчи на подобие — http://budget.webeffector.ru/index.html. Пол недели бьюсь над этой задачей, не получается, может вы сможете решить. При каждом обновлении страниц меняется каптча, которая находится по адресу http://budget.webeffector.ru/captcha. Заранее спасибо

Seo Ghost
15/09/2010 18:12

Vlad, у меня лично проблема та же, не могу найти по какому параметру определяется какая именно каптча. Регистрация не нужна. Через браузер сделайте — пойдет, а программно. Задача довольно интересная, учитывая, что пока такой каптчи я нигде не встречал

Lenar
02/12/2010 16:06

Добрый день Влад!
Статья ваша понравилась много что понял и взял на вооружение. Спасибо
Только вот не могу реализовать тоже самое с сайтом http://gibdd.tatar.ru/rus/fines.htm
Никак не пойму как такое можно реализовать, т.к. капча постоянно меняется и ключей не выдает никаких так что я в полном недоумении. Помогите пожалуйста если сможете
 

trackback

[…] Captcha в виде картинок, генерируемых php или js-скриптами я уже говорил по-моему достаточно, поэтому сегодня рассмотрим […]

Дарья
Дарья
19/07/2014 20:04

Добрый день! подскажите и помогите, пожалуйста, не могу зайти в инстаграм, ошибка в captche. Вожу символы, которые программа предлагает, но зайти не могу, типо ошибка ввода, но ввожу все верно!! как исправить все это и вернуть свою страничку!!? Спасибо!