Có thể giải CAPTCHA hình ảnh ngay trong Google Trang tính, không cần rời trình duyệt hay dựng server riêng — chỉ một hàm Apps Script gọi UrlFetchApp tới CaptchaAI, kết quả trả thẳng về ô tính như bất kỳ công thức nào khác.
Khi nào nên dùng cách này
Hợp khi dữ liệu đã có sẵn trong Trang tính — URL ảnh CAPTCHA từ crawler, ảnh xác minh export nội bộ, hoặc CAPTCHA văn bản cần OCR. Team QA, outsourcing ở TP.HCM, Hà Nội hay gom dữ liệu này trước khi đưa qua pipeline chính thức, ví dụ cột link ảnh CAPTCHA khi theo dõi giá sàn TMĐT. Một hàm xử lý trọn luồng:
- Tìm nạp từng ảnh CAPTCHA từ URL trong cột
- Chuyển ảnh sang base64
- Gửi ảnh tới CaptchaAI
- Ghi văn bản đã giải ngược lại vào ô bên cạnh
Ưu điểm và giới hạn của cách làm này
- Không cần server hay cron job riêng — Trang tính tự chứa toàn bộ luồng xử lý
- API key lưu trong Script Properties, không lộ ra trong code
- Custom function giới hạn 30 giây, chỉ hợp với vài chục ảnh mỗi lần gọi
- Khối lượng lớn hơn cần menu hàng loạt (6 phút) hoặc worker chạy ngoài Apps Script
Bước 1: Tạo project Apps Script trong Trang tính
Trong Google Trang tính của bạn:
- Đi tới Tiện ích mở rộng → Apps Script
- Tạo một tệp tập lệnh mới, đặt tên
captchaai.gs
Tên
captchaai.gsgiúp dễ tìm khi project có nhiều file script.
Bước 2: Viết hàm giải CAPTCHA gọi CaptchaAI
Đoạn code định nghĩa hai hàm tùy chỉnh (SOLVE_CAPTCHA cho URL ảnh, SOLVE_CAPTCHA_BASE64 cho base64 có sẵn) cùng hai hàm nội bộ để gửi task và polling kết quả:
// captchaai.gs
// Store your API key in Script Properties for security
// File → Project Properties → Script Properties → Add: CAPTCHAAI_KEY
function getApiKey() {
return PropertiesService.getScriptProperties().getProperty('CAPTCHAAI_KEY');
}
/**
* Solve an image CAPTCHA from a URL.
* @param {string} imageUrl URL of the CAPTCHA image to solve.
* @return {string} The solved CAPTCHA text.
* @customfunction
*/
function SOLVE_CAPTCHA(imageUrl) {
if (!imageUrl) return 'No URL provided';
try {
// Fetch the image and convert to base64
var imageResponse = UrlFetchApp.fetch(imageUrl);
var imageBlob = imageResponse.getBlob();
var base64Image = Utilities.base64Encode(imageBlob.getBytes());
// Submit to CaptchaAI
var taskId = submitCaptcha(base64Image);
if (!taskId) return 'Submit failed';
// Poll for result
var result = pollForResult(taskId);
return result || 'Solve failed';
} catch (e) {
return 'Error: ' + e.message;
}
}
/**
* Solve an image CAPTCHA from base64 text in a cell.
* @param {string} base64Image Base64-encoded CAPTCHA image.
* @return {string} The solved CAPTCHA text.
* @customfunction
*/
function SOLVE_CAPTCHA_BASE64(base64Image) {
if (!base64Image) return 'No image data';
try {
var taskId = submitCaptcha(base64Image);
if (!taskId) return 'Submit failed';
var result = pollForResult(taskId);
return result || 'Solve failed';
} catch (e) {
return 'Error: ' + e.message;
}
}
function submitCaptcha(base64Image) {
var apiKey = getApiKey();
var payload = {
'key': apiKey,
'method': 'base64',
'body': base64Image,
'json': '1'
};
var options = {
'method': 'post',
'payload': payload,
'muteHttpExceptions': true
};
var response = UrlFetchApp.fetch(
'https://ocr.captchaai.com/in.php', options
);
var result = JSON.parse(response.getContentText());
if (result.status === 1) {
return result.request;
}
Logger.log('Submit error: ' + result.request);
return null;
}
function pollForResult(taskId) {
var apiKey = getApiKey();
var maxAttempts = 20;
// Initial wait
Utilities.sleep(5000);
for (var i = 0; i < maxAttempts; i++) {
var url = 'https://ocr.captchaai.com/res.php'
+ '?key=' + apiKey
+ '&action=get'
+ '&id=' + taskId
+ '&json=1';
var response = UrlFetchApp.fetch(url, {muteHttpExceptions: true});
var result = JSON.parse(response.getContentText());
if (result.status === 1) {
return result.request;
}
if (result.request !== 'CAPCHA_NOT_READY') {
Logger.log('Solve error: ' + result.request);
return null;
}
Utilities.sleep(5000);
}
return null; // Timeout
}
Bước 3: Lưu API key an toàn trong Script Properties
Không hard-code API key trong code — lưu trong Script Properties:
- Trong trình chỉnh sửa Apps Script, nhấp Cài đặt dự án (biểu tượng bánh răng)
- Cuộn xuống Thuộc tính tập lệnh
- Nhấp Thêm thuộc tính tập lệnh
- Property:
CAPTCHAAI_KEY, Value: API key CaptchaAI của bạn - Lưu lại
Không cần bước xác thực nào khác — API key trong Script Properties là đủ.
Bước 4: Gọi hàm ngay trong Trang tính
Dùng như một công thức thông thường
Dùng trực tiếp trong ô với URL ảnh, hoặc với dữ liệu base64 đã có sẵn ở ô khác:
=SOLVE_CAPTCHA("https://example.com/captcha-image.jpg")
=SOLVE_CAPTCHA_BASE64(A2)
Giải hàng loạt bằng menu tùy chỉnh
Gọi SOLVE_CAPTCHA trực tiếp tiện cho vài chục ảnh, nhưng cột hàng trăm dòng sẽ hết timeout 30 giây của custom function. Giải pháp: thêm menu chạy hàm riêng (giới hạn 6 phút):
function onOpen() {
SpreadsheetApp.getUi().createMenu('CaptchaAI')
.addItem('Solve Column A CAPTCHAs', 'solveColumnCaptchas')
.addItem('Check Balance', 'checkBalance')
.addToUi();
}
function solveColumnCaptchas() {
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var urls = sheet.getRange('A2:A' + lastRow).getValues();
for (var i = 0; i < urls.length; i++) {
if (!urls[i][0]) continue;
var row = i + 2;
var existingResult = sheet.getRange('B' + row).getValue();
if (existingResult) continue; // Skip already solved
sheet.getRange('B' + row).setValue('Solving...');
SpreadsheetApp.flush();
var result = SOLVE_CAPTCHA(urls[i][0]);
sheet.getRange('B' + row).setValue(result);
sheet.getRange('C' + row).setValue(new Date()); // Timestamp
// Avoid hitting Apps Script quotas
Utilities.sleep(1000);
}
SpreadsheetApp.getUi().alert('Batch solving complete!');
}
function checkBalance() {
var apiKey = getApiKey();
var url = 'https://ocr.captchaai.com/res.php'
+ '?key=' + apiKey
+ '&action=getbalance'
+ '&json=1';
var response = UrlFetchApp.fetch(url);
var result = JSON.parse(response.getContentText());
SpreadsheetApp.getUi().alert(
'CaptchaAI Balance: $' + result.request
);
}
Menu thêm mục Check Balance để xem số dư ngay trong Trang tính, không cần mở dashboard riêng.
Bước 5: Kết quả trông như thế nào trên trang tính
Sau khi chạy solveColumnCaptchas, cột B và C tự điền theo từng dòng:
https://example.com/captcha1.jpg→ABC123, giải lúc2026-04-04 10:15:00https://example.com/captcha2.jpg→XYZ789, giải lúc2026-04-04 10:15:12https://example.com/captcha3.jpg→ vẫn hiệnGiải quyết...cho tới khi có kết quả
Cột thời điểm giải giúp đối chiếu khi audit dữ liệu (chi tiết ở FAQ bên dưới).
Xử lý sự cố thường gặp
Lỗi hay gặp khi chạy SOLVE_CAPTCHA và cách xử lý:
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
TypeError: Cannot read property 'getProperty' |
Chưa đặt Script Properties | Thêm CAPTCHAAI_KEY trong Cài đặt dự án |
Exception: Address unavailable |
URL hình ảnh bị chặn hoặc không hợp lệ | Xác minh URL ảnh có thể truy cập được từ máy chủ Google |
Hàm tùy chỉnh trả về Loading... mãi không dừng |
Vượt timeout 30 giây của custom function | Chuyển sang cách tiếp cận menu hàng loạt — hàm menu có giới hạn 6 phút |
ERROR_ZERO_BALANCE |
Tài khoản hết credit | Nạp thêm vào số dư CaptchaAI |
Vượt hạn ngạch UrlFetchApp |
Gọi API quá nhiều trong ngày | Thêm Utilities.sleep() giữa các lần gọi; chia nhỏ theo lô |
Câu hỏi thường gặp
Giới hạn thời gian thực thi của Apps Script khi giải CAPTCHA là bao nhiêu?
- Custom function: 30 giây
- Hàm chạy từ menu: 6 phút
- Trigger theo thời gian: 6 phút (30 phút Workspace)
Dùng menu/trigger cho lô lớn; công thức SOLVE_CAPTCHA chỉ hợp số lượng nhỏ.
Apps Script có giải được reCAPTCHA v2, hay chỉ giải CAPTCHA hình ảnh?
Có, nếu thêm sitekey và pageurl của trang đích — dùng method=userrecaptcha thay vì method=base64, kèm tham số googlekey và pageurl.
Làm sao kiểm tra số dư CaptchaAI ngay trong Trang tính?
- Mở menu CaptchaAI vừa thêm ở Bước 4
- Chọn Check Balance
- Hàm
checkBalance()gọiaction=getbalancevà hiện số dư trong hộp thoại — không cần mở dashboard riêng
Khi nào nên chuyển từ Apps Script sang worker Python/Node để giải hàng loạt?
- Khối lượng vượt vài nghìn CAPTCHA/ngày
- Chạm hạn ngạch
UrlFetchApp(20.000 lệnh/ngày cá nhân, 100.000 Workspace) - Trước đó, Trình kích hoạt (Chỉnh sửa → Trình kích hoạt) chạy
solveColumnCaptchastheo lịch vẫn là bước trung gian đủ dùng
Cột thời điểm giải trong Bước 5 có đủ cho audit dữ liệu theo Nghị định 13/2023/NĐ-CP không?
- Là điểm khởi đầu hợp lý, chưa phải hồ sơ audit đầy đủ
- Muốn truy vết kỹ hơn, ghi thêm người thực thi bằng
Session.getActiveUser().getEmail() - Đây là gợi ý kỹ thuật, không phải tư vấn pháp lý
Bước tiếp theo
Dán script vào Trang tính, thêm CAPTCHAAI_KEY, thử SOLVE_CAPTCHA với URL ảnh thật — lấy API key CaptchaAI tại đây. Xem thêm giải CAPTCHA không cần code với Zapier, giải 1.000 CAPTCHA hình ảnh hàng loạt, hoặc tích hợp Make (Integromat).