Предисловие:
В Беларуси стоит острая проблема с получением виз в Еврозону (т.е. Шенген). Все из-за того, что Польское посольство предоставляет так называемые мульти-визы за покупками (т.е. многократные). Регистрация производится на сайте посольства онлайн. Но вся проблема состоит в том, что свободных дат не словить. Единственный вариант — круглосуточно чекать страницу, и если появится дата — быстро «ловить» ее и заканчивать регистрацию. Т.к. свободного времени для круглосуточного чека нет, было принято решение об автоматизации данного процесса.
Сразу оговорюсь, что существуют различные скрипты, которые вылавливают свободные даты и за которые люди получают деньги. Мой скрипт не претендует на их место по быстроте, качеству и т.д. Данный скрипт был сделан только для себя, никакой коммерческой и иной выгоды я не преследовал.
Постановка задачи и входные данные:
Для начала необходимо было изучить то, как проходит процесс регистрации.
Линк на сайт посольства: 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 — данный урл я и брал за входную точку, т.к. нет смысла в автоматизации предыдущих страниц (конечно, перед этим я проверил возможность входа по данному урлу с чистыми куками)
Далее мы видим капчу. Введя ее, нам дается результат — Отсутствие свободных дат.
Исходя из этих данных, мы можем сделать набросок плана нашего будущего скрипта:
Выбор инструмента
После того, как я определился с тем, что необходимо делать — стал вопрос о подходящем инструменте. Сразу хочу оговориться, я не являюсь программистом, я тестироващик. Но некоторые знания языков присутствуют.
В самом начале я хотел автоматизировать данный процесс на TestComplete. После автоматизации я столкнулся с некоторыми проблемами, основная из которых была скорость отработки скрипта, да и плюс ко всему я юзал старую версию тесткомплита 7.5, которая работает максимум с браузером Mozzila 3.5. Сами понимаете, что в таком старом браузере отображение элементов хромает, да и верстка местами едет. Поэтому на данный инструмент я забил и присмотрелся к Selenium WebDriver.
Языком написания скрипта был выбран Python. Выбор пал на него по одной только причине, я был немного знаком с данным скриптовым языком, а лезть в Java, например и изучать его не было ни времени, ни желания.
Работа с капчей
На самом деле автоматизировать данные действия не составляет особого труда, но все портит ненавистная капча. Вся проблема заключалась в том, что капчи с периодичность раз в один-два месяца менялись и поэтому не было смысла продумывать технологию разгадывания капчи (создания шаблонов, масок и т.д.). По этой причине я решил заюзать antigate.
Зарегистрировавшись там и закинув 3 доллара, я получил ресурсов на 3000 капч.
Но теперь необходимо было продумать алгоритм обработки данной капчи, отправки ее на антигейт и получения значения капчи. Выглядело это примерно так:
Для работы с антигейтом я использовал 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="--$boundaryrn";
$content.="Content-Disposition: form-data; name="method"rn";
$content.="rn";
$content.="postrn";
$content.="--$boundaryrn";
$content.="Content-Disposition: form-data; name="key"rn";
$content.="rn";
$content.="$apikeyrn";
$content.="--$boundaryrn";
$content.="Content-Disposition: form-data; name="phrase"rn";
$content.="rn";
$content.="$is_phrasern";
$content.="--$boundaryrn";
$content.="Content-Disposition: form-data; name="regsense"rn";
$content.="rn";
$content.="$is_regsensern";
$content.="--$boundaryrn";
$content.="Content-Disposition: form-data; name="numeric"rn";
$content.="rn";
$content.="$is_numericrn";
$content.="--$boundaryrn";
$content.="Content-Disposition: form-data; name="min_len"rn";
$content.="rn";
$content.="$min_lenrn";
$content.="--$boundaryrn";
$content.="Content-Disposition: form-data; name="max_len"rn";
$content.="rn";
$content.="$max_lenrn";
$content.="--$boundaryrn";
$content.="Content-Disposition: form-data; name="is_russian"rn";
$content.="rn";
$content.="$is_russianrn";
$content.="--$boundaryrn";
$content.="Content-Disposition: form-data; name="file"; filename="capcha.$ext"rn";
$content.="Content-Type: $conttypern";
$content.="rn";
$content.=$body."rn";
$content.="--$boundary--";
$poststr="POST http://$sendhost/in.php HTTP/1.0rn";
$poststr.="Content-Type: multipart/form-data; boundary=$boundaryrn";
$poststr.="Host: $sendhostrn";
$poststr.="Content-Length: ".strlen($content)."rnrn";
$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,"rnrn")+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 и считывать урл текущей капчи. При обращении на данный урл, мы получим только изображение капчи, без лишних элементов.
Теперь листинг скрипта, с комментариями:
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) дописать авторегистратор, а не просто чекер даты
и т.д.
Послесловие
Еще раз хочу оговориться, что данный скрипт слабоват, но результат от него был. Также стоит заметить, что в посольстве далеко не дураки сидят и часто меняют капчу, поэтому необходимо будет переписывать скрипт под новые условия.
Автор: dgu_minsk