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

Thu thập dữ liệu nghiên cứu thị trường bằng cách xử lý CAPTCHA

Một đội outsourcing ở TP.HCM cần theo dõi giá, đánh giá và tin tuyển dụng của hàng chục đối thủ mỗi ngày trên Shopee, G2, LinkedIn — nhưng cứ vài chục trang lại dính reCAPTCHA hoặc Turnstile, làm gãy script. CaptchaAI giải các CAPTCHA này qua API để pipeline chạy liên tục, không cần canh giải tay.

Nguồn dữ liệu nghiên cứu thị trường và loại CAPTCHA thường gặp

Mỗi nhóm nguồn thường gắn với một loại CAPTCHA đặc trưng — biết trước giúp chọn đúng method:

Loại nguồn Ví dụ Loại CAPTCHA
Trang đánh giá G2, Trustpilot, Yelp reCAPTCHA v2/v3
Trang tuyển dụng LinkedIn, Indeed Cloudflare Challenge
Danh bạ doanh nghiệp YellowPages, Crunchbase Cloudflare Turnstile, reCAPTCHA
Mạng xã hội Twitter/X, Reddit Nhiều loại khác nhau
Cơ sở dữ liệu bằng sáng chế USPTO, Google Patents reCAPTCHA v2
Dữ liệu chính phủ Hồ sơ SEC, dữ liệu điều tra dân số CAPTCHA hình ảnh

Xây dựng pipeline thu thập dữ liệu với CaptchaAI

Luồng xử lý gồm bốn bước, lặp lại cho mọi request bị chặn:

  1. Phát hiện sitekey trong HTML của trang vừa fetch
  2. Gửi task tới in.php, nhận về task_id
  3. Polling res.php mỗi 5 giây tới khi có token
  4. Gắn token vào request tiếp theo (g-recaptcha-response hoặc cf-turnstile-response) rồi gửi lại

Cùng một class dùng cho cả trang đánh giá lẫn thư mục doanh nghiệp, chỉ khác hàm parse:

import requests
import time
import re
import csv
import os

API_KEY = os.environ["CAPTCHAAI_API_KEY"]

def solve_captcha(params):
    params["key"] = API_KEY
    resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
    if not resp.text.startswith("OK|"):
        raise Exception(f"Submit: {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(f"Solve: {result.text}")
    raise TimeoutError()

class MarketResearchCollector:
    def __init__(self):
        self.session = requests.Session()
        self.session.headers["User-Agent"] = (
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/120.0.0.0"
        )

    def fetch(self, url):
        """Fetch a page, solving CAPTCHAs as needed."""
        resp = self.session.get(url)

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

        # Detect Turnstile
        match = re.search(
            r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', resp.text
        )
        if match and "cf-turnstile" in resp.text:
            token = solve_captcha({
                "method": "turnstile",
                "sitekey": match.group(1),
                "pageurl": url,
            })
            resp = self.session.post(url, data={
                "cf-turnstile-response": token,
            })

        return resp.text

    def collect_reviews(self, urls):
        """Collect review data from multiple pages."""
        reviews = []
        for url in urls:
            try:
                html = self.fetch(url)
                page_reviews = self._parse_reviews(html)
                reviews.extend(page_reviews)
                print(f"  Collected {len(page_reviews)} reviews from {url}")
                time.sleep(2)  # Polite delay
            except Exception as e:
                print(f"  Error on {url}: {e}")
        return reviews

    def collect_company_profiles(self, urls):
        """Collect company profile data."""
        profiles = []
        for url in urls:
            try:
                html = self.fetch(url)
                profile = self._parse_profile(html)
                if profile:
                    profiles.append(profile)
                    print(f"  Collected: {profile.get('name', 'Unknown')}")
                time.sleep(2)
            except Exception as e:
                print(f"  Error on {url}: {e}")
        return profiles

    def _parse_reviews(self, html):
        """Extract review data from HTML."""
        reviews = []
        # Generic review extraction patterns
        for match in re.finditer(
            r'class="review-text"[^>]*>(.*?)</div>', html, re.DOTALL
        ):
            reviews.append({
                "text": match.group(1).strip()[:500],
            })
        return reviews

    def _parse_profile(self, html):
        """Extract company profile from HTML."""
        name = re.search(r'<h1[^>]*>(.*?)</h1>', html)
        desc = re.search(
            r'class="description"[^>]*>(.*?)</div>', html, re.DOTALL
        )
        return {
            "name": name.group(1).strip() if name else None,
            "description": desc.group(1).strip()[:300] if desc else None,
        }

    def export_csv(self, data, filename):
        """Export collected data to CSV."""
        if not data:
            return
        keys = data[0].keys()
        with open(filename, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=keys)
            writer.writeheader()
            writer.writerows(data)
        print(f"Exported {len(data)} records to {filename}")

Chạy thử pipeline

Khởi tạo collector rồi truyền danh sách URL cần thu thập:

collector = MarketResearchCollector()

# Collect competitor reviews
review_urls = [
    "https://example-reviews.com/product/competitor-a",
    "https://example-reviews.com/product/competitor-b",
    "https://example-reviews.com/product/competitor-c",
]
reviews = collector.collect_reviews(review_urls)
collector.export_csv(reviews, "competitor_reviews.csv")

# Collect company profiles
profile_urls = [
    "https://example-directory.com/company/alpha-corp",
    "https://example-directory.com/company/beta-inc",
]
profiles = collector.collect_company_profiles(profile_urls)
collector.export_csv(profiles, "company_profiles.csv")

Mẹo mở rộng quy mô pipeline

Khi pipeline vượt quá vài chục trang/ngày, siết lại theo các mốc sau:

  • Khoảng cách request: 2-5 giây/trang, đừng gửi dồn dập
  • Collector đồng thời: 5-10 luồng là đủ cho quy mô vừa
  • Nguồn request (proxy): đa dạng hoá khi vượt 100 trang/giờ
  • Chống trùng lặp: khấu trừ theo hàm băm trước khi lưu
  • Lịch chạy: hàng ngày hoặc hàng tuần cho dữ liệu xu hướng, không cần real-time

Nếu một trang chặn hoàn toàn crawler, kết hợp CaptchaAI với việc đa dạng nguồn request và mẫu request giống hành vi thật thay vì gửi dồn dập — xem bài dùng proxy hiệu quả khi thu thập dữ liệu tránh bị chặn.

Ứng dụng thực tế trong nghiên cứu thị trường

Theo dõi giá đối thủ trên sàn thương mại điện tử

Theo dõi giá, khuyến mãi và tồn kho trên Shopee, Lazada, Tiki hoặc các sàn quốc tế. Dữ liệu thường cần theo dõi:

  • Giá niêm yết và giá sau mã giảm giá theo từng SKU
  • Số lượng đã bán / tồn kho hiển thị công khai
  • Tần suất đổi giá theo khung giờ (flash sale)

Nhiều đội ngũ tại Việt Nam chạy việc này hàng ngày để cập nhật bảng giá B2B — chỉ thu thập dữ liệu công khai trên catalogue, không đụng thông tin cá nhân người mua.

Phân tích cảm nhận thương hiệu qua đánh giá

Thu thập đánh giá, xếp hạng từ các nền tảng review rồi tổng hợp cảm nhận trên nhiều nguồn để so sánh thương hiệu theo thời gian.

Phân tích xu hướng tuyển dụng

Quét tin tuyển dụng trên các job board để nắm xu hướng ngành, cụ thể:

  • Mức lương tham khảo theo vị trí và cấp bậc
  • Kỹ năng xuất hiện lặp lại trong mô tả công việc
  • Số lượng tin đăng mới theo tuần, theo ngành

Theo dõi xu hướng bằng sáng chế

Thu thập hồ sơ bằng sáng chế từ cơ sở dữ liệu công khai để theo dõi hướng đổi mới và R&D đối thủ.

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

Giải CAPTCHA cho dữ liệu nghiên cứu thị trường tốn bao nhiêu?

Một dự án nghiên cứu thị trường điển hình kích hoạt CAPTCHA trên khoảng 30% số trang. Với 1.000 trang/ngày, ước tính ~300 lần giải, tổng chi phí khoảng $0,5–3 — gói BASIC ($15/tháng, 5 thread) thường đủ.

Nên dùng polling hay webhook khi thu thập dữ liệu ở quy mô lớn?

Polling (res.php định kỳ) đơn giản, đủ dùng cho vài trăm task/ngày. Ở quy mô hàng nghìn task song song, webhook (pingback) giảm request thừa vì CaptchaAI tự gửi kết quả về URL của bạn.

Thu thập dữ liệu công khai để nghiên cứu thị trường có hợp pháp tại Việt Nam không?

Thường được phép, nhưng vẫn cần kiểm tra điều khoản dịch vụ từng trang.

Nếu dữ liệu chạm tới thông tin cá nhân, áp dụng Nghị định 13/2023/NĐ-CP về bảo vệ dữ liệu cá nhân — tránh thu thập email, số điện thoại hay dữ liệu định danh nếu không có căn cứ pháp lý rõ ràng.

Cần bao nhiêu thread để chạy pipeline 5.000-10.000 trang/ngày?

CaptchaAI tính phí theo số thread đồng thời, không giới hạn số lần giải mỗi thread. Với ~3.000 CAPTCHA/ngày (ước tính từ 10.000 trang, tỷ lệ kích hoạt ~30%), gói STANDARD ($30/tháng, 15 thread) thường đủ cho 10-15 collector song song; nhiều hơn thì chuyển lên ADVANCE ($90/tháng, 50 thread).

Hướng dẫn liên quan

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