Tích Hợp

Airtable + CaptchaAI: Giải quyết CAPTCHA được kích hoạt cơ sở dữ liệu

Bạn quản lý danh sách URL trong Airtable và mỗi URL đứng sau reCAPTCHA v2? Không cần tool ngoài: Airtable Automation có thể tự gọi CaptchaAI ngay khi một bản ghi mới xuất hiện, rồi ghi token giải xong thẳng vào bảng — toàn bộ luồng chạy trong chính Airtable, không cần server riêng hay cron job.

Bài viết này đi thẳng vào cách dựng luồng đó: một Automation lắng nghe bản ghi mới, một script gọi in.php/res.php của CaptchaAI, và một biến thể xử lý hàng loạt bằng Scripting extension khi bạn có hàng trăm bản ghi cùng lúc.

Cách này hợp nhất với các đội đã dùng Airtable làm nơi quản lý dữ liệu trung tâm thay vì viết riêng một dịch vụ backend. Nhiều đội vận hành dữ liệu tại các công ty outsourcing ở TP.HCM và Hà Nội (dạng FPT Software, VNG, hoặc các agency nhỏ hơn) dùng đúng mô hình này: một bảng Airtable liệt kê hàng trăm URL cần theo dõi — có thể là biểu mẫu QA nội bộ có reCAPTCHA v2, hoặc trang giá công khai cần thu thập định kỳ — và Automation tự giải CAPTCHA cho từng URL mới thay vì để nhân sự copy-paste sitekey thủ công. Luồng xử lý gồm bốn bước:

  1. Airtable Automation phát hiện bản ghi mới (hoặc bản ghi được đặt lại "đang chờ xử lý")
  2. Script gửi sitekeypageurl sang CaptchaAI
  3. CaptchaAI trả về token đã giải
  4. Token được ghi lại vào đúng bản ghi đó

Bước 1: Tạo Automation giải CAPTCHA trong Airtable

Trước khi tạo Automation, dựng bảng dữ liệu mà script sẽ đọc và ghi vào — đặt tên CAPTCHA Tasks với các trường sau (tên trường giữ nguyên tiếng Anh vì script bên dưới tham chiếu trực tiếp tới chúng):

Tên trường Loại Mục đích
URL URL URL của trang đích cần giải CAPTCHA
Sitekey Văn bản một dòng Sitekey reCAPTCHA lấy từ trang đích
Status Chọn một lần pending, solving, solved, failed
Token Văn bản dài Token CAPTCHA sau khi giải xong
Solved At Ngày/giờ Thời điểm giải xong
Error Văn bản một dòng Nội dung lỗi nếu bản ghi giải thất bại

Có bảng rồi, tạo Automation trong tab Automations của Airtable:

  1. Mở tab Automations, nhấp Create automation, đặt tên "Giải CAPTCHA khi có bản ghi mới"
  2. Chọn trigger When record matches conditions
  3. Table: CAPTCHA Tasks — Điều kiện: Status = "pending"
  4. Lưu trigger — điều kiện này kích hoạt cả bản ghi hoàn toàn mới lẫn bản ghi cũ được đặt lại về "pending", tức là bạn có sẵn cơ chế giải lại chỉ bằng cách đổi giá trị Status

Bước 2: Thêm script giải CAPTCHA qua CaptchaAI

Thêm action → Run a script:

// Airtable Automation Script — Solve CAPTCHA via CaptchaAI

// Input configuration (set in the left panel):
// - recordId: Record ID from trigger
// - sitekey: Sitekey field from trigger
// - pageurl: URL field from trigger
const config = input.config();
const recordId = config.recordId;
const sitekey = config.sitekey;
const pageurl = config.pageurl;

const API_KEY = 'YOUR_API_KEY'; // Use input.config() for security

// Update status to "solving"
const table = base.getTable('CAPTCHA Tasks');
await table.updateRecordAsync(recordId, {
  'Status': { name: 'solving' },
});

try {
  // Step 1: Submit task to CaptchaAI
  const submitUrl = `https://ocr.captchaai.com/in.php?key=${API_KEY}&method=userrecaptcha&googlekey=${encodeURIComponent(sitekey)}&pageurl=${encodeURIComponent(pageurl)}&json=1`;

  const submitResponse = await fetch(submitUrl);
  const submitResult = await submitResponse.json();

  if (submitResult.status !== 1) {
    throw new Error(`Submit failed: ${submitResult.request}`);
  }

  const taskId = submitResult.request;
  console.log(`Task submitted: ${taskId}`);

  // Step 2: Poll for result (wait 15 seconds first)
  await new Promise(resolve => setTimeout(resolve, 15000));

  let token = null;
  for (let i = 0; i < 20; i++) {
    const pollUrl = `https://ocr.captchaai.com/res.php?key=${API_KEY}&action=get&id=${taskId}&json=1`;
    const pollResponse = await fetch(pollUrl);
    const pollResult = await pollResponse.json();

    if (pollResult.status === 1) {
      token = pollResult.request;
      break;
    }

    if (pollResult.request !== 'CAPCHA_NOT_READY') {
      throw new Error(`Solve failed: ${pollResult.request}`);
    }

    await new Promise(resolve => setTimeout(resolve, 5000));
  }

  if (!token) {
    throw new Error('Polling timeout — CAPTCHA not solved in time');
  }

  // Step 3: Update record with solved token
  await table.updateRecordAsync(recordId, {
    'Status': { name: 'solved' },
    'Token': token,
    'Solved At': new Date().toISOString(),
    'Error': '',
  });

  console.log(`CAPTCHA solved for record ${recordId}`);

} catch (error) {
  // Update record with error
  await table.updateRecordAsync(recordId, {
    'Status': { name: 'failed' },
    'Error': error.message,
  });
  console.error(`Failed: ${error.message}`);
}
Biến input Ánh xạ từ bước trigger
recordId Record ID
sitekey Trường Sitekey
pageurl Trường URL

Bước 3: Xử lý hàng loạt bằng Scripting extension

Khi cần giải CAPTCHA cho nhiều bản ghi cùng lúc thay vì từng bản ghi một, dùng Scripting extension của Airtable (nằm trong panel Apps):

// Batch CAPTCHA Solver — Airtable Scripting Extension
const API_KEY = 'YOUR_API_KEY';
const table = base.getTable('CAPTCHA Tasks');

// Get all pending records
const query = await table.selectRecordsAsync({
  fields: ['URL', 'Sitekey', 'Status'],
});

const pendingRecords = query.records.filter(
  r => r.getCellValueAsString('Status') === 'pending'
);

output.text(`Found ${pendingRecords.length} pending CAPTCHAs`);

for (const record of pendingRecords) {
  const sitekey = record.getCellValueAsString('Sitekey');
  const pageurl = record.getCellValueAsString('URL');

  if (!sitekey || !pageurl) {
    output.text(`Skipping ${record.id} — missing sitekey or URL`);
    continue;
  }

  output.text(`Solving for: ${pageurl}`);

  await table.updateRecordAsync(record.id, {
    'Status': { name: 'solving' },
  });

  try {
    // Submit
    const submitResp = await fetch(
      `https://ocr.captchaai.com/in.php?key=${API_KEY}&method=userrecaptcha&googlekey=${encodeURIComponent(sitekey)}&pageurl=${encodeURIComponent(pageurl)}&json=1`
    );
    const submitData = await submitResp.json();

    if (submitData.status !== 1) throw new Error(submitData.request);

    // Poll
    await new Promise(r => setTimeout(r, 15000));
    let token = null;

    for (let i = 0; i < 20; i++) {
      const pollResp = await fetch(
        `https://ocr.captchaai.com/res.php?key=${API_KEY}&action=get&id=${submitData.request}&json=1`
      );
      const pollData = await pollResp.json();

      if (pollData.status === 1) { token = pollData.request; break; }
      if (pollData.request !== 'CAPCHA_NOT_READY') throw new Error(pollData.request);
      await new Promise(r => setTimeout(r, 5000));
    }

    if (!token) throw new Error('Timeout');

    await table.updateRecordAsync(record.id, {
      'Status': { name: 'solved' },
      'Token': token,
      'Solved At': new Date().toISOString(),
    });
    output.text(`✓ Solved: ${pageurl}`);

  } catch (e) {
    await table.updateRecordAsync(record.id, {
      'Status': { name: 'failed' },
      'Error': e.message,
    });
    output.text(`✗ Failed: ${e.message}`);
  }
}

output.text('Batch processing complete');

Các lỗi thường gặp và cách xử lý

Vấn đề Nguyên nhân Cách xử lý
Automation không kích hoạt Bản ghi không khớp điều kiện "pending" Kiểm tra lại giá trị trường Status khớp chính xác chuỗi "pending"
fetch is not defined Airtable Scripting dùng remoteFetchAsync trong một số ngữ cảnh Thử remoteFetchAsync thay cho fetch
Script bị timeout Automation script của Airtable giới hạn 30 giây Giảm số lần lặp polling; tăng thời gian chờ ban đầu lên 20 giây
Cập nhật bản ghi thất bại Tên trường trong updateRecordAsync không khớp bảng Đối chiếu chính xác tên trường trong bảng với chuỗi trong script
API key lộ trong script API key gắn cứng trực tiếp trong code Dùng input.config() với biến input dạng secret

Câu hỏi thường gặp

CaptchaAI hỗ trợ giải những loại CAPTCHA nào khi tích hợp qua Airtable?

  • Ngoài reCAPTCHA v2 dùng trong ví dụ này, CaptchaAI còn giải được reCAPTCHA v3, Cloudflare Turnstile, Cloudflare Challenge, GeeTest v3, CAPTCHA ảnh/OCR, grid image và BLS
  • Tất cả dùng chung một cặp endpoint in.php/res.php, chỉ đổi tham số method
  • CaptchaAI không hỗ trợ hCaptcha và FunCaptcha (Arkose Labs); GeeTest v4 đang ở trạng thái sắp ra mắt
  • CaptchaFox, Friendly Captcha và Lemin đang trong giai đoạn beta

Automation xử lý hàng trăm bản ghi cùng lúc thì nên chọn gói CaptchaAI nào?

  • Tính theo số bản ghi "pending" cùng lúc, không phải tổng số bản ghi trong bảng — CaptchaAI tính phí theo thread (số CAPTCHA giải song song), không theo lượt giải
  • Vài chục bản ghi mỗi đợt: BASIC ($15/tháng, 5 thread) là đủ
  • Automation thường xuyên kích hoạt hàng trăm bản ghi cùng lúc: cân nhắc STANDARD ($30/tháng, 15 thread) hoặc ADVANCE ($90/tháng, 50 thread) để tránh nghẽn hàng đợi

Vì sao script báo timeout trước khi CaptchaAI kịp trả token?

  • Automation script của Airtable chỉ chạy tối đa 30 giây, trong khi giải reCAPTCHA v2 có thể mất 15–30 giây tùy độ khó
  • Giảm số vòng lặp polling hoặc tăng thời gian chờ ban đầu (initial wait) lên 20 giây
  • Chuyển phần polling sang Scripting extension — nơi không bị giới hạn 30 giây như Automation

Lưu API key CaptchaAI trong Airtable bằng cách nào để không bị lộ trong script?

  • Trong Automation: khai báo API key như một input variable ở panel cấu hình script, không gõ trực tiếp vào code
  • Trong Scripting extension: dùng input.textAsync() để nhập key mỗi lần chạy thay vì hardcode

Bước tiếp theo

Tự động hóa việc giải CAPTCHA ngay từ cơ sở dữ liệu Airtable của bạn — lấy API key CaptchaAI và dựng Automation theo các bước trên.

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