Trường Hợp Sử Dụng

Quy trình xác minh quảng cáo bằng cách xử lý CAPTCHA

Script quét 5.000 trang publisher mỗi ngày, và một nửa số request bị chặn giữa chừng? Không phải script lỗi — publisher tự vệ trước traffic tự động, bật CAPTCHA khi thấy nhiều request giống nhau từ cùng dải IP. Cách xử lý là gắn một bước giải CAPTCHA vào đúng chỗ bị chặn, để pipeline xác minh quảng cáo — vị trí, an toàn thương hiệu, khả năng hiển thị — chạy liên tục. Dưới đây là cách làm với CaptchaAI, kèm code Python chạy được ngay.

Vì sao đội ad ops hay gặp CAPTCHA khi xác minh quảng cáo

Agency và đội ad ops tại Việt Nam — kể cả nhóm outsourcing ở FPT Software, VNG hay agency quản lý campaign — thường quét hàng loạt trang báo (VnExpress, Dân Trí, Zing News) và sàn TMĐT (Shopee, Lazada, Tiki) để xác nhận quảng cáo đúng vị trí, đúng khu vực, không cạnh nội dung nhạy cảm. Traffic kiểm tra này giống một đợt scraping, và publisher soi ra rất nhanh vì các tín hiệu lặp lại đều đặn:

  • Cùng một user-agent xuất hiện trên hàng nghìn request liên tiếp.
  • Nhịp request đều tăm tắp — không có độ trễ ngẫu nhiên kiểu người dùng thật.
  • Nhiều request đến từ cùng một dải IP hoặc cùng một datacenter proxy.
  • Trình duyệt headless thiếu các tín hiệu render mà Cloudflare kỳ vọng, nên bị gắn cờ ngay ở lượt tải trang đầu tiên.

Khi một trong bốn tín hiệu này vượt ngưỡng, publisher bật CAPTCHA — thường là reCAPTCHA hoặc Cloudflare Turnstile — và script xác minh dừng lại giữa chừng nếu không có bước giải tự động.

Ví dụ thực tế: agency quét Shopee và VnExpress mỗi ngày

Một agency quản lý campaign cho khách hàng TMĐT thường chạy job xác minh vị trí quảng cáo trên khoảng 3.000 trang publisher mỗi ngày, chia đều giữa trang tin tức và trang danh mục sản phẩm trên Shopee/Lazada. Không có bước giải CAPTCHA, khoảng 30–40% request bị chặn giữa chừng vào các khung giờ cao điểm — đúng lúc dữ liệu vị trí quảng cáo quan trọng nhất. Gắn solve_captcha() vào đúng điểm bị chặn (như trong script bên dưới) giữ cho job chạy hết danh sách URL mà không cần người canh chừng và chạy lại thủ công mỗi khi gặp thử thách CAPTCHA.

Với khối lượng này, kết quả thực tế phụ thuộc vào tần suất CAPTCHA của từng publisher và gói CaptchaAI đang dùng — xem phần Câu hỏi thường gặp bên dưới về chi phí và số lượng thread cần thiết.

Những hạng mục cần kiểm tra khi xác minh quảng cáo

Hạng mục Mô tả Vì sao CAPTCHA chặn nó
Vị trí đặt quảng cáo Quảng cáo có hiển thị trong màn hình đầu tiên không? Truy cập trang tự động kích hoạt phát hiện bot
An toàn thương hiệu Không có quảng cáo cạnh nội dung có hại Kiểm tra hàng loạt URL trông giống scraping
Khả năng hiển thị Quảng cáo có thực sự được render không? Trình duyệt headless bị Cloudflare gắn cờ
Nhắm mục tiêu theo khu vực Đúng quảng cáo cho đúng khu vực Traffic qua proxy kích hoạt CAPTCHA
Giám sát đối thủ Đối thủ đang chạy quảng cáo gì? Tra cứu quảng cáo với tần suất cao

Triển khai: gắn bước giải CAPTCHA vào script xác minh

Script Python dưới đây mở trang publisher, giải reCAPTCHA nếu có, rồi kiểm tra tag quảng cáo và nội dung xung quanh để chấm điểm an toàn thương hiệu.

import requests
import time
import re
import json
import os
from datetime import datetime

API_KEY = os.environ["CAPTCHAAI_API_KEY"]

def solve_captcha(method, params):
    params["key"] = API_KEY
    params["method"] = method

    resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
    if not resp.text.startswith("OK|"):
        raise Exception(resp.text)

    task_id = resp.text.split("|")[1]
    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id,
        })
        if result.text == "CAPCHA_NOT_READY":
            continue
        if result.text.startswith("OK|"):
            return result.text.split("|", 1)[1]
        raise Exception(result.text)
    raise TimeoutError()

def verify_ad_placement(url, session):
    """Verify ad placement on a publisher page."""
    resp = session.get(url)

    # Solve CAPTCHA if present
    match = re.search(r'data-sitekey=["\']([A-Za-z0-9_-]+)["\']', resp.text)
    if match:
        token = solve_captcha("userrecaptcha", {
            "googlekey": match.group(1),
            "pageurl": url,
        })
        resp = session.post(url, data={"g-recaptcha-response": token})

    html = resp.text

    # Check for ad elements
    result = {
        "url": url,
        "timestamp": datetime.utcnow().isoformat(),
        "ads_found": [],
        "brand_safety": True,
        "captcha_solved": match is not None,
    }

    # Detect ad tags
    ad_patterns = [
        (r'googletag\.pubads', "Google Ad Manager"),
        (r'doubleclick\.net', "DFP/DoubleClick"),
        (r'ad\.doubleclick', "DoubleClick"),
        (r'amazon-adsystem', "Amazon Ads"),
        (r'criteo\.com/.*\.js', "Criteo"),
    ]

    for pattern, name in ad_patterns:
        if re.search(pattern, html):
            result["ads_found"].append(name)

    # Brand safety check — flag problematic content
    safety_keywords = [
        "violence", "hate speech", "explicit",
        "gambling", "illegal",
    ]
    page_text = re.sub(r'<[^>]+>', '', html).lower()
    for keyword in safety_keywords:
        if keyword in page_text:
            result["brand_safety"] = False
            break

    return result

def run_verification(urls, output_file="verification_report.json"):
    """Run ad verification across multiple publisher URLs."""
    session = requests.Session()
    session.headers["User-Agent"] = (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 Chrome/120.0.0.0"
    )

    results = []
    for i, url in enumerate(urls):
        try:
            result = verify_ad_placement(url, session)
            results.append(result)
            ads = ", ".join(result["ads_found"]) or "None"
            safe = "SAFE" if result["brand_safety"] else "UNSAFE"
            print(f"  [{i+1}/{len(urls)}] {url}: {ads} [{safe}]")
        except Exception as e:
            results.append({
                "url": url,
                "error": str(e),
                "timestamp": datetime.utcnow().isoformat(),
            })
            print(f"  [{i+1}/{len(urls)}] {url}: ERROR - {e}")

        time.sleep(2)

    with open(output_file, "w") as f:
        json.dump(results, f, indent=2)

    # Summary
    total = len(results)
    safe = sum(1 for r in results if r.get("brand_safety"))
    captchas = sum(1 for r in results if r.get("captcha_solved"))
    errors = sum(1 for r in results if "error" in r)

    print(f"\n  Total: {total} | Safe: {safe} | CAPTCHAs solved: {captchas} | Errors: {errors}")

    return results

# Publisher URLs to verify
publisher_urls = [
    "https://publisher1.com/article/tech-news",
    "https://publisher2.com/sports/latest",
    "https://publisher3.com/finance/markets",
]

run_verification(publisher_urls)

Mở rộng sang các publisher dùng Cloudflare

Publisher lớn thường chạy sau Cloudflare — vừa có Turnstile trên form, vừa có full-page challenge:

def handle_cloudflare(url, session):
    """Handle Cloudflare-protected publisher pages."""
    resp = session.get(url)

    if "cf-turnstile" in resp.text:
        match = re.search(r'data-sitekey=["\']([^"\']+)', resp.text)
        if match:
            token = solve_captcha("turnstile", {
                "sitekey": match.group(1),
                "pageurl": url,
            })
            return session.post(url, data={
                "cf-turnstile-response": token,
            })

    if resp.status_code == 403 and "cf-browser-verification" in resp.text:
        data = solve_captcha("cloudflare_challenge", {
            "pageurl": url,
            "proxy": "user:pass@proxy:port",
            "proxytype": "HTTP",
        })
        # Parse qa_session_cookie and use same proxy
        return data

    return resp

Ghi lại thời điểm giải, task_id và trạng thái brand_safety cho mỗi lần chạy giúp đội ad ops có audit log rõ ràng — hữu ích khi cần đối chiếu dữ liệu đã thu thập theo tinh thần Nghị định 13/2023/NĐ-CP về bảo vệ dữ liệu cá nhân (không lưu trữ dữ liệu ngoài phạm vi cần cho việc xác minh).

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

Xác minh quảng cáo tự động bằng CaptchaAI tốn bao nhiêu?

CaptchaAI tính phí theo thread, không theo mỗi lần giải. Với vài nghìn trang/ngày, gói STANDARD ($30/tháng, 15 thread) hoặc ADVANCE ($90/tháng, 50 thread) thường đủ.

CaptchaAI giải được những loại CAPTCHA nào mà publisher hay dùng?

CaptchaAI hỗ trợ reCAPTCHA v2/v3, Cloudflare Turnstile, Cloudflare Challenge, GeeTest v3, CAPTCHA ảnh/OCR và BLS — đủ cho phần lớn CAPTCHA trên trang publisher và sàn TMĐT. hCaptcha và FunCaptcha (Arkose Labs) hiện chưa được hỗ trợ.

Nên dùng polling hay callback khi verify hàng nghìn URL mỗi ngày?

Vài nghìn URL/ngày thì polling res.php như trong ví dụ trên là đủ đơn giản. Khi khối lượng lên hàng chục nghìn trang/ngày, chuyển sang callback để giảm request polling thừa.

Xác minh quảng cáo video có cần cách làm khác không?

Có. Cách trên phù hợp quảng cáo hiển thị và quảng cáo gốc; quảng cáo video cần render trình duyệt thật bằng Selenium hoặc Playwright để bắt sự kiện phát video.

Log xác minh có cần tuân thủ quy định bảo vệ dữ liệu cá nhân không?

Có, nếu log lưu thông tin có thể định danh người dùng trên trang publisher. Chỉ lưu URL, timestamp và kết quả kiểm tra (như script mẫu ở trên) — tránh lưu nội dung trang hoặc dữ liệu cá nhân ngoài phạm vi cần cho xác minh quảng cáo, phù hợp tinh thần Nghị định 13/2023/NĐ-CP.

Hướng dẫn liên quan

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