Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 3 — Thu thập web: search, chọn nguồn và fetch nội dung

Đã đọc 0/10 mục · còn khoảng 4 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

Đây là khâu "lấy nguyên liệu". Chất lượng báo cáo cuối phụ thuộc trực tiếp vào việc bạn tìm và chọn nguồn tốt. Rác vào thì rác ra (garbage in, garbage out).

Ba bước con của khâu thu thập

  • Query expansion — biến 1 câu hỏi thành nhiều truy vấn.
  • Search & rank — gọi search API, chấm điểm nguồn.
  • Fetch & clean — tải trang được chọn, bóc lấy text chính.

Bước 1: Query expansion

Một truy vấn hiếm khi đủ. Câu hỏi "phí rút tiền của ví điện tử đối thủ" nên tách thành nhiều query cụ thể theo từng đối thủ và từng khía cạnh.

def expand_queries(goal, competitors):
    qs = []
    for c in competitors:
        qs.append(f"{c} phí rút tiền 2026")
        qs.append(f"{c} biểu phí chính thức site:{c}.vn")
    return qs

Mẹo: thêm năm hiện tại và site: domain chính thức để ưu tiên nguồn gốc thay vì bài viết lại.

Bước 2: Search và chấm điểm nguồn

Không phải link nào cũng đáng đọc. Chấm điểm để đọc nguồn tốt trước, tiết kiệm token:

def score_source(item, official_domains):
    score = 0
    url = item["url"].lower()
    if any(d in url for d in official_domains):
        score += 5          # trang chính thức
    if "2026" in item.get("title", ""):
        score += 2          # mới
    if any(b in url for b in ["blogspot", "pinterest"]):
        score -= 3          # nguồn rác
    return score

Ưu tiên: trang chính thức > báo uy tín > blog phân tích > diễn đàn. Với CI, trang tuyển dụng và trang giá của chính đối thủ là vàng.

Bước 3: Fetch và làm sạch

Snippet ngắn dễ gây hiểu sai. Tải nguyên trang rồi bóc phần nội dung chính (bỏ menu, quảng cáo):

import httpx, trafilatura

def fetch_clean(url, timeout=10):
    try:
        html = httpx.get(url, timeout=timeout, follow_redirects=True).text
        text = trafilatura.extract(html) or ""
        return text[:8000]     # cắt để kiểm soát token
    except Exception as e:
        return ""              # fail an toàn, không làm sập loop

Luôn cắt độ dài ([:8000]) để một trang dài không nuốt hết ngân sách token.

Xử lý trang chặn bot / render JS

  • Nếu fetch_clean trả rỗng nhiều lần với một domain → có thể trang chặn bot hoặc render bằng JS.
  • Khi đó dùng Playwright (headless) cho riêng domain đó, đừng dùng cho tất cả (chậm và tốn).
  • Tôn trọng robots.txt và đặt User-Agent trung thực; thêm delay giữa các request để không dội máy chủ.

Khung tư duy chọn nguồn: 3C

  • Credible — ai xuất bản? Có phải nguồn gốc không?
  • Current — thông tin của năm nào?
  • Consistent — có ít nhất 2 nguồn nói giống nhau không?
Một con số chỉ xuất hiện ở 1 blog vô danh thì chưa đủ tin để đưa vào báo cáo.

Sai lầm thường gặp

  • Chỉ dùng snippet rồi kết luận — snippet cắt xén, dễ hiểu sai ngữ cảnh.
  • Đọc mọi link không lọc — đốt token vào nguồn rác.
  • Không cắt độ dài trang — một trang 50k từ làm nổ ngân sách.
  • Fetch làm sập cả loop khi 1 URL lỗi — thiếu try/except.
  • Tin số liệu từ 1 nguồn duy nhất — không đối chiếu.
  • Bỏ qua ngày tháng — lấy nhầm biểu phí của 2 năm trước.

Checklist khâu thu thập

  • [ ] Mỗi đối thủ có ít nhất 2-3 query
  • [ ] Nguồn được chấm điểm và sắp xếp
  • [ ] Fetch có try/except, có cắt độ dài
  • [ ] Ưu tiên domain chính thức
  • [ ] Mỗi dữ kiện quan trọng có ≥2 nguồn

Ví dụ: một vòng thu thập hoàn chỉnh

def collect(goal, competitors, official_domains):
    queries = expand_queries(goal, competitors)
    hits = []
    for q in queries:
        for item in tv.search(q, max_results=5)["results"]:
            item["score"] = score_source(item, official_domains)
            hits.append(item)
    hits.sort(key=lambda x: x["score"], reverse=True)
    texts = {}
    for item in hits[:12]:              # chỉ đọc 12 nguồn tốt nhất
        t = fetch_clean(item["url"])
        if t:
            texts[item["url"]] = t
    return texts

Lưu ý ba quyết định kiểm soát chi phí trong đoạn này: giới hạn max_results=5 mỗi query, chỉ fetch 12 nguồn điểm cao nhất, và bỏ qua trang fetch rỗng. Ba giới hạn nhỏ này quyết định phần lớn chi phí một lần chạy — hãy chỉnh chúng theo ngân sách thay vì đọc mọi thứ.

Bài 4: biến đống text thô này thành dữ liệu có cấu trúc để so sánh.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi