Tutoriais

Notion API + CaptchaAI: entrada automatizada de dados com tratamento CAPTCHA

A API do Notion permite ler e gravar em bancos de dados de forma programática, tornando-o uma camada flexível de gerenciamento de tarefas para fluxos de trabalho de automação. Quando esses fluxos de trabalho encontram formulários protegidos por CAPTCHA,CaptchaAIcuida da resolução. Este guia mostra como usar um banco de dados Notion como uma fila de tarefas CAPTCHA – armazenando URLs e chaves de site, acionando soluções e gravando resultados.

Cenário do mundo real

Sua equipe mantém um banco de dados Notion de URLs de sites que precisam de extração periódica de dados. Algumas dessas URLs são protegidas por CAPTCHA. Um roteiro:

  1. Lê tarefas pendentes do Notion
  2. Resolve CAPTCHAs via CaptchaAI
  3. Atualiza cada registro do Notion com o token e o status

Pré-requisitos

  • Integração de noção (integração interna viadesenvolvedores.notion.com)
  • Um banco de dados Notion compartilhado com a integração
  • Chave de API CaptchaAI
  • Python 3.8+ ou Node.js 18+

Configuração do banco de dados do Notion

Crie um banco de dados Notion com estas propriedades:

Propriedade Tipo Objetivo
Nome Título Identificador de tarefa
URL URL Página de destino com CAPTCHA
Chave do site Texto rico chave do site reCAPTCHA
Estado Selecione Pendente, Resolvendo, Resolvido, Falha
Símbolo Texto rico Token CAPTCHA resolvido
Resolvido em Data Resolver carimbo de data/hora
Erro Texto rico Mensagem de erro se falhar

Compartilhe o banco de dados com sua integração do Notion.

Implementação Python

# notion_captcha_worker.py
import os
import time
import requests

NOTION_TOKEN = os.environ.get("NOTION_TOKEN")
NOTION_DB_ID = os.environ.get("NOTION_DB_ID")
CAPTCHAAI_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")

NOTION_HEADERS = {
    "Authorization": f"Bearer {NOTION_TOKEN}",
    "Content-Type": "application/json",
    "Notion-Version": "2022-06-28",
}

def get_pending_tasks():
    """Fetch tasks with Status = Pending from Notion."""
    url = f"https://api.notion.com/v1/databases/{NOTION_DB_ID}/query"
    payload = {
        "filter": {
            "property": "Status",
            "select": {"equals": "Pending"},
        }
    }
    resp = requests.post(url, headers=NOTION_HEADERS, json=payload)
    resp.raise_for_status()
    return resp.json()["results"]

def update_task(page_id, properties):
    """Update a Notion page with new property values."""
    url = f"https://api.notion.com/v1/pages/{page_id}"
    payload = {"properties": properties}
    resp = requests.patch(url, headers=NOTION_HEADERS, json=payload)
    resp.raise_for_status()

def set_status(page_id, status, token=None, error=None):
    """Update task status in Notion."""
    props = {"Status": {"select": {"name": status}}}

    if token:
        props["Token"] = {"rich_text": [{"text": {"content": token[:2000]}}]}
        props["Solved At"] = {"date": {"start": time.strftime("%Y-%m-%dT%H:%M:%S")}}

    if error:
        props["Error"] = {"rich_text": [{"text": {"content": error[:200]}}]}

    update_task(page_id, props)

def solve_captcha(sitekey, pageurl):
    """Submit to CaptchaAI and poll for result."""
    # Submit
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": CAPTCHAAI_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": "1",
    })
    result = resp.json()

    if result.get("status") != 1:
        raise Exception(f"Submit failed: {result.get('request')}")

    task_id = result["request"]

    # Poll
    time.sleep(15)
    for _ in range(25):
        poll = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": CAPTCHAAI_KEY,
            "action": "get",
            "id": task_id,
            "json": "1",
        })
        poll_result = poll.json()

        if poll_result.get("status") == 1:
            return poll_result["request"]
        if poll_result.get("request") != "CAPCHA_NOT_READY":
            raise Exception(f"Solve failed: {poll_result.get('request')}")

        time.sleep(5)

    raise Exception("Polling timeout")

def extract_property(page, prop_name, prop_type="rich_text"):
    """Extract a property value from a Notion page."""
    prop = page["properties"].get(prop_name, {})
    if prop_type == "rich_text":
        texts = prop.get("rich_text", [])
        return texts[0]["plain_text"] if texts else ""
    elif prop_type == "url":
        return prop.get("url", "")
    return ""

def main():
    tasks = get_pending_tasks()
    print(f"Found {len(tasks)} pending tasks")

    for task in tasks:
        page_id = task["id"]
        sitekey = extract_property(task, "Sitekey")
        pageurl = extract_property(task, "URL", "url")

        if not sitekey or not pageurl:
            set_status(page_id, "Failed", error="Missing sitekey or URL")
            continue

        print(f"Solving: {pageurl}")
        set_status(page_id, "Solving")

        try:
            token = solve_captcha(sitekey, pageurl)
            set_status(page_id, "Solved", token=token)
            print(f"  Solved successfully")
        except Exception as e:
            set_status(page_id, "Failed", error=str(e))
            print(f"  Failed: {e}")

        time.sleep(1)  # Rate limit for Notion API

    print("All tasks processed")

if __name__ == "__main__":
    main()

Implementação de JavaScript

// notion_captcha_worker.js
const { Client } = require('@notionhq/client');
const axios = require('axios');

const notion = new Client({ auth: process.env.NOTION_TOKEN });
const DB_ID = process.env.NOTION_DB_ID;
const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';

async function getPendingTasks() {
  const response = await notion.databases.query({
    database_id: DB_ID,
    filter: { property: 'Status', select: { equals: 'Pending' } },
  });
  return response.results;
}

async function updateTask(pageId, status, token, error) {
  const properties = {
    Status: { select: { name: status } },
  };
  if (token) {
    properties.Token = { rich_text: [{ text: { content: token.slice(0, 2000) } }] };
    properties['Solved At'] = { date: { start: new Date().toISOString() } };
  }
  if (error) {
    properties.Error = { rich_text: [{ text: { content: error.slice(0, 200) } }] };
  }
  await notion.pages.update({ page_id: pageId, properties });
}

async function solveCaptcha(sitekey, pageurl) {
  const submit = await axios.get('https://ocr.captchaai.com/in.php', {
    params: {
      key: API_KEY, method: 'userrecaptcha',
      googlekey: sitekey, pageurl, json: '1',
    },
  });
  if (submit.data.status !== 1) throw new Error(submit.data.request);

  await new Promise(r => setTimeout(r, 15000));

  for (let i = 0; i < 25; i++) {
    const poll = await axios.get('https://ocr.captchaai.com/res.php', {
      params: { key: API_KEY, action: 'get', id: submit.data.request, json: '1' },
    });
    if (poll.data.status === 1) return poll.data.request;
    if (poll.data.request !== 'CAPCHA_NOT_READY') throw new Error(poll.data.request);
    await new Promise(r => setTimeout(r, 5000));
  }
  throw new Error('Timeout');
}

async function main() {
  const tasks = await getPendingTasks();
  console.log(`Found ${tasks.length} pending tasks`);

  for (const task of tasks) {
    const sitekey = task.properties.Sitekey?.rich_text?.[0]?.plain_text;
    const pageurl = task.properties.URL?.url;

    if (!sitekey || !pageurl) {
      await updateTask(task.id, 'Failed', null, 'Missing sitekey or URL');
      continue;
    }

    console.log(`Solving: ${pageurl}`);
    await updateTask(task.id, 'Solving');

    try {
      const token = await solveCaptcha(sitekey, pageurl);
      await updateTask(task.id, 'Solved', token);
      console.log('  Solved');
    } catch (e) {
      await updateTask(task.id, 'Failed', null, e.message);
      console.log(`  Failed: ${e.message}`);
    }

    await new Promise(r => setTimeout(r, 1000));
  }
}

main().catch(console.error);

Solução de problemas

Problema Causa Correção
401 Unauthorized da noção Integração não conectada ao banco de dados Compartilhe o banco de dados com sua integração no Notion
Os nomes das propriedades não correspondem Sensibilidade a maiúsculas e minúsculas Os nomes das propriedades do Notion diferenciam maiúsculas de minúsculas - correspondem exatamente
Token truncado Noção de limite rich_text de 2.000 caracteres Os tokens CAPTCHA normalmente têm <1000 caracteres; isso não deveria ser um problema
Limite de taxa de noção (429) Muitas chamadas de API Adicione atrasos de 1 segundo entre as atualizações do Notion

Perguntas frequentes

Posso executar isso como um trabalho agendado?

Sim. Use cron (Linux), Agendador de tarefas (Windows) ou um agendador de nuvem (AWS EventBridge, Google Cloud Scheduler) para executar o script de acordo com uma programação.

Como configuro a integração do Notion?

Vá paranoção.so/my-integrations, crie uma nova integração interna, copie o token secreto e compartilhe seu banco de dados com a integração.

Posso processar outros tipos de CAPTCHA?

Sim. Adicione uma propriedade "Tipo CAPTCHA" ao banco de dados Notion e modifique a função de resolução para usar o método CaptchaAI apropriado (turnstile, geetest, base64, etc.).

Artigos relacionados

Próximas etapas

Transforme seus bancos de dados Notion em filas automatizadas de resolução de CAPTCHA -obtenha sua chave API CaptchaAI.

Guias relacionados:

Os comentários estão desativados para este artigo.