Недавно обратил внимание, что в статьях Хумана есть много советов КАК делать скрипты, но мало объяснений ПОЧЕМУ лучше подходит тот или иной способ. Попробую исправить это упущение на примере авторегистратора для мыл qip.ru, который и для личного пользования пригодится.
Как обычно, всё начинается с создания нового скрипта. Конечно, есть несколько способов начать работу иным способом, но их мы рассматривать не будем.
В стандартном шаблоне присутствуют строки команд, направляющих наш браузер на Гугл. Вы можете легко проверить это, просто запустив скрипт. И легко догадаться, что переход этот выполняется из-за строки $browser->navigate(«http://www.google.com»); Поскольку цель моя – квип, вставляю qip.ru в строку вместо гугла и перехожу на этот сайт для дальнейшего изучения.
Регистрация скрывается в форме, появляющейся после нажатия ссылки «Войти на сайт». Через меню, открывающемся по правому клику на нужном элементе, добавляю в скрипт строку $anchor->click_by_inner_text(‘Войти на сайт’,true); .
На выбор есть ещё три:
1 2 3 |
$anchor->click_by_number(0); $anchor->click_by_href("http://qip.ru/login",true); $anchor->click_by_atribute("name","",true); |
Первый вариант использовать можно, но если в структуре сайта произойдут мелкие изменения и нужная нам ссылка изменит свой порядковый номер, то скрипт перестанет работать.
Второй надёжен примерно так же, как и выбранный мной, так что тут всё зависит от личных предпочтений. Просто если я вижу надпись «Войти на сайт» в функции, то смогу поискать её глазами на странице. Для того же, чтобы найти ссылку «http://qip.ru/login», необходимо навести на неё курсор мыши. Лишние движения.
По той же причине отпадает и третий вариант: click_by_atribute. Лишняя возня. В сложных ситуациях эта функция попросту незаменима, но сейчас не тот случай.
Через тоже самое меню, но уже выдаваемое для ссылки с текстом «Регистрация», добавляю функцию $anchor->click_by_inner_text(‘Регистрация’,true); Запустим получившийся скрипт для теста (от слова «тестировать», а не «тесто»)
Теперь перед нашим взором предстала регистрационная форма с необходимыми для заполнения полями, которых вы, уверен, повидали уже сотни. Если не тысячи. На первый взгляд форма вполне обыкновенна и проста. Надеюсь, никаких сюрпризов в ней не припрятано.
Кстати, пока не забыл, нужно добавить функцию $browser->wait_for(); после $anchor->click_by_inner_text(‘Регистрация’,true); Она приостановит выполнение следующей после неё части скрипта до момента полной прогрузки страницы, а значит он не будет работать «в холостую».
Начинается самое интересное. Форму нужно заполнять данными, которые каждый раз будут новыми. Зачем — объяснять не требуется, надеюсь. Через тот-же правый клик на всех инпутах, составляю список функций для работы с ними. Только функция должна содержать не click_by (что значить «кликнуть по…»), а set_value_by («установить значение по…»). Тогда передаваемая нами в функцию информация будет заполнять инпуты сайта.
Стоп. При первом-же взгляде на предлагаемые варианты функций, бросается в глаза слишком длинное имя для инпута «Пользователь». Оно выглядит как случайный набор букв и цифр, и вполне возможно, что это имя меняется при каждом обновлении страницы. Следовательно, вариант set_value_by_name в данной ситуации отпадает. Хотя минутку… Функция set_value_by_name может использовать для поиска не только атрибут “name” но и “id”. На всякий случай стоит проверить, что за айди присвоено нужным нам инпутам. Проще и быстрее всего это сделать через ДОМ-модель, которая находится в выпадающем меню «Отладка».
В верхней части таблицы «ДОМ-модель» есть закладки, отвечающие за разные типы объектов. Нас интересуют инпуты, значит соответствующую закладку и нужно открыть. Проверял не зря – id нужного поля действительно имеет обычное и наверняка статичное название. Теперь, сверяясь с ДОМ-моделью, легко добавить в скрипт функции для работы со всеми нужными инпутами.
Получается вот такой список:
1 2 3 4 |
$input->set_value_by_name("reg_username", ""); $input->set_value_by_name("reg_pwd",""); $input->set_value_by_name("reg_pwd2",""); $input->set_value_by_name("reg_email",""); |
Как можно понять из пояснительного текста на странице, поля «E-mail», «Фамилия», «Имя», «Отчество» и «Город» не обязательны для заполнения, но что-то из них нужно вбить для возможной надобности восстановить пароль. Естественно, у нас подобной необходимости не возникнет, но квип об этом не знает, а потому будет стоять на своём. Попробую указывать емейлы и имя с фамилией.
Как заполнять нужные поля мы разобрались. Теперь займёмся тем, ЧТО будет в них вбиваться. Обычно подобными задачами занимается персональный Хумановский бредогенератор — объект $submitter. В его базе присутствуют списки имён, фамилий, улиц и т.д., а так же функции генерации случайного текста, чисел и ников. Они-то и будут придумывать логины, пароли и фейковые мыла.
После добавления функций из сабмиттера, скрипт приобрёл следующий вид:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// переход на сайт $browser->navigate("qip.ru"); $browser->wait_for(); // переход в форму регистрации $anchor->click_by_inner_text("Войти на сайт",true); $anchor->click_by_inner_text("Регистрация",true); $browser->wait_for(); // создание случайных данных для профиля $user = $submitter->generate_random_text(rand(8,12),1); $pass = $submitter->generate_random_text(rand(6,10),1); // заполнение инпутов $input->set_value_by_name("reg_username",$user); $input->set_value_by_name("reg_pwd",$pass); $input->set_value_by_name("reg_pwd2",$pass); $input->set_value_by_name("reg_email",$submitter->generate_random_text(rand(6,10),1)."@mail.ru"); |
Как видите, функция generate_random_text принимает на вход два параметра:
Количество знаков в генерируемом тексте. В данном случае я поставил rand(8,12), что будет выдавать разные числа от 8 и до 12.
Тип генерируемого текста. 1 – английские буквы 2 – русские.
Получившееся можно запустить и полюбоваться, но это ещё не всё. Следующими на очереди будут раскрывающиеся листбоксы, предлагающие выбрать дату рождения пользователя. Тут всё так-же просто, как и прежде: последовательно кликаем правой кнопкой мышки на каждый и добавляем функцию select_num_value_by_name для всех трёх. Эта функция выберет значение в выпадающей менюшке листбокса по его (значения) номеру. Так будет значительно проще создавать случайные даты рождения, в чём вы сможете убедиться чуть позже.
В итоге, в конец скрипта добавились следующие три строки:
1 2 3 |
$listbox->select_num_value_by_name("b_day","1"); $listbox->select_num_value_by_name("b_month","1"); $listbox->select_name_by_inner_name("b_year","1"); |
Единицы в качестве второго аргумента каждой функции поставил я. Просто для проверки того, как сработает функция. После запуска скрипта с этими изменениями день и месяц рождения выставились на «1» и «январь» соотвественно, а вот год остался без изменений. Попробую 2000. Эврика!
Поскольку даты рождения нужны случайные, воспользуемся спасительной функцией rand(); уже появлявшейся ранее. С ней нужные строки приобрели следующий вид:
1 2 3 |
$listbox->select_num_value_by_name("b_day", rand(1,28)); $listbox->select_num_value_by_name("b_month", rand(1,12)); $listbox->select_name_by_inner_name("b_year", rand(1970,2001)); |
Как видите, у первой строки крайнее число 28. Это сделано из-за кастрированного месяца февраля, который тоже может выпасть. Если захотеть, то можно высчитывать и високосные годы, и месяцы с 30 или 31 днём, но всё упирается в популярный вопрос «Оно нам надо?»
Следующие на очереди два обязательных радиобокса (крупная такая точка для тырканья мышкой), которые, в теории, должны определить наличие Y-хромосомы у пользователя. К счастью, нам верят на слово, так что тут тоже можно использовать случайный выбор одного из предложенных вариантов.
Правый щелчок мышью на первом (мужском) радиобоксе, помимо кучи бесполезных функций, отображает его номер: 0. А второй (женский) оказывается номером 1. Такой расклад очень подходит для следующей функции, которую мы и добавим в скрипт:
1 |
$radiobox->set_checked_by_number(rand(0,1),true); |
В начале текста я говорил, что клики по номеру лучше не использовать, но тут выгода от данного действия сильно сокращает время на поиски альтернативного варианта, а значит принимается без возражений.
Последнее обязательное поле – каптча, якобы призванная стоять на фейсконтроле и защищать ресурс от посягательств роботов. Святая наивность! Единственное, что удерживает роботов от порабощения этого мира – оно им нафиг не нужно. Как и умение распознавать каптчи, в принципе. Поэтому, сначала мы встроим в скрипт ручную распознавалку, а позднее подключим антикаптчу с трудолюбивыми индусами.
Чтобы отправить каптчу на распознание или показать её в отдельном диалоговом окне, нужно сохранить её картинку на жёсткий диск. Поэтому обращайте внимание на функции со словами «save_to_file_by», когда будете смотреть менюшку после ставшего уже привычным правого клика по картинке.
Нужных функций на выбор три:
1 2 3 |
$image->save_to_file_by_number(0,"C:\tmp.jpeg"); $image->save_to_file_by_name("img_captcha","C:\tmp.jpeg"); $image->save_to_file_by_url("http://qip.ru/api/captcha?n=register&k=1324745167","C:\tmp.jpeg",true); |
Первая. Картинка на странице единственная, и вполне вероятно что так оно и останется ещё долго. Можно использовать без колебаний.
Вторая. Название картинки не выглядит генерирующимся случайно, так что использование этой функции тоже вполне оправдано.
Третья. Цифры в урле картинки смущают. При желании, можно сохранять картинку и этой функцией, но часть урла придётся отрезать. Функция будет выглядеть примерно так: $image->save_to_file_by_url(‘http://qip.ru/api/captcha?’,’C:\tmp.jpeg’,false); Помимо обрезки урла, я сменил третий аргумент с true на false. Это значит, что путь к картинке указан не полностью, и функция будет искать первое похожее совпадение.
После строки сохранения открываем меню «Добавить код» (альт+контрол+вправо, если вы не смотрели видео на сайте программы) и втыкаем из объекта $app функцию dlg_captcha_from_url. В качестве урла указываем ‘C:\tmp.jpeg’ из предыдущей функции. Можно запустить и проверить что будет. Хотя нет, один момент. То, что будет возвращать функция $app->dlg_captcha_from_url, нужно поместить в переменную. К примеру, $cap. А потом вставлять содержимое этой переменной в поле ввода текста с каптчи. Если вы уже запутались во всех этих пояснениях, то вот текст скрипта, что должен у нас получиться к этому моменту:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// переход на сайт $browser->navigate("qip.ru"); $browser->wait_for(); // переход в форму регистрации $anchor->click_by_inner_text("Войти на сайт",true); $anchor->click_by_inner_text("Регистрация",true); $browser->wait_for(); // создание случайных данных для профиля $user = $submitter->generate_random_text(rand(8,12),1); $pass = $submitter->generate_random_text(rand(6,10),1); // заполнение инпутов $input->set_value_by_name("reg_username",$user); $input->set_value_by_name("reg_pwd",$pass); $input->set_value_by_name("reg_pwd2",$pass); $input->set_value_by_name("reg_email",$submitter->generate_random_text(rand(6,10),1)."@mail.ru"); // выбор случайного дня рождения $listbox->select_num_value_by_name("b_day", rand(1,28)); $listbox->select_num_value_by_name("b_month", rand(1,12)); $listbox->select_name_by_inner_name("b_year", rand(1970,2001)); // выбор пола $radiobox->set_checked_by_number(rand(0,1),true); // каптча $image->save_to_file_by_name("img_captcha","C:\tmp.jpeg"); $cap = $app->dlg_captcha_from_url("C:\tmp.jpeg"); $input->set_value_by_name("captchan",$cap); |
Можете запустить это дело пару раз, осознать величие происходящего, проникнуться моментом и вернуться к дальнейшей работе.
Незаполненными остались поля для имени, фамилии и отчества. Листбокс «Секретный вопрос» я решил сейчас не трогать, но при желании можете сделать скрипт для него сами. Проще всего будет сделать выбор варианта «Девичья фамилия матери» и заполнение появившегося поля случайной женской фамилией из объекта submitter.
Помимо инпутов для ФИО, остался один нюанс. Поле для ввода логина выдаёт подсказки, занят ли логин, или свободен, если текст набирать вручную. Но мы-то вставляем нужные данные в страницу напрямую, без помощи таких примитивных средств как ручной набор. Потому форме нужно отдельно отправить событие, будто мы честно и благородно, портя зрение и зарабатывая сколиоз, трудолюбиво и аккуратно впечатываем интересующий нас логин пальчиками. Делается это с помощью функций, в которых присутствуют слова send_event.
Только перед этим нужно разобраться, какое именно событие ожидает инпут от пользователя. Узнать это не так сложно — просто загляните снова в ДОМ модель, в раздел инпутов. Для событий, присвоенных элементам, есть отдельная колонка. Самая последняя, под названием «events». К нашему инпуту привязан лишь один эвент — «onkeyup». Посылаем его с помощью функции $input->send_event_by_name(‘reg_username’,’onkeyup’);
Хм. Не срабатывает. Попробую $input->send_event_by_number(2,’onkeyup’); Так работает. Выяснилось, что хоть функция $input->set_value_by_name и работает, помимо имени, с ID элемента, $input->send_event_by_name принимает только имя. Печальное открытие, но ничего страшного – есть равнозначная альтернатива. Функция $input->send_event_by_atribute(«id»,»reg_username»,true,’onkeyup’); делает именно то, чего мы и хотели.
Начинается самый сложный к восприятию момент. Мы должны научить скрипт проверять наличие текста «Логин занят.» на странице и соответственно реагировать.
Сначала поиск. Текущее содержимое страницы в текстовом виде можно получить функцией $webpage->get_body(); Где-то в этом содержимом и будет болтаться искомая нами фраза, обнаружить которую можно с помощью функции «чистого» PHP strpos. Она ищет позицию вхождения текста, и если найдёт – значит, текст присутствует. А если нет – всё в порядке и логин доступен.
Выглядеть эта функция будет так: strpos ($webpage->get_body(), «Логин занят»); В аргументах: 1) Где искать 2) Что искать.
Но мало найти текст, нужно также определиться с последующими действиями. А они предполагают замену уже зарегистрированного кем-то логина на новый. Для этой цели лучше всего подойдёт цикл while, на русском означающий что-то вроде «До тех пор…». То есть, содержимое цикла будет выполняться до тех пор, пока условие, указанное в начале, не будет нарушено:
1 2 3 4 5 6 7 8 9 |
//проверка доступности логина while (strpos ($webpage->get_body(), "Логин занят") != 0) { $user = $submitter->generate_random_text(rand(8,12),1); $input->set_value_by_name("reg_username",$user); $input->send_event_by_atribute("id","reg_username",true,"onkeyup"); sleep(5); } |
Вы можете заметить, что в конце цикла у меня стоит sleep(5) – пауза в пять секунд. Это примерное время, нужное форме, чтобы сравнить новый логин со своей базой и выдать ответ, свободен ли он. Если мы не выдержим паузу, ответ появится слишком поздно и наша проверка доступности логина не сработает.
Итак, с проверкой закончили. Если сомневаетесь с местом, куда нужно впихнуть получившийся код, смело ставьте его сразу после ввода каптчи.
На очереди имя и фамилия. Отчеств в субмиттере, к сожалению, нет, но они и не обязательны.
В начале скрипта есть строка, отвечающая за пол пользователя. Значит, для пущей правдоподобности имя и фамилия должны соответствовать этому выбору. Кстати, ту строку придётся изменить в соответствии с текущими потребностями. Из одной $radiobox->set_checked_by_number(rand(0,1),true); получится две:
1 2 3 |
//выбор пола $s = rand(0,1); $radiobox->set_checked_by_number($s,true); |
После этого, в зависимости от содержимого переменной $s, можно создать вставку фамилий нужного пола. Это будет выглядеть примерно так:
1 2 3 4 5 |
// вставка имени и фамилии if ($s == 1) $sex = "woman"; else $sex = "man"; $input->set_value_by_name("last_name",$submitter->generate_random_second_name("RU",$sex)); $input->set_value_by_name("first_name",$submitter->generate_random_name("RU",$sex)); |
Судя по всему, скрипт практически готов. Осталось лишь добавить клик по кнопке «Регистрация», что будет отправлять введённые данные на сервер. Ставший уже привычным клик правой кнопкой и функция $button->click_by_name(‘register’); добавляется в скрипт… И не срабатывает. Снова лезем в ДОМ модель, на этот раз закладка «button». Так и есть, нужно добавить эвент $button->send_event_by_name(‘register’,’onclick’); Теперь вставим после всего этого функцию $browser->wait_for(); и порядок.
Запустив скрипт несколько раз можно заметить, что пользы от него как-то маловато. Да, он регистрирует аккаунты (а может уже написал вам что-то вроде «Слишком много регистраций с одного IP»), но результат его деятельности никуда не сохраняется. То есть, у нас на руках не остаётся логинов с паролями, без которых весь этот процесс лишён смысла. Исправим.
Процесс сохранения текста в файл прост и незамысловат. Нужна лишь функция $textfile->add_string_to_file($file,$str,$timeout); которая заботливо запишет все нужные нам данные, если её правильно настроить. Вместо аргумента $file необходимо указать путь к сохраняемому файлу. Если файла не существует, функция его создаст. $str – строка, которую мы сохраняем. Помните, мы добавляли в переменные $user и $pass результат работы бредогенератора? Они-то нам и нужны. Третий аргумент – время, в течение которого функция будет пытаться записать файл. С нашими объёмами текста хватит и тысячной секунды, а я обычно ставлю там десятку, с запасом. Заполненная функция выглядит примерно так:
1 |
$textfile->add_string_to_file("C:\qip.txt",$user."||".$pass."\r\n",10); |
Смело вставляйте её в самом конце скрипта, сразу за нажиманием кнопки «Регистрация». Постепенно мой скрипт приобрёл следующий вид:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
// очистка кэша и куков $browser->clear_cache(); $browser->clear_cookies("",true); // переход на сайт $browser->navigate("qip.ru"); $browser->wait_for(); // переход в форму регистрации $anchor->click_by_inner_text("Войти на сайт",true); $anchor->click_by_inner_text("Регистрация",true); $browser->wait_for(); // создание случайных данных для профиля $user = $submitter->generate_random_text(rand(8,12),1); $pass = $submitter->generate_random_text(rand(6,10),1); // заполнение инпутов $input->set_value_by_name("reg_username",$user); $input->send_event_by_atribute("id","reg_username",true,"onkeyup"); $input->set_value_by_name("reg_pwd",$pass); $input->set_value_by_name("reg_pwd2",$pass); $input->set_value_by_name("reg_email",$submitter->generate_random_text(rand(6,10),1)."@mail.ru"); // выбор случайного дня рождения $listbox->select_num_value_by_name("b_day", rand(1,28)); $listbox->select_num_value_by_name("b_month", rand(1,12)); $listbox->select_name_by_inner_name("b_year", rand(1970,2001)); // выбор пола $s = rand(0,1); $radiobox->set_checked_by_number($s,true); // вставка имени и фамилии if ($s == 1) $sex = "woman"; else $sex = "man"; $input->set_value_by_name("last_name",$submitter->generate_random_second_name("RU",$sex)); $input->set_value_by_name("first_name",$submitter->generate_random_name("RU",$sex)); // каптча $image->save_to_file_by_name("img_captcha","C:\tmp.jpeg"); $cap = $app->dlg_captcha_from_url("C:\tmp.jpeg"); $input->set_value_by_name("captchan",$cap); // пороверка логина на занятость while (strpos ($webpage->get_body(), "Логин занят") != 0) { // создание другого логина $user = $submitter->generate_random_text(rand(8,12),1); $input->set_value_by_name("reg_username",$user); $input->send_event_by_atribute("id","reg_username",true,"onkeyup"); sleep(5); } // сабмит формы $button->click_by_name("register"); $button->send_event_by_name("register","onclick"); $browser->wait_for(); // сохраняем данные $textfile->add_string_to_file("C:\qip.txt",$user."||".$pass."\r\n",10); |
В самом начале скрипта вы можете заметить две новые строчки – очистку кэша и куков. Ещё к ним неплохо бы добавить функцию установки прокси, поскольку квип, как оказалось, весьма капризен, и не позволяет регистрировать тучи аккаунтов с одного айпи. С прокси возможно два варианта: 1) на каждую проксю выполнять одну регистрацию и брать следующую 2) регистрировать аккаунты до появления надписи «Слишком много регистраций». Первое организовывается циклом for или foreach:
1 2 3 4 5 6 7 8 9 10 |
$proxy = file ("proxy.txt"); for($i=0;$i<count($proxy);$i++) { // очистка кэша и куков, подключение прокси $browser->clear_cache(); $browser->clear_cookies("",true); $browser->enable_proxy("all connections",$proxy[$i]); /*Весь остальной скрипт*/ } |
Второе требует проверки наличия на странице предупреждающего сообщения. Этим мы уже занимались на примере с занятостью логинов чуть выше, но тут будет модификация:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$proxy = file ("proxy.txt"); for($i=0;$i<count($proxy);$i++) { // очистка кэша и куков, подключение прокси $browser->clear_cache(); $browser->clear_cookies("",true); $browser->enable_proxy("all connections",$proxy[$i]); /*Весь остальной скрипт*/ if (strpos ($webpage->get_body(), "Много регистраций") == 0) { $textfile->add_string_to_file("C:\qip.txt",$user."||".$pass."\r\n",10); $i--; } } |
Такой подход позволит регистрировать аккаунты до появления надписи, менять прокси и продолжать регистрацию уже с нового айпи. Модификация заключается в том, что теперь данные логина и пароля будут записываться лишь при успешной регистрации. Её неплохо бы добавить и в первый вариант, но это теперь можно сделать самостоятельно. Дальнейшие пояснения будут даваться для второго варианта.
Снова проверяя получившийся скрипт в действии я неправильно ввёл одну каптчу, но это было засчитано программой за успешную регистрацию, потому данные записались в файлик и работа пошла по новой. Сие натолкнуло на мысль, что надобно бы сделать проверку и для неправильно введённой каптчи. На примере наших прошлых достижений можно разобраться и наваять что-то такое:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
while (strpos ($webpage->get_body(), "Неверный код подтверждения") != 0) { echo "Ай-ай! Ошибочка. Попробуем снова каптчу ввести<br>"; // каптча $image->save_to_file_by_name("img_captcha","C:\tmp.jpeg"); $cap = $app->dlg_captcha_from_url("C:\tmp.jpeg"); $input->set_value_by_name("captchan",$cap); // сабмит формы $button->click_by_name("register"); $button->send_event_by_name("register","onclick"); sleep(3); $browser->wait_for(); } |
Поскольку после ошибки сайт показывает новую каптчу, мы снова её сохраняем. После этого вводим и самбитим. Если вдруг каптча будет снова введена неправильно, цикл while будет срабатывать, пока не пропадёт надпись «Неверный код подтверждения». То есть, до успешного распознания.
На этом скрипт представляется мне готовым. Возможно, в процессе его использования всплывут какие-то ещё нюансы, но сейчас я этого не знаю. Привожу его полный вариант со всеми проверками и несколькими косметическими поправками:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
$xhe_host ="127.0.0.1:7010"; require("../Templates/xweb_human_emulator.php"); $proxy = file ("proxy.txt"); // цикл пробежки по всем проксям в списке for($i=0;$i<count($proxy);$i++) { // очистка кэша и куков, подключение прокси $browser->clear_cache(); $browser->clear_cookies("",true); $browser->enable_proxy("all connections",$proxy[$i]); // переход на сайт $browser->navigate("qip.ru"); $browser->wait_for(); // переход в форму регистрации $anchor->click_by_inner_text("Войти на сайт",true); $anchor->click_by_inner_text("Регистрация",true); $browser->wait_for(); // создание случайных данных для профиля $user = $submitter->generate_random_text(rand(8,12),1); $pass = $submitter->generate_random_text(rand(6,10),1); // заполнение инпутов $input->set_value_by_name("reg_username",$user); $input->send_event_by_atribute("id","reg_username",true,"onkeyup"); $input->set_value_by_name("reg_pwd",$pass); $input->set_value_by_name("reg_pwd2",$pass); $input->set_value_by_name("reg_email",$submitter->generate_random_text(rand(6,10),1)."@mail.ru"); // выбор случайного дня рождения $listbox->select_num_value_by_name("b_day", rand(1,28)); $listbox->select_num_value_by_name("b_month", rand(1,12)); $listbox->select_name_by_inner_name("b_year", rand(1970,2001)); // выбор пола $s = rand(0,1); $radiobox->set_checked_by_number($s,true); // вставка имени и фамилии if ($s == 1) $sex = "woman"; else $sex = "man"; $input->set_value_by_name("last_name",$submitter->generate_random_second_name("RU",$sex)); $input->set_value_by_name("first_name",$submitter->generate_random_name("RU",$sex)); // каптча $image->save_to_file_by_name("img_captcha","C:\tmp.jpeg"); $cap = $app->dlg_captcha_from_url("C:\tmp.jpeg"); $input->set_value_by_name("captchan",$cap); // пороверка логина на занятость while (strpos ($webpage->get_body(), "Логин занят") != 0) { // создание другого логина $user = $submitter->generate_random_text(rand(8,12),1); $input->set_value_by_name("reg_username",$user); $input->send_event_by_atribute("id","reg_username",true,"onkeyup"); sleep(5); } // сабмит формы $button->click_by_name("register"); $button->send_event_by_name("register","onclick"); sleep(3); $browser->wait_for(); // проверка на правильность ввода каптчи while (strpos ($webpage->get_body(), "Неверный код подтверждения") != 0) { echo "Ай-ай! Ошибочка. Попробуем снова каптчу ввести<br>"; // каптча $image->save_to_file_by_name("img_captcha","C:\tmp.jpeg"); $cap = $app->dlg_captcha_from_url("C:\tmp.jpeg"); $input->set_value_by_name("captchan",$cap); // сабмит формы $button->click_by_name("register"); $button->send_event_by_name("register","onclick"); sleep(3); $browser->wait_for(); } // проверка успешности регистрации с текущего айпи if (strpos ($webpage->get_body(), "Много регистраций") == 0) { $textfile->add_string_to_file("C:\qip.txt",$user."||".$pass."\r\n",10); $i--; } else echo "Много регистраций, меняем прокси<br>"; } $app->quit(); |
Статья для владельцев однопоточной версии программы на этом заканчивается. А вот пользователи МТ могут задержаться и посмотреть, как изменяется этот же самый скрипт с учётом предстоящей работы в многопоточном режиме.
Давайте сначала определимся с вещами, которые во многопотоке должны работать не так, как сейчас.
- 1. Уверен, что распознавать каптчу вручную при использовании многопотока никто не будет. Необходимо заменить ответственную за это часть на антикапчевских индусов.
- 2. Прокси не должны браться по порядку. Если десяток-другой потоков вдруг через одну проксю ломанётся регистрировать акки, то сработает это лишь у некоторых из них. Остальные увидят надпись «Много регистраций с одного айпи» и возьмут следующую. Которая тоже, в свою очередь, будет использоваться и другими потоками… В общем, стоит избегать работы «вхолостую».
Пожалуй, всё.
Первое решается простой заменой:
1 2 3 |
$image->save_to_file_by_name("img_captcha","C:\tmp.jpeg"); $cap = $app->dlg_captcha_from_url("C:\tmp.jpeg"); $input->set_value_by_name("captchan",$cap); |
текущего скрипта меняется на:
1 2 3 4 |
$ins = rand(0,999); $image->save_to_file_by_name("img_captcha","C:\\".$ins.".jpeg"); $cap = $image->recognize_by_anticaptcha($url,"C:\\".$ins.".jpeg","http://antigate.com"); $input->set_value_by_name("captchan",$cap); |
в обоих случаях.
Переменная со случайным числом в начале отвечает за то, чтобы потоки не дрались за отправку каптчи. Без неё возможны ситуации, когда отправлена будет совсем не та картинка, потому ответ придёт заведомо неправильный.
Второе тоже решаемо, но требует применения соображаловки. Можно из списка прокси брать и использовать случайную. После возникновения сообщения об айпи удалять её из файла и брать новую. Можно брать прокси, по счёту кратную количеству запущенных потоков. Но я обычно использую стандартную заготовку: создаю текстовый файлик, в котором каждый поток записывает номер используемой прокси плюс единичка. В процессе получается, что каждый поток берёт следующую по списку прокси по мере надобности, а не в порядке очереди. Чтобы добавить это в код, его придётся слегка переделать.
Самое начало скрипта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$xhe_host ="127.0.0.1:7010"; require("../Templates/xweb_human_emulator.php"); $proxy = file ("proxy.txt"); $i = $textfile->read_file("C:\res.txt",10); $browser->enable_proxy("all connections",$proxy[$i]); $textfile->write_file("C:\res.txt",$i+1,10,false); while ($textfile->read_file("C:\res.txt",10)<count($proxy)) { // очистка кэша и куков $browser->clear_cache(); $browser->clear_cookies("",true); /*Весь остальной скрипт*/ |
И самый конец скрипта:
1 2 3 4 5 6 7 8 9 10 11 |
/*Весь скрипт*/ else { echo "Много регистраций, меняем прокси<br>"; $i = $textfile->read_file("C:\res.txt",10); $browser->enable_proxy("all connections",$proxy[$i]); $textfile->write_file("C:\res.txt",$i+1,10,false); } } $app->quit(); |
Остался последний штрих – подготовить всю эту писанину к запуску. Чтобы всего этого избежать, нужно внимательно и без косяков выполнить следующую последовательность действий:
- 1. В строке $xhe_host =»127.0.0.1:7010″; скрипта заменить 7010″; на «.$argv[1]; Получится $xhe_host =»127.0.0.1:».$argv[1]; Сохранить.
- 2. Запустить XHEManager из корневика программы и добавить в нём наш скрипт, указав желаемое количество потоков и номер порта, с которого пойдёт отсчёт для остальных копий.
- 3. Менеджер покажет список потоков, готовых к труду и обороне. Проигнорируйте его и через меню «Файл» создайте BAT файл. В этом файлике будут записаны все данные, необходимые для запуска.
- 4. Запустите получившийся BAT файлик.
- 5. ……………
- 6. PROFIT!!!!!
Сказать по правде, запускать этот скрипт многопоточно я не планирую. Все разъяснения по настройке лишь для примера. Но если вы решитесь на сей подвиг и столкнётесь с какими-либо ошибками, смело ругайтесь матом в аську техподдержки. По опыту, каждая жалоба туда сдвигает исправление какой-либо огорчающей вас фичи в приоритетах на один пункт вверх по списку.
Напоследок привожу конечный вариант скрипта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
$xhe_host ="127.0.0.1:".$argv[1]; require("../Templates/xweb_human_emulator.php"); $proxy = file ("proxy.txt"); $i = $textfile->read_file("C:\res.txt",10); $browser->enable_proxy("all connections",$proxy[$i]); $textfile->write_file("C:\res.txt",$i+1,10,false); while ($textfile->read_file("C:\res.txt",10)<count($proxy)) { // очистка кэша и куков $browser->clear_cache(); $browser->clear_cookies("",true); // переход на сайт $browser->navigate("qip.ru"); $browser->wait_for(); // переход в форму регистрации $anchor->click_by_inner_text("Войти на сайт",true); $anchor->click_by_inner_text("Регистрация",true); $browser->wait_for(); // создание случайных данных для профиля $user = $submitter->generate_random_text(rand(8,12),1); $pass = $submitter->generate_random_text(rand(6,10),1); // заполнение инпутов $input->set_value_by_name("reg_username",$user); $input->send_event_by_atribute("id","reg_username",true,"onkeyup"); $input->set_value_by_name("reg_pwd",$pass); $input->set_value_by_name("reg_pwd2",$pass); $input->set_value_by_name("reg_email",$submitter->generate_random_text(rand(6,10),1)."@mail.ru"); // выбор случайного дня рождения $listbox->select_num_value_by_name("b_day", rand(1,28)); $listbox->select_num_value_by_name("b_month", rand(1,12)); $listbox->select_name_by_inner_name("b_year", rand(1970,2001)); // выбор пола $s = rand(0,1); $radiobox->set_checked_by_number($s,true); // вставка имени и фамилии if ($s == 1) $sex = "woman"; else $sex = "man"; $input->set_value_by_name("last_name",$submitter->generate_random_second_name("RU",$sex)); $input->set_value_by_name("first_name",$submitter->generate_random_name("RU",$sex)); // каптча $image->save_to_file_by_name("img_captcha","C:\tmp.jpeg"); $cap = $app->dlg_captcha_from_url("C:\tmp.jpeg"); $input->set_value_by_name("captchan",$cap); // проверка логина на занятость while (strpos ($webpage->get_body(), "Логин занят") != 0) { // создание другого логина $user = $submitter->generate_random_text(rand(8,12),1); $input->set_value_by_name("reg_username",$user); $input->send_event_by_atribute("id","reg_username",true,"onkeyup"); sleep(5); } // сабмит формы $button->click_by_name("register"); $button->send_event_by_name("register","onclick"); sleep(3); $browser->wait_for(); // проверка на правильность ввода каптчи while (strpos ($webpage->get_body(), "Неверный код подтверждения") != 0) { echo "Ай-ай! Ошибочка. Попробуем снова каптчу ввести<br>"; // каптча $image->save_to_file_by_name("img_captcha","C:\tmp.jpeg"); $cap = $app->dlg_captcha_from_url("C:\tmp.jpeg"); $input->set_value_by_name("captchan",$cap); // сабмит формы $button->click_by_name("register"); $button->send_event_by_name("register","onclick"); sleep(3); $browser->wait_for(); } // проверка успешности регистрации с текущего айпи if (strpos ($webpage->get_body(), "Много регистраций") == 0) { $textfile->add_string_to_file("C:\qip.txt",$user."||".$pass."\r\n",10); } else { echo "Много регистраций, меняем прокси<br>"; $i = $textfile->read_file("C:\res.txt",10); $browser->enable_proxy("all connections",$proxy[$i]); $textfile->write_file("C:\res.txt",$i+1,10,false); } } $app->quit(); |
Производительность скрипта очень сильно зависит от скорости и качества используемых прокси.
Скрипт написан 18.01.2012 в Human Emulator 4.2 Advanced.
На момент публикации статьи 23.01.2012 скрипт был рабочий.