Создание JS парсеров. Работа с CAPTCHA.

Пример разработки JS парсера для работы с каптчами

  1. Support
    Каптча - это один из самых распространенных способов защиты от автоматизированного парсинга или постинга на сайтах. Каптчи бывают различных видов, но в данном случае речь пойдет о цифровых и буквенных каптчах, где сама каптча - это картинка, и пользователь должен ввести в качестве ответа изображенное слово (бывает несколько слов) или цифры.

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

    В А-Парсере есть встроенный парсер Util::AntiGate Util::AntiGate. Он позволяет отправить картинку каптчи на такой сервис (или в софт) и получает в ответ решение. О том, как использовать данный функционал в A-Parser и пойдет речь ниже.
    В подавляющем большинстве случаев алгоритм работы с каптчей одинаковый:
    1. Открываем страницу, на которой выдается каптча
    2. Парсим ссылку на картинку каптчи (иногда кроме картинки нужно еще парсить id каптчи)
    3. Отправляем картинку на разгадывание
    4. Получаем ответ
    5. Отправляем полученный ответ обратно на сайт и получаем нужную нам информацию
    Для работы с каптчей в JS парсерах предусмотрена отдельная функция: yield this.captcha.*
    В качестве примера воспользуемся этой страничкой: http://web9.uits.uconn.edu/webdev/doc/captcha_script.php

    Прежде, чем приступать к написанию нашего парсера, нужно исследовать обьект парсинга. Как обычно, для этого используем Инструменты разработчика в Chrome.
    Открыв страницу, мы видим саму каптчу, ссылку на обновление каптчи, поле для ввода ответа и кнопку отправки. Это довольно простой пример, но его как раз достаточно, чтобы продемонстрировать общие принципы работы с каптчей, не отвлекаясь на другие нюансы.
    [​IMG]
    Открываем Инструменты разработчика, вкладка Network. Здесь мы будем смотреть куда уходят запросы.
    Вводим в поле ответа символы с картинки и нажимаем Submit. Видим, что отправляется GET запрос, в параметрах которого передается строка с ответом. И если каптча разгадана верно, в ответ приходит страница с текстом "User has been verified.".
    [​IMG]
    Рекомендую всегда тестировать также и неправильный ответ, чтобы в процессе написания JS парсера учесть все ситуации.
    Теперь, когда нам понятен весь процесс получения каптчи и отправки ответа, мы можем перейти к написанию парсера.

    Создаем и открываем новый парсер. Как обычно загружается небольшой шаблон. Сначала редактируем "шапку". Т.к. мы пишем небольшой пример, то выводить будем только результат работы. Также, нам нужно предусмотреть выбор пресета настроек для Util::AntiGate Util::AntiGate
    Код:
    constructor() {
            this.defaultConf = {
                version: '0.1.1',
                results: {
                    flat: [
                        ['result', 'Captcha result'],
                    ]
                },
                results_format: '$result\\n',
                parsecodes: {
                    200: 1,
                },
                max_size: 200 * 1024,
                Util_AntiGate_preset: 'default'
            };
            this.editableConf = [
                ['Util_AntiGate_preset', ['combobox', 'Util::AntiGate preset']]
            ];
        }
    Далее, в функции *parse(set, results) мы следуем алгоритму, который ранее определили.
    Открываем страницу, на которой выдается каптча (рекомендую всегда делать проверку на "удачность" запроса):
    Код:
    let response = yield this.request('GET', 'http://web9.uits.uconn.edu/webdev/doc/captcha_script.php', {}, {
            decode: 'auto-html',
    });
    if(!response.success) {
            this.logger.put('Error open page');
            results.success = 0;
            return results;
    }
    Парсим ссылку на картинку каптчи (тут также делается проверка на предмет того, чтобы регулярное выражение сработало):
    Код:
    let captchaUrl;
    try {
            captchaUrl = response.data.match(/<img id="captcha" src="([^"]+)/)[1];
    } catch(e) {
            this.logger.put('Captcha not found');
            results.success = 0;
            return results;
    }
    Отправляем картинку на разгадывание и получаем ответ (используется метод yield this.captcha.recognizeFromUrl, а также проверяется, не возникла ли ошибка в процессе разгадывания):
    Код:
    let captchaResp = yield this.captcha.recognizeFromUrl(this.conf.Util_AntiGate_preset, captchaUrl, {})
    if(!captchaResp.answer) {
            this.logger.put('Captcha recognize failed');
            results.success = 0;
            return results;
    }
    Отправляем полученный ответ обратно на сайт (также проверяем "удачность" запроса):
    Код:
    response = yield this.request('GET', 'http://web9.uits.uconn.edu/webdev/doc/captcha_script.php', {
            captcha: captchaResp.answer,
            submit: 'Submit'
    }, {
            decode: 'auto-html',
    });
    if(!response.success) {
            this.logger.put('Error checking captcha');
            results.success = 0;
            return results;
    }
    И проверяем полученную страницу, если на ней есть текст User has been verified - значит каптча была разгадана верно и в результат можно вывести сообщение об успехе, иначе ошибка разгадывания:
    Код:
    if(!/User has been verified/.test(response.data)) {
            this.logger.put('Bad captcha');
            yield this.captcha.reportBad(this.conf.Util_AntiGate_preset, captchaResp.id, {})
            results.success = 0;
            return results;
    } else {
            this.logger.put('Good captcha');
            results.result = 'Success';
            results.success = 1;
    }
    Возвращаем результат:
    Код:
    return results;
    Сохраняем парсер и проверяем его в работе. В данном случае, на вход в парсер можно подавать что угодно, т.к. это пример, и от начального запроса ничего не зависит.
    [​IMG]
    Как видим, парсер составлен правильно и каптча разгадывается.

    Данный пример демонстрирует работу с каптчами в JS парсерах. При написании более сложных парсеров алгоритм может немного отличаться, но в целом общий подход именно такой, как описано выше.

    P.S. Т.к. для правильной работы антигейта нужно сначала его настроить, т.е. создать пресет настроек, то рекомендуется вначале парсинга делать проверку выбранного пресета настроек. Т.е., если пресет не выбран (используется значение по-умолчанию - default), то дальнейшая работа парсера не имеет смысла. Проверка выглядит примерно так:
    Код:
    if(this.conf.Util_AntiGate_preset == 'default') {
            this.logger.put('Select antigate preset');
            results.success = 0;
            return results;
    }
    Полностью готовый парсер можно забрать в каталоге.