1. Вступайте в наш Telegram чат: https://t.me/a_parser Нас уже 2600+ и мы растем!
    Скрыть объявление

Генерация картинок ruDALL-E. Мануал по созданию TS парсера

Позволяет генерировать изображения по тексту

  1. Support Ilia
    Минимальная версия A-Parser:
    1.2.1435
    В данный момент А-Парсер предоставляет широкие возможности для написания собственного парсера имея небольшие знания программирования и JavaScript. А-Парсер позволяет сосредоточится именно на написании логики; работу с многопоточностью, сетью, прокси, результатами, логами и т.д. A-Parser берет на себя. Код можно писать прямо в интерфейсе парсера, добавив новый парсер в Редакторе парсеров. Также для написания парсеров можно использовать сторонние редакторы, например VSCode

    В данной статье описано создание TypeScript парсера, отправка GET и POST запросов (чтобы не путать GET/POST запрос с запросом который подаётся на вход парсеру, далее будем называть реквест от анг. request), нахождение капчи на странице с отправкой на разгадывание в сервис и получением результата

    В качестве целевого сайта выступает сервис нейросеть ruDALL-E что позволяет генерировать изображения по тексту. Для успешной генерации нужно решить капчу и подождать время для генерации

    Готовый пресет можно скачать тут: Генерация картинок ruDALL-E

    [​IMG]


    Определяем задачу и действия

    В самом начале нужно понять алгоритм действий для получения изображения. После нескольких генераций на сайте вручную, можно понять последовательность действий:
    1. Переходим на страницу https://rudalle.ru/demo и получаем картинку с капчей
    2. Разгадываем капчу
    3. Отправляем текст по которому будет сгенерирована картинка и ответ капчи.
    [​IMG]
    4. Получаем страницу с временем ожидания
    5. После ожидания получаем страницу с сгенерированной картинкой, и парсим ссылку на неё
    6. По ссылке скачиваем картинку в нужную нам папку

    После получения понимания алгоритма нужно просмотреть каждый запрос в браузере, какие заголовки, метод, данные отправляются по реквесту. И просто повторить эти действия в коде. В данном случае все реквесты идут по цепочке и чтобы выполнить следующий реквест, нужны данные в ответе предыдущего (такое часто встречается в парсинге)

    Таким образом определили что в качестве запроса парсеру будет строка текста по которой будет произведена генерация картинки.

    Также новому парсеру нужно добавить опцию выбора пресета для разгадывания капч:
    this.conf.Util_AntiGate_preset - это настройка в конфиге, хранит название выбранного пресета для Util::AntiGate
    Чтобы добавить нужно прописать значение будущей опции в конфиг в
    static defaultConf: typeof BaseParser.defaultConf
    Код:
    Util_AntiGate_preset: 'default'

    и добавить комбобокс для выбора в интерфейсе
    Код:
    static editableConf: typeof BaseParser.editableConf = [
        ['Util_AntiGate_preset', ['combobox', 'Util::AntiGate preset']]
    ];
    После чего вот так можно будет выбрать эту опцию в парсере:
    [​IMG]


    1. Переходим на страницу https://rudalle.ru/demo и получаем картинку с капчей
    Код:
    let csrfmiddlewaretoken, captcha_0, captchaImageLink, resp = await this.request('GET', 'https://rudalle.ru/demo', {}, {
        decode: 'auto-html',
        headers: {
            'Upgrade-Insecure-Requests': '1',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
        },
        check_content: [(data) => {
            try {
                csrfmiddlewaretoken = data.match(/name="csrfmiddlewaretoken" value="(.*?)"/)[1];
                captcha_0 = data.match(/name="captcha_0" value="(.*?)"/)[1];
                captchaImageLink = 'https://rudalle.ru' + data.match(/<img src="(\/captcha\/image\/.*?\/)" alt="captcha" class="captcha"/)[1];
                return true;
            } catch (e) {
                return false;
            }
        }]
    });
    
    csrfmiddlewaretoken и captcha_0 - это специальные идентификаторы капчи, нужны для отправки результата капчи
    captchaImageLink - это ссылка на картинку с капчей и нужна чтобы отправить сервису на разгадывание

    Как видно данные собираем обычными регулярками


    2. Разгадываем капчу
    Используем раннее полученную ссылку на изображение капчи и выполняем отправку её в сервис разгадывания с помощью метода А-Парсера
    await this.captcha.recognizeFromUrl(preset, url[, overrides]):
    Код:
    let captchaResponse = await this.captcha.recognizeFromUrl(this.conf.Util_AntiGate_preset, captchaImageLink);
    
    if(!captchaResponse.answer) {
        this.logger.put('Ошибка в разгадывании капчи');
    }
    
    this.logger.put(captchaResponse.answer);
    
    if(captchaResponse.answer) {
    // тут будет следующая логика: отправка текста по которому будет сгенерирована картинка и ответ капчи
    }

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

    Код:
    if(captchaResponse.answer) {
        const {success, data} = await this.request('POST', 'https://rudalle.ru/generate_image', {
            csrfmiddlewaretoken,
            text: query, // тут в query содержится set.query - текст по которому будет сгенерировано изображение
            captcha_0,
            captcha_1: captchaResponse.answer, // отправляем разгаданную капчу
        }, {
            decode: 'auto-html',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Upgrade-Insecure-Requests': '1',
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
            }
        });
    
        if(success && typeof data == 'string') {
            if(/>Вы ввели что-то не то в форму или неправильно заполнили каптчу.</i.test(data)) {
                this.logger.put('Вы ввели что-то не то или неправильно заполнили каптчу. Нужно попробовать снова');
                await this.captcha.reportBad(this.conf.Util_AntiGate_preset, captchaResponse.id);
            } else {
                gen.success = success;
                gen.data = data;
            }
        }
    }
    После реквеста видно проверку на правильность введённых данных. Неправильно можно было ввести капчу, когда сервис в ответе прислал не верное решения капчи. Тогда нужно сообщить сервису который разгадал капчу, что капча была разгадана неверно:
    Код:
    await this.captcha.reportBad(this.conf.Util_AntiGate_preset, captchaResponse.id);
    Если же проверка пройдена и данные введены верно, тогда записываем данные в объект gen который можно инициализировать где-то выше

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

    Код:
    async startGeneration(query) {
        let gen = {
            success: 0,
            data: ''
        };
        for (let attempt = 1; attempt <= this.conf.proxyretries; ++attempt) {
            let csrfmiddlewaretoken, captcha_0, captchaImageLink, resp = await this.request('GET', 'https://rudalle.ru/demo', {}, {
                decode: 'auto-html',
                attempt,
                headers: {
                    'Upgrade-Insecure-Requests': '1',
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
                },
                check_content: [(data) => {
                    try {
                        csrfmiddlewaretoken = data.match(/name="csrfmiddlewaretoken" value="(.*?)"/)[1];
                        captcha_0 = data.match(/name="captcha_0" value="(.*?)"/)[1];
                        captchaImageLink = 'https://rudalle.ru' + data.match(/<img src="(\/captcha\/image\/.*?\/)" alt="captcha" class="captcha"/)[1];
                        return true;
                    } catch (e) {
                        return false;
                    }
                }]
            });
    
            let captchaResponse = await this.captcha.recognizeFromUrl(this.conf.Util_AntiGate_preset, captchaImageLink);
    
            if(!captchaResponse.answer) {
                this.logger.put('Ошибка в разгадывании капчи');
                continue;
            }
    
            this.logger.put(captchaResponse.answer);
    
            if(captchaResponse.answer) {
                const {success, data} = await this.request('POST', 'https://rudalle.ru/generate_image', {
                    csrfmiddlewaretoken,
                    text: query,
                    captcha_0,
                    captcha_1: captchaResponse.answer,
                }, {
                    decode: 'auto-html',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                        'Upgrade-Insecure-Requests': '1',
                        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
                    }
                });
    
                if(success && typeof data == 'string') {
                    if(/>Вы ввели что-то не то в форму или неправильно заполнили каптчу.</i.test(data)) {
                        this.logger.put('Вы ввели что-то не то или неправильно заполнили каптчу. Нужно попробовать снова');
                        await this.captcha.reportBad(this.conf.Util_AntiGate_preset, captchaResponse.id);
                        continue;
                    } else {
                        gen.success = success;
                        gen.data = data;
                        return gen;
                    }
                }
            }
        }
        return gen;
    }
    Такой метод вернёт страницу которая говорит об успешном начале генерации и примерном времени ожидания


    4. Получаем страницу с временем ожидания
    По сути эта страница уже получена предыдущим шагом. Т.к. когда был отправлен реквест с ответом капчи, тогда и произошёл редирект на страницу с временем ожидания. И осталось только распарить её, получить полезные данные перед следующим шагом

    Минуты ожидания и ссылку по которой можно получить страницу с сгенерированной картинкой:
    Код:
    let minutes;
    let checkImageUrl;
    if (/estimationTime = \+"\d+";/.test(data)) {
        minutes = parseInt(data.match(/estimationTime = \+"(\d+)";/)[1]);
    }
    if (/url = "https:\/\/rudalle.ru\/check_image\/.+?";/.test(data)) {
        checkImageUrl = data.match(/url = "(https:\/\/rudalle.ru\/check_image\/.+?)";/)[1]
    }
    if (minutes && checkImageUrl) {
        this.logger.put(`Генерация началась. Примерное время ожидания: ${minutes} мин.`);
    
        for (let time = 20; time <= 600; time+=20) {
            await this.sleep(20);
    
            //тут будет следующий шаг и его реквест
        }
    }
    else {
        this.logger.put('Не удалось получить ссылку на проверку готовности изображения');
    }
    
    После получения количества минут, прописываем задержку в потоке на 20 секунд с помощью метода await this.sleep(sec). Таким образом парсер будет проверять через каждые 20 секунд в течении 10 минут готовность изображения к скачиванию. В случае когда сайт говорит что будет готово через 2 минуты, но фактически изображение может быть готово уже через минуту, это ускорит парсинг в целом

    5. После ожидания получаем страницу с сгенерированной картинкой, и парсим ссылку на неё
    Получаем страницу и парсим с неё ссылку на сгенерированное изображение

    Код:
    const {success, data} = await this.request('GET', checkImageUrl, {}, {
        decode: 'auto-html',
        headers: {
            'Upgrade-Insecure-Requests': '1',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
        }
    });
    
    if(success && typeof data == 'string') {
        let imageUrl
        if (/<img src="https:\/\/img.rudalle.ru\/images\/[^"]+"\s*class="cardimage"/.test(data)) {
            imageUrl = data.match(/<img src="(https:\/\/img.rudalle.ru\/images\/[^"]+)"\s*class="cardimage"/)[1];
            // тут следующий шаг по скачиванию изображения await this.saveImage(imageUrl);
            break;
        }
        else {
            this.logger.put('Не удалось получить ссылку на картинку');
        }
    }
    

    6. По ссылке скачиваем картинку в нужную нам папку
    Метод который скачивает изображение по ссылке. Знаем что ссылка на изображение может иметь несколько слэшей, текст между слэшами и заканчивается на уникальный идентификатор картинки и расширение. Берём уникальный идентификатор картинки и расширение (по сути всё что после последнего слэша) за название изображения при скачивании

    Код:
    async saveImage(url) {
        let reverseStr = [...url].reverse().join("");
        reverseStr = reverseStr.match(/([^\/]+)/)[1];
        let file = [...reverseStr].reverse().join("");
    
        const {success} = await this.request('GET', url, {}, {
            save_to_file: 'results/images/' + file
        });
    
        if(success)
            this.logger.put('Картинка сохранена успешно');
        else
            this.logger.put('Не удалось сохранить картинку');
    }
    После этого сгенерированные изображения сохраняются в папку aparser\results\images

    [​IMG]

Пoследние рецензии

  1. pimenmih
    pimenmih
    5/5,
    крутая штуковина