Предисловие:
В Беларуси стоит острая проблема с получением виз в Еврозону (т.е. Шенген). Все из-за того, что Польское посольство предоставляет так называемые мульти-визы за покупками (т.е. многократные). Регистрация производится на сайте посольства онлайн. Но вся проблема состоит в том, что свободных дат не словить. Единственный вариант — круглосуточно чекать страницу, и если появится дата — быстро «ловить» ее и заканчивать регистрацию. Т.к. свободного времени для круглосуточного чека нет, было принято решение об автоматизации данного процесса.
Сразу оговорюсь, что существуют различные скрипты, которые вылавливают свободные даты и за которые люди получают деньги. Мой скрипт не претендует на их место по быстроте, качеству и т.д. Данный скрипт был сделан только для себя, никакой коммерческой и иной выгоды я не преследовал.
Постановка задачи и входные данные:
Для начала необходимо было изучить то, как проходит процесс регистрации.
Линк на сайт посольства: 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="--$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 и считывать урл текущей капчи. При обращении на данный урл, мы получим только изображение капчи, без лишних элементов.
Теперь листинг скрипта, с комментариями:
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/
Добавить комментарий