Скрипт проверки наличия свободных дат в посольстве

от автора

Предисловие:

В Беларуси стоит острая проблема с получением виз в Еврозону (т.е. Шенген). Все из-за того, что Польское посольство предоставляет так называемые мульти-визы за покупками (т.е. многократные). Регистрация производится на сайте посольства онлайн. Но вся проблема состоит в том, что свободных дат не словить. Единственный вариант — круглосуточно чекать страницу, и если появится дата — быстро «ловить» ее и заканчивать регистрацию. Т.к. свободного времени для круглосуточного чека нет, было принято решение об автоматизации данного процесса.
Сразу оговорюсь, что существуют различные скрипты, которые вылавливают свободные даты и за которые люди получают деньги. Мой скрипт не претендует на их место по быстроте, качеству и т.д. Данный скрипт был сделан только для себя, никакой коммерческой и иной выгоды я не преследовал.

Постановка задачи и входные данные:

Для начала необходимо было изучить то, как проходит процесс регистрации.
Линк на сайт посольства: by.e-konsulat.gov.pl/
На главной странице видим два селекта, с выбором страны и города. Выбрав необходимые параметры нас редиректит на by.e-konsulat.gov.pl/Informacyjne/Placowka.aspx?IDPlacowki=94.
Потом выбираем из меню «Национальная Виза — Зарегистрируйте бланк» и переходим на by.e-konsulat.gov.pl/Uslugi/RejestracjaTerminu.aspx?IDUSLUGI=1&IDPlacowki=94 — данный урл я и брал за входную точку, т.к. нет смысла в автоматизации предыдущих страниц (конечно, перед этим я проверил возможность входа по данному урлу с чистыми куками)
Далее мы видим капчу. Введя ее, нам дается результат — Отсутствие свободных дат.
Исходя из этих данных, мы можем сделать набросок плана нашего будущего скрипта:
image

Выбор инструмента

После того, как я определился с тем, что необходимо делать — стал вопрос о подходящем инструменте. Сразу хочу оговориться, я не являюсь программистом, я тестироващик. Но некоторые знания языков присутствуют.
В самом начале я хотел автоматизировать данный процесс на TestComplete. После автоматизации я столкнулся с некоторыми проблемами, основная из которых была скорость отработки скрипта, да и плюс ко всему я юзал старую версию тесткомплита 7.5, которая работает максимум с браузером Mozzila 3.5. Сами понимаете, что в таком старом браузере отображение элементов хромает, да и верстка местами едет. Поэтому на данный инструмент я забил и присмотрелся к Selenium WebDriver.
Языком написания скрипта был выбран Python. Выбор пал на него по одной только причине, я был немного знаком с данным скриптовым языком, а лезть в Java, например и изучать его не было ни времени, ни желания.

Работа с капчей

На самом деле автоматизировать данные действия не составляет особого труда, но все портит ненавистная капча. Вся проблема заключалась в том, что капчи с периодичность раз в один-два месяца менялись и поэтому не было смысла продумывать технологию разгадывания капчи (создания шаблонов, масок и т.д.). По этой причине я решил заюзать antigate.
Зарегистрировавшись там и закинув 3 доллара, я получил ресурсов на 3000 капч.
Но теперь необходимо было продумать алгоритм обработки данной капчи, отправки ее на антигейт и получения значения капчи. Выглядело это примерно так:
image
Для работы с антигейтом я использовал API данного сервиса. Пришлось развернуть на локальной машине PHP server, не заморачиваясь выбор пал на Denwer. Создал локальный сайт test1.ru и закинул туда php страницу для работы с API сервиса.
Листинг данной страницы

<?php function recognize(     $filename,     $apikey,     $is_verbose = true,     $sendhost = "antigate.com",     $rtimeout = 10,     $is_phrase = 0,     $is_regsense = 1,     $is_numeric = 0,     $min_len = 4,     $max_len = 4,     $is_russian = 1) {     if (!file_exists($filename))     {         if ($is_verbose) echo "<b>file $filename not found</b>";         return false;     }     $fp=fopen($filename,"r");     if ($fp!=false)     {         $body="";         while (!feof($fp)) $body.=fgets($fp,1024);         fclose($fp);         $ext=strtolower(substr($filename,strpos($filename,".")+1));     }     else     {         if ($is_verbose) echo "<b>could not read file $filename<b>";         return false;     }      if ($ext=="jpg") $conttype="image/pjpeg";     if ($ext=="gif") $conttype="image/gif";     if ($ext=="png") $conttype="image/png";       $boundary="---------FGf4Fh3fdjGQ148fdh";      $content="--$boundary\r\n";     $content.="Content-Disposition: form-data; name=\"method\"\r\n";     $content.="\r\n";     $content.="post\r\n";     $content.="--$boundary\r\n";     $content.="Content-Disposition: form-data; name=\"key\"\r\n";     $content.="\r\n";     $content.="$apikey\r\n";     $content.="--$boundary\r\n";     $content.="Content-Disposition: form-data; name=\"phrase\"\r\n";     $content.="\r\n";     $content.="$is_phrase\r\n";     $content.="--$boundary\r\n";     $content.="Content-Disposition: form-data; name=\"regsense\"\r\n";     $content.="\r\n";     $content.="$is_regsense\r\n";     $content.="--$boundary\r\n";     $content.="Content-Disposition: form-data; name=\"numeric\"\r\n";     $content.="\r\n";     $content.="$is_numeric\r\n";     $content.="--$boundary\r\n";     $content.="Content-Disposition: form-data; name=\"min_len\"\r\n";     $content.="\r\n";     $content.="$min_len\r\n";     $content.="--$boundary\r\n";     $content.="Content-Disposition: form-data; name=\"max_len\"\r\n";     $content.="\r\n";     $content.="$max_len\r\n";     $content.="--$boundary\r\n";     $content.="Content-Disposition: form-data; name=\"is_russian\"\r\n";     $content.="\r\n";     $content.="$is_russian\r\n";     $content.="--$boundary\r\n";     $content.="Content-Disposition: form-data; name=\"file\"; filename=\"capcha.$ext\"\r\n";     $content.="Content-Type: $conttype\r\n";     $content.="\r\n";     $content.=$body."\r\n";     $content.="--$boundary--";       $poststr="POST http://$sendhost/in.php HTTP/1.0\r\n";     $poststr.="Content-Type: multipart/form-data; boundary=$boundary\r\n";     $poststr.="Host: $sendhost\r\n";     $poststr.="Content-Length: ".strlen($content)."\r\n\r\n";     $poststr.=$content;       $fp=fsockopen($sendhost,80,$errno,$errstr,30);     if ($fp!=false)     {         fputs($fp,$poststr);         $resp="";         while (!feof($fp)) $resp.=fgets($fp,1024);         fclose($fp);         $result=substr($resp,strpos($resp,"\r\n\r\n")+4);     }     else     {         if ($is_verbose) echo "<b>could not connect to anti-captcha</b>";         if ($is_verbose) echo "<b>socket error: $errno ( $errstr )</b>";         return false;     }      if (strpos($result, "ERROR")!==false or strpos($result, "<HTML>")!==false)     {         if ($is_verbose) echo "<b>server returned error: $result</b>";         return false;     }     else     {         $ex = explode("|", $result);         $captcha_id = $ex[1];         if ($is_verbose) echo "<b>$captcha_id</b>";     } }  $text=recognize("captcha.png","Здесь должен быть ключ для работы с сервисом",true,"antigate.com"); ?> 

Я не стал досконально разбираться, что к чему, но единственное, что я выставил — это следующие настройки:

    $is_phrase = 0, //является ли ваша капча фразой     $is_regsense = 1, //регистро зависимая или нет?     $is_numeric = 0, //Состоит из цифр?     $min_len = 4, //минимальная длинна     $max_len = 4, //максимальная длинна     $is_russian = 1 //есть ли русские символы 

В итоге нам необходимо поместить изображение captcha.png в директорию, где находится index.php и перейти по урлу test1.ru
В итоге капча полетит на сервис, когда она разгадается нам придет ее id, обрамленный в тег b, либо придет какая-нибудь ошибка, которая отобразиться.
Останется дело за малым, только забрать значение капчи со страницы по ее id.

Создание скрипта

Т.к. все предварительные подготовки сделаны, то можем приступать непосредственно к написанию скрипта.
Работать мы будем с двумя открытыми окнами Firefox. Т.к. в одном окне у нас будет происходить чек дат, а во втором все работы относительно капчи. Для отображения капчи в новом окне, мы просто будем находить сам элемент на странице по id и считывать урл текущей капчи. При обращении на данный урл, мы получим только изображение капчи, без лишних элементов.
image

Теперь листинг скрипта, с комментариями:

from selenium import webdriver from selenium.webdriver.support.ui import WebDriverWait import time  driver = webdriver.Firefox() #запускаем первое окно (основное) add_driver = webdriver.Firefox() #запускаем дополнительное окно для работы с капчей driver.get("https://by.e-konsulat.gov.pl/Uslugi/RejestracjaTerminu.aspx?IDUSLUGI=1&IDPlacowki=94") #переходим на наш урл captcha_url = driver.find_element_by_id('c_uslugi_rejestracjaterminu_ctl00_cp_botdetectcaptcha_CaptchaImage').get_attribute('src') #Находим элемент капчи по его id и считываем урл, по которому будет доступно изображение  add_driver.get(captcha_url) #во втором окошке открываем нашу капчу add_driver.set_window_size(50,200) #делаем ресайз окна браузера, чтобы сделать скриншот именно капчи, без лишних серых полей add_driver.get_screenshot_as_file('captcha.png') #делаем скриншот окна, в итоге на нашем скриншоте будет только капча и сохраняем его в директорию локального сайта test1.ru, т.к. у меня скрипт лежит тамже, то путь не писал add_driver.get(http://test1.ru) #переходим на урл нашей странички, для работы с антигейтом captcha_id = add_driver.find_element_by_xpatch('//b') #находим элемент, который обрамлен в тег b, подразумевая ,что там хранится значение id капчи count = false while (count = false)     add_driver.get('http://antigate.com/res.php?key=Ключ для работы с антигейтом&action=get&id=" + captcha_id)     captcha_complete = add_driver.find_element_by_xpatch('//pre').text # находим наше значение (на антигейте оно обрамлено в тег pre)     if (captcha_complete.find('ERROR') >= 0) #проверяем, выскочила ли ошибка         time.sleep(5) #спим 5 секунд     else         count = true #выходим из цикла проверки # теперь значение нашей капчи содержится в переменной  captcha_complete, его и вводим в инпут  driver.find_element_by_id('ctl00_cp_BotDetectCaptchaCodeTextBox').send_keys(captcha_complete) #находим наш инпут и вводим значение капчи driver.find_element_by_id('ctl00_cp_btnDalej').click() #находим кнопку далее и кликаем на нее result = driver.find_element_by_id('ctl00_cp_lblBrakTerminow').text if (result.find('Отсутствие') >= 0)     print('Нет даты') else     print('Дата есть') 
Будущие улучшения

Основа готова, наш скрипт переходит на страницу, получает капчу, распознает ее через сервис распознавания, вводит капчу, кликает далее и проверяет наличие даты. Для себя я сделал следующее — загнал все это действие в цикл while (true) и чекал сайт, пока не словилась дата (также я добавил отправку письма на мыло, в случае положительного результата). Доработок по скрипту конечно же можно произвести много, например:
1) поставить проверку на ошибки и исходя из ошибок предпринимать различные действия
2) поставить проверку на неправильную капчу и отправку репорта на антигейт (пожаловаться на плохого работника)
3) дописать авторегистратор, а не просто чекер даты
и т.д.

Послесловие

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

ссылка на оригинал статьи http://habrahabr.ru/post/190870/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *