Muốn giải Cloudflare Turnstile bằng CaptchaAI, việc đầu tiên bạn cần là hai giá trị: sitekey và pageurl của trang đích. Thiếu sitekey, request gửi tới in.php sẽ luôn báo lỗi. Bài viết này gom lại 3 cách trích xuất sitekey Turnstile — đọc thuộc tính DOM, bắt lệnh gọi turnstile.render(), và đọc src của iframe — kèm code Python, JavaScript chạy được ngay, cộng cách đưa token vào form sau khi CaptchaAI trả kết quả.
Sitekey Turnstile xuất hiện ở đâu?
Sitekey nằm ở một trong ba chỗ sau, tùy cách trang dựng widget:
- Thuộc tính
data-sitekeytrên phần tử.cf-turnstile - Tham số truyền vào lệnh gọi JavaScript
turnstile.render() - Query string trong
srccủa iframe Turnstile
Cách 1 rẻ nhất nên luôn thử trước. Nếu widget được dựng động bằng JavaScript, chuyển sang cách 2 hoặc cách 3.
Cách 1: đọc thuộc tính DOM
Nếu Turnstile nhúng tĩnh trong HTML, mở console trình duyệt và chạy đoạn sau để liệt kê mọi widget trên trang:
// Browser console
document.querySelectorAll('.cf-turnstile').forEach((el, i) => {
console.log(`Turnstile ${i}:`, {
sitekey: el.getAttribute('data-sitekey'),
action: el.getAttribute('data-action'),
cData: el.getAttribute('data-cdata'),
theme: el.getAttribute('data-theme'),
});
});
Kết quả in ra sitekey, action và theme của từng widget — đủ để gửi request tới CaptchaAI ngay.
Python (HTML tĩnh)
Nếu bạn lấy HTML qua request thay vì trình duyệt, dùng regex bắt data-sitekey trong response:
import re
import requests
html = requests.get("https://staging.example.com/qa-login").text
matches = re.findall(
r'class=["\'][^"\']*cf-turnstile[^"\']*["\'][^>]*data-sitekey=["\']([^"\']+)',
html
)
for sk in matches:
print(f"Sitekey: {sk}")
Cách này nhanh, không cần khởi động trình duyệt — phù hợp khi crawler chỉ đọc HTML thô.
Python (Selenium)
Khi trang cần JavaScript để render đầy đủ, dùng Selenium để đọc thuộc tính sau khi trang tải xong:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://staging.example.com/qa-login")
widgets = driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile")
for w in widgets:
sitekey = w.get_attribute("data-sitekey")
action = w.get_attribute("data-action")
print(f"Sitekey: {sitekey}, Action: {action}")
Cách 2: bắt lệnh gọi turnstile.render()
Một số trang không nhúng data-sitekey sẵn trong HTML mà dựng widget hoàn toàn bằng JavaScript:
turnstile.render('#captcha-container', {
sitekey: '0x4AAAAAAAB...',
callback: function(token) {
document.getElementById('cf-token').value = token;
},
});
Nếu bạn có sẵn source hoặc bundle JS đã tải, tìm lệnh gọi này bằng regex:
# Find turnstile.render calls
render_match = re.search(
r'turnstile\.render\s*\([^,]*,\s*\{([^}]+)\}',
html
)
if render_match:
config = render_match.group(1)
sk = re.search(r'sitekey\s*:\s*["\']([^"\']+)', config)
if sk:
print(f"Sitekey from render: {sk.group(1)}")
Chặn lệnh render bằng Puppeteer
Khi cả HTML lẫn bundle đều không lộ sitekey — ví dụ bị minify hoặc tải từ script bên thứ ba — cách chắc ăn nhất là chặn chính lệnh gọi turnstile.render() trước khi trang kịp thực thi:
// Intercept turnstile.render before page loads
await page.evaluateOnNewDocument(() => {
window.__turnstileParams = [];
const origRender = window.turnstile?.render;
Object.defineProperty(window, 'turnstile', {
set(val) {
this._turnstile = val;
const orig = val.render;
val.render = function(container, params) {
window.__turnstileParams.push(params);
console.log('Turnstile render:', JSON.stringify(params));
return orig.apply(this, arguments);
};
},
get() { return this._turnstile; }
});
});
await page.goto('https://staging.example.com/qa-login', { waitUntil: 'networkidle2' });
const params = await page.evaluate(() => window.__turnstileParams);
console.log('Captured Turnstile params:', params);
Đoạn script ghi đè window.turnstile trước khi trang load, nên bắt được params kể cả khi widget được dựng hoàn toàn bằng JavaScript động.
Cách 3: lấy sitekey từ iframe src
Turnstile luôn hiển thị qua iframe. Ngay cả khi hai cách trên không dùng được, bạn vẫn đọc được sitekey trong src của iframe sau khi widget đã render:
document.querySelectorAll('iframe').forEach(iframe => {
if (iframe.src.includes('challenges.cloudflare.com')) {
console.log('Turnstile iframe:', iframe.src);
const match = iframe.src.match(/sitekey=([A-Za-z0-9_-]+)/);
if (match) console.log('Sitekey:', match[1]);
}
});
Đây là fallback đáng tin cậy nhất: một khi widget đã mount, sitekey chắc chắn nằm trong URL của iframe.
Giải sitekey Turnstile bằng CaptchaAI
Có sitekey và pageurl rồi, gửi task tới in.php với method=turnstile rồi polling res.php cho tới khi có token:
Python
import requests
import time
API_KEY = "YOUR_API_KEY"
SITEKEY = "0x4AAAAAAAB..."
PAGE_URL = "https://staging.example.com/qa-login"
# Submit
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "turnstile",
"sitekey": SITEKEY,
"pageurl": PAGE_URL,
"json": "1",
}).json()
if resp["status"] != 1:
raise Exception(f"Submit error: {resp['request']}")
task_id = resp["request"]
# Poll
for _ in range(24):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": "1"
}).json()
if result["status"] == 1:
token = result["request"]
print(f"Turnstile token: {token[:50]}...")
break
if result["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Error: {result['request']}")
JavaScript
const axios = require('axios');
const submit = await axios.post('https://ocr.captchaai.com/in.php', null, {
params: {
key: 'YOUR_API_KEY',
method: 'turnstile',
sitekey: '0x4AAAAAAAB...',
pageurl: 'https://staging.example.com/qa-login',
json: 1,
}
});
const taskId = submit.data.request;
// Poll for result
let token = null;
for (let i = 0; i < 24; i++) {
await new Promise(r => setTimeout(r, 5000));
const poll = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: 'YOUR_API_KEY', action: 'get', id: taskId, json: 1 }
});
if (poll.data.status === 1) {
token = poll.data.request;
break;
}
}
console.log(`Token: ${token.substring(0, 50)}...`);
Turnstile thường giải xong trong dưới 10 giây với tỷ lệ giải thành công cao trên gói thread hiện có — không cần đổi endpoint hay logic polling so với các loại CAPTCHA khác của CaptchaAI, chỉ khác method.
Đưa token vào form
Turnstile lưu token trong input ẩn tên cf-turnstile-response. Sau khi CaptchaAI trả token, gán trực tiếp vào input rồi gọi lại callback của widget (nếu có) để trang nhận diện:
# Selenium
driver.execute_script("""
const input = document.querySelector('input[name="cf-turnstile-response"]');
if (input) input.value = arguments[0];
// Also set in the Turnstile widget's callback
const widget = document.querySelector('.cf-turnstile');
const callbackName = widget?.getAttribute('data-callback');
if (callbackName && typeof window[callbackName] === 'function') {
window[callbackName](arguments[0]);
}
""", token)
Ví dụ thực tế: QA checkout khi Turnstile bật đột ngột
Nhiều team QA tại các công ty outsourcing ở TP.HCM và Hà Nội gặp đúng tình huống này: Cloudflare bật Turnstile cho luồng đăng nhập hoặc checkout trên staging sau một lần cập nhật cấu hình, và bộ test tự động bắt đầu fail vì script không có sitekey. Quy trình 3 cách ở trên áp dụng nguyên vẹn — chỉ cần trỏ regex hoặc Selenium vào domain staging thay vì domain production, rồi nối vào bước gọi CaptchaAI phía trên. Với bot theo dõi giá trên các sàn như Shopee hay Lazada, tình huống tương tự cũng xảy ra khi trang sản phẩm bất ngờ bật Turnstile để chặn truy cập tự động — cách trích xuất sitekey không đổi, chỉ đổi URL mục tiêu.
Xử lý các lỗi thường gặp
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
Không tìm thấy phần tử .cf-turnstile |
Widget được render động | Đợi trang tải xong hoặc dùng MutationObserver |
| Sitekey rỗng | Sitekey được set qua API JavaScript, không nằm sẵn trong HTML | Tìm turnstile.render trong script, chuyển sang cách 2 |
| Token bị từ chối | pageurl hoặc sitekey không khớp trang đích |
Kiểm tra lại cả hai giá trị đúng với domain đang test |
Sai tham số method |
Dùng nhầm userrecaptcha cho Turnstile |
Đổi sang method=turnstile |
Câu hỏi thường gặp
Sitekey Turnstile khác gì sitekey reCAPTCHA?
Về bản chất giống nhau — một chuỗi định danh widget cho biết CAPTCHA đó thuộc trang nào. Khác biệt dễ thấy nhất là định dạng: sitekey Turnstile thường bắt đầu bằng 0x4AAAAAAA, còn sitekey reCAPTCHA thường bắt đầu bằng 6L. CaptchaAI phân biệt hai loại qua tham số method (turnstile so với userrecaptcha), không dựa vào định dạng sitekey.
Vì sao không tìm thấy phần tử .cf-turnstile dù trang chắc chắn có Turnstile?
Nhiều khả năng widget được dựng bằng JavaScript sau khi trang tải xong, hoặc nằm trong iframe/script bên thứ ba không xuất hiện ngay trong DOM ban đầu. Chuyển sang cách 2 (bắt turnstile.render()) hoặc cách 3 (đọc iframe src) thay vì tiếp tục query data-sitekey trên DOM ban đầu.
Turnstile có chế độ ẩn (invisible) không?
Có. Chế độ "managed" và "non-interactive" của Turnstile không hiển thị widget cho người dùng thấy, nhưng sitekey vẫn nằm ở đúng 3 vị trí nêu trên nên cả 3 cách trích xuất vẫn hoạt động bình thường.
Giải sitekey Turnstile bằng CaptchaAI mất bao lâu và tốn bao nhiêu?
Turnstile thường giải xong trong dưới 10 giây. Chi phí tính theo thread chứ không theo từng lần giải — gói BASIC ($15/tháng, 5 thread) đã đủ cho khối lượng test nhỏ, nâng lên ADVANCE ($90/tháng, 50 thread) khi cần chạy nhiều luồng song song.
Bắt đầu với CaptchaAI
Lấy API key tại captchaai.com và chạy thử ví dụ Python ở trên với sitekey vừa trích xuất được.