Как решать капчу в code step Pipedream (Node.js)

Решение капчи в Pipedream проще, чем та же задача в Zapier или Make.com, потому что code step это настоящая среда Node.js v20 с импортом npm-пакетов. Вы ничего не устанавливаете, пишете обычный JavaScript, и SDK CapSkip работает ровно так же, как на вашем ноутбуке. Отличий два, и оба про то, где выполняется код. Pipedream выполняет его в собственном облаке, поэтому решатель должен быть доступен из интернета. А у запуска сценария есть лимит времени, который короче решения reCAPTCHA, и именно он определяет, пишете вы один шаг или два.
Что понадобится
- Сценарий Pipedream с code step. Ниже используется среда Node.js. Версия на Python приведена дальше.
- CapSkip, запущенный в режиме Server на машине, доступной из интернета, со статическим публичным IP.
- Sitekey и URL страницы сайта, который вы автоматизируете.
- Две переменные окружения Pipedream с адресом решателя и его ключом.
Почему адрес loopback здесь не работает
Сценарии Pipedream выполняются на собственной инфраструктуре сервиса, в сети AWS us-east-1. Запрос к 127.0.0.1 изнутри code step ведёт в контейнер, где выполняется шаг, а не к вашему столу. Там никто не слушает, и SDK выбрасывает NetworkException.
У CapSkip два режима подключения, и ответ здесь второй. Local слушает 127.0.0.1 и обслуживает только это устройство, что правильно, когда автоматизация и решатель стоят на одной машине. Server слушает ваш сетевой адрес или публичный IP, поэтому облачная платформа может обратиться к той же машине Windows по API. Рекомендуется статический публичный IP, потому что домашний адрес, который меняется, сломает сценарий в три часа ночи и ничего вам не скажет. Оба режима настраиваются в разделе Настройки подключения.
Режим Server меняет только то, где работает решатель, и больше ничего. Это по-прежнему ваше железо и по-прежнему без счётчика, поэтому сценарий, который срабатывает десять тысяч раз в месяц, стоит столько же, сколько срабатывающий десять раз.
Шаг 1: положите адрес в переменную окружения
Не прописывайте публичный IP прямо в шаге. В Pipedream есть переменные окружения рабочего пространства, и code step читает их из обычного окружения процесса. Создайте две.
| Имя переменной | Значение |
|---|---|
| Переменная с именем CAPSKIP_HOST | Публичный IP машины с решателем, без схемы и без порта |
| Переменная с именем CAPSKIP_API_KEY | Ключ, который вы сгенерировали в приложении CapSkip для этого сценария |
Дайте этому сценарию отдельный ключ вместо общего. Отзыв ключа, который утёк в общее рабочее пространство, не должен утаскивать за собой остальную автоматизацию.
Шаг 2: всё решение в одном code step
Pipedream устанавливает npm-пакет в тот момент, когда вы его импортируете, поэтому отдельного шага установки и файла пакета нет. В строке импорта же задаётся и версия, а фиксировать её стоит на всём, что работает без присмотра.
// npm install capskip - Pipedream installs it from this import.
// The package is CommonJS, so take the default and destructure.
import capskip from "capskip";
const { CapSkip } = capskip;
export default defineComponent({
async run({ steps, $ }) {
const solver = new CapSkip({
host: process.env.CAPSKIP_HOST,
port: 8080,
apiKey: process.env.CAPSKIP_API_KEY,
});
const result = await solver.recaptcha(
"YOUR_SITEKEY",
"https://example.com/page-with-recaptcha"
);
return result.code; // the token, for the next step
},
});Вот и всё. Все варианты reCAPTCHA это один и тот же метод с объектом настроек: invisible со значением 1, enterprise со значением 1 или version со значением v3 вместе с именем действия. У Turnstile и GeeTest есть свои методы такой же формы, а полный список параметров приведён в документации CapSkip API.
Всё, что возвращает шаг, попадает в экспорты сценария, поэтому следующий шаг читает токен по имени самого шага. Если хотите дать значению метку, используйте хелпер экспорта.
// A named export reads better downstream than a bare return.
$.export("token", result.code);
// The next step then reads steps.solve_captcha.tokenТаймаут выполнения и момент, когда одного шага перестаёт хватать
Вот ограничение, которое определяет всё остальное. У запуска в Pipedream лимит времени по умолчанию составляет 30 секунд для триггеров HTTP и email и 60 секунд для триггеров по расписанию. Его можно поднять в настройках сценария: до 300 секунд на бесплатных тарифах и до 750 секунд на платных.
Решение reCAPTCHA v2 обычно укладывается заметно быстрее 30 секунд, но обычно это не всегда, а собственный предел SDK задан в recaptchaTimeout и равен 300 секундам. Поэтому вариант из одного шага выше надёжен ровно настолько, насколько выставлен ваш таймаут. Если лимит сценария меньше времени решения, выполнение убивают на середине опроса, и вы получаете упавший запуск без всякой полезной информации.
| Ваша ситуация | Что делать |
|---|---|
| Небольшие объёмы, и вы можете поднять лимит выполнения до 300 секунд | Оставьте вариант из одного шага. Поднимите таймаут в настройках сценария |
| Большие объёмы или вы платите за время выполнения | Разделите шаг и используйте хелпер rerun, описанный ниже |
| Страницы проверки Turnstile или GeeTest, которые занимают больше времени | Разделите шаг. Именно эти типы чаще всего выходят за короткий лимит |
Шаг 3: разделите его с помощью хелпера rerun
В Pipedream есть примитив опроса, которого нет у большинства платформ автоматизации. Хелпер flow.rerun завершает текущий шаг, ждёт и запускает тот же шаг снова с переданным вами состоянием. Пока идёт ожидание, сценарий не выполняется, поэтому медленное решение ничего вам не стоит и не может упереться в лимит.
Работает это благодаря трём вещам. Счётчик запусков начинается с 1 и увеличивается при каждом перезапуске. Объект контекста, который вы передаёте, доступен для чтения на следующем проходе. А превышение предела повторов переводит сценарий к следующему шагу, а не роняет его, поэтому бросайте исключение, если вам нужно другое поведение.
// No SDK here. The raw endpoints suit a step that exits
// between polls, because nothing has to stay in memory.
const MAX_RETRIES = 20;
const DELAY = 15000; // 15s, the recommended first wait for v2
export default defineComponent({
async run({ steps, $ }) {
const { run } = $.context;
const base = `http://${process.env.CAPSKIP_HOST}:8080`;
const key = process.env.CAPSKIP_API_KEY;
if (run.runs === 1) {
const params = new URLSearchParams({
key,
method: "userrecaptcha",
googlekey: "YOUR_SITEKEY",
pageurl: "https://example.com/page-with-recaptcha",
json: "1",
});
const submitted = await fetch(`${base}/in.php?${params}`);
const { request: id } = await submitted.json();
// The id survives into the next run through the context.
return $.flow.rerun(DELAY, { id }, MAX_RETRIES);
}
const { id } = $.context.run.context;
const polled = await fetch(
`${base}/res.php?key=${key}&action=get&id=${id}&json=1`
);
const data = await polled.json();
if (data.request !== "CAPCHA_NOT_READY") {
return data.request; // the token
}
if (run.runs === MAX_RETRIES + 1) {
throw new Error("Solve did not finish in time");
}
return $.flow.rerun(DELAY, { id }, MAX_RETRIES);
},
});Написание этого ответа об ожидании многих сбивает с толку. Это CAPCHA_NOT_READY, без буквы T, и это не ошибка: он означает, что ответ ещё не готов и нужно опросить снова. Считать его сбоем это самая частая ошибка в самописном цикле опроса, и на эту тему есть подробный разбор ответа CAPCHA_NOT_READY.
Ещё одна деталь про этот эндпоинт. Результат можно прочитать ровно один раз. Если вы залогируете тело ответа, а затем прочитаете его снова на более позднем шаге, второе чтение вернёт пустоту и будет выглядеть как неудачное решение.
Отправляйте токен сразу
Токен reCAPTCHA живёт около двух минут. В сценарии потерять его проще, чем кажется, потому что шаг задержки, медленный HTTP-запрос или слишком долгое ожидание перед перезапуском съедают один и тот же бюджет. Поставьте шаг, который отправляет токен, сразу после шага, который его получил, и не накапливайте токены на потом. Полная картина в руководстве по сроку действия токена reCAPTCHA.
Версия на Python
Pipedream также выполняет code step на Python 3.12 и точно так же ставит пакеты pip по вашим импортам. SDK сам не читает переменные окружения, поэтому шаг ниже читает CAPSKIP_HOST, CAPSKIP_PORT и CAPSKIP_API_KEY и передаёт их в конструктор.
# pip install capskip - Pipedream installs it from this import
import os
from capskip import CapSkip
def handler(pd: "pipedream"):
# Your Pipedream environment variables. The SDK does not read
# them by itself, so pass them to the constructor.
solver = CapSkip(
host=os.environ["CAPSKIP_HOST"],
port=int(os.environ["CAPSKIP_PORT"]),
apiKey=os.environ["CAPSKIP_API_KEY"],
)
page_url = pd.steps["trigger"]["event"]["body"]["url"]
result = solver.recaptcha(sitekey="YOUR_SITEKEY", url=page_url)
# Downstream steps read pd.steps["solve"]["token"]
return {"token": result["code"]}Если пойдёте этим путём, задайте CAPSKIP_PORT со значением 8080 вместе с двумя остальными. Одно ограничение, о котором стоит знать заранее: хелперы rerun и delay описаны в документации для Node.js, поэтому шаг на Python подходит только для варианта в один проход. Если вам нужна схема с разделённым опросом, напишите этот шаг на Node.
Защита порта, который вы только что открыли
Режим Server выставляет слушателя в публичный интернет, поэтому относитесь к нему как к любому другому открытому сервису. Три вещи стоит сделать в первый же день.
- Включите проверку API-ключа в приложении CapSkip и дайте этому сценарию отдельный ключ. Без неё в качестве ключа принимается любая строка.
- Закройте решатель правилом фаервола вместо того, чтобы держать 8080 открытым для всех.
- Решите, как вы сформулируете это правило. Обычный исходящий трафик Pipedream идёт из стандартных диапазонов AWS us-east-1, которые слишком широки, чтобы их имело смысл вносить в белый список. Если нужно узкое правило, Pipedream предлагает VPC с выделенным статическим исходящим IP на рабочее пространство, и в белый список вы вносите именно этот адрес.
Частые ошибки и что они означают
| Что вы видите | Причина | Исправить |
|---|---|---|
| NetworkException или отказ в соединении на порту 8080 | Решатель в режиме Local или переменная с хостом задана неверно | Переключитесь в режим Server и укажите в переменной с хостом публичный IP |
| Выполнение убивают на середине решения | Лимит времени сценария меньше, чем заняло решение | Поднимите его в настройках сценария или перейдите к варианту с rerun |
| Шаг возвращает как результат буквальный ответ об ожидании | Ветка опроса приняла CAPCHA_NOT_READY за ответ | Проверяйте это значение явно и перезапускайте шаг, а не возвращайте его |
| ERROR_WRONG_USER_KEY от сырого эндпоинта | Переменная с ключом пуста, поэтому была отправлена пустая строка | Проверьте имя переменной окружения, включая регистр |
| При втором чтении токен пуст | Результат можно прочитать только один раз | Прочитайте его один раз, сохраните в переменную и передавайте дальше её |
| Целевой сайт отклоняет действительный токен | Он истёк между решением и отправкой | Переставьте шаг отправки сразу после шага решения |
| Cannot use import statement outside a module | Шаг смешивает import и require | Выберите один стиль на шаг. Code step является ES-модулем |
FAQ
Можно ли использовать CapSkip из Pipedream, ничего не открывая наружу?
Напрямую нет, потому что вычисления Pipedream принадлежат Pipedream. Кто-то должен принимать входящее соединение. Простой ответ это режим Server с проверкой ключа и правилом фаервола. Если ваша политика полностью запрещает открытый порт, альтернатива в том, чтобы держать работу с капчей на подконтрольной вам машине и заставить Pipedream обращаться к её собственному эндпоинту, но это перемещает открытую точку, а не убирает её.
Считается ли перезапуск отдельным выполнением?
Шаг запускается снова, поэтому код выполняется больше одного раза, именно для этого и существует счётчик. Смысл хелпера в том, что сценарий не держится открытым во время ожидания, поэтому медленное решение не выталкивает одно выполнение за лимит времени. Если для вас важен биллинг, сверьтесь с правилами учёта на вашем тарифе, но проблема таймаута решается в любом случае.
Что использовать: SDK или сырые эндпоинты?
SDK, когда всю работу делает один шаг, потому что он берёт на себя опрос, наращивает паузу начиная с 250 миллисекунд вместо сна с постоянным интервалом и выдаёт типизированные ошибки. Сырые эндпоинты, когда вы разделяете работу на перезапуски, потому что между опросами шаг завершается и в памяти не остаётся клиента, который держал бы задачу. Оба варианта обращаются к одному и тому же сервису на одном порту.
Чем это отличается от того же в n8n или Zapier?
В основном средой выполнения. Pipedream даёт настоящий Node.js с импортом npm-пакетов, поэтому SDK встаёт сразу, а хелпер rerun аккуратно справляется с медленными решениями. Code step в Zapier не умеет ставить пакеты, поэтому руководство по капче в Zapier построено вокруг его таймаута. У Make.com вообще нет code step, и именно поэтому пошаговое руководство по Make.com собрано из HTTP-модулей. n8n можно развернуть у себя рядом с решателем, поэтому руководство по n8n часто может оставить режим Local.
Коротко
Переведите CapSkip в режим Server, храните его адрес и ключ в переменных окружения Pipedream и импортируйте SDK прямо в code step на Node.js. Если лимит времени сценария с запасом больше вашего самого медленного решения, вся интеграция это один шаг. Если нет, разделите шаг: отправьте задачу на сырой эндпоинт, передайте её id хелперу rerun и опрашивайте результат при следующем заходе. Отправляйте токен сразу после получения, потому что он истекает примерно через две минуты.
Сторона Node.js разобрана на странице сервиса распознавания капчи для Node.js, капча с флажком на странице сервиса распознавания reCAPTCHA v2, а эквивалентные вызовы в Python, PHP и C# на на странице SDK для распознавания капчи.
Стоит знать, прежде чем встраивать это в то, что работает целый день: CapSkip выполняет обход капчи на железе, которым вы уже владеете, поэтому сценарий, который срабатывает постоянно, и сценарий, который срабатывает изредка, стоят совершенно одинаково.
