🎯 Trình duyệt đám mây tùy chỉnh, chống phát hiện được hỗ trợ bởi Chromium tự phát triển, thiết kế dành cho trình thu thập dữ liệu webtác nhân AI. 👉Dùng thử ngay
Quay lại blog

Cách xây dựng một trình thu thập dữ liệu tự phục hồi

Emily Chen
Emily Chen

Advanced Data Extraction Specialist

05-Aug-2026

Tóm tắt:

  • Một bộ chọn CSS hoạt động hôm nay sẽ bị hỏng vào sáng hôm sau nếu một trang web đổi tên một lớp, và một công cụ thu thập thông tin bình thường sẽ im lặng trả về không gì cả.
  • Một công cụ thu thập thông tin tự sửa chữa sẽ sửa chữa bộ chọn tại thời điểm chạy: nó lấy trang, thử bộ chọn đã lưu, và nếu không thành công, sẽ yêu cầu một mô hình ngôn ngữ để đọc một bộ chọn mới từ DOM trực tiếp.
  • Dòng xử lý này lấy trang đã được render qua API Thu thập thông tin Tổng hợp Scrapeless, vì vậy mô hình luôn được sửa chữa dựa trên markup thực tế đã được thực thi bởi script thay vì một shell trống rỗng.
  • Một bài chạy trực tiếp cho thấy bộ chọn đã lưu .author-name không khớp với phần tử nào, sau đó deepseek-v4-flash đề xuất small.author, cái này trích xuất tất cả mười tác giả.
  • Bộ chọn đã được sửa chữa được xác thực với DOM trước khi được tin tưởng — một phỏng đoán sai sẽ không khớp với gì và sẽ bị loại bỏ, vì vậy một gợi ý xấu không bao giờ lặng lẽ làm hỏng dữ liệu của bạn.
  • Nhận một khóa API Scrapeless trên gói miễn phí và chạy toàn bộ quy trình từ đầu đến cuối.

Dòng xử lý qua các giai đoạn

Một công cụ thu thập thông tin không bị hỏng bởi vì mã sai mà bởi vì trang đã đổi vị trí. Cách sửa chữa là xem bộ chọn như dữ liệu có thể được tái sinh, không phải là một hằng số đã được cắm vào trong mã nguồn. Dòng xử lý này thực hiện điều đó qua năm giai đoạn:

lấy trang đã render (Scrapeless) → thử bộ chọn đã lưu → phát hiện sự cố → sửa chữa với mô hình ngôn ngữ → xác thực và trích xuất → lưu trữ bộ chọn hoạt động

Mỗi giai đoạn bên dưới đều chạy chống lại trang web trực tiếp. Lấy và sửa chữa là hai cuộc gọi rời khỏi máy của bạn; mọi thứ khác là phân tích cục bộ mà bạn có thể kiểm tra.

Giai đoạn 1: Lấy trang đã render với Scrapeless

Việc sửa chữa chỉ hiệu quả như HTML bạn hiện cho mô hình, vì vậy, việc lấy trang phải trả về trang mà trình duyệt sẽ xây dựng, chứ không phải là container trống mà một trang web được render bởi client gửi lần đầu. Việc lấy trang đã được render là nhiệm vụ của API Thu thập thông tin Tổng hợp Scrapeless. API Thu thập thông tin Tổng hợp sẽ render trang ở phía máy chủ với js_render và trả về HTML hoàn chỉnh trong trường data của nó. Cài đặt hai thư viện mà dòng xử lý sử dụng:

bash Copy
pip install requests beautifulsoup4

Đặt cả hai khóa vào môi trường — khóa Scrapeless cho việc lấy, khóa mô hình cho việc sửa chữa:

bash Copy
export SCRAPELESS_API_KEY="your_scrapeless_api_key"
export OPENROUTER_API_KEY="your_openrouter_api_key"

Giai đoạn 2: Thử bộ chọn đã lưu và phát hiện sự cố

Chạy công cụ thu thập thông tin của tháng trước trên trang của ngày hôm nay và sự cố sẽ yên lặng: bộ chọn không khớp với gì và việc trích xuất trả về một danh sách trống, vì vậy một công việc đơn giản sẽ viết không có hàng nào và báo cáo thành công. Trang demo bên dưới xây dựng các trích dẫn của nó bằng JavaScript; bộ chọn đã lưu .author-name là một lớp mà markup không còn sử dụng, đại diện cho bất kỳ bộ chọn nào mà việc thiết kế lại đã đổi tên:

python Copy
import os, requests
from bs4 import BeautifulSoup

def fetch(url):
    r = requests.post(
        "https://api.scrapeless.com/api/v2/unlocker/request",
        headers={"x-api-token": os.environ["SCRAPELESS_API_KEY"], "Content-Type": "application/json"},
        json={"actor": "unlocker.webunlocker", "input": {"url": url, "js_render": True}},
        timeout=120,
    )
    r.raise_for_status()
    return r.json()["data"]

soup = BeautifulSoup(fetch("https://quotes.toscrape.com/js/"), "html.parser")

SAVED_SELECTOR = ".author-name"          # đã hoạt động tháng trước; trang đã đổi tên lớp
authors = [e.get_text(strip=True) for e in soup.select(SAVED_SELECTOR)]
print(f"bộ chọn đã lưu {SAVED_SELECTOR!r} khớp với: {len(authors)}",
      "-> HỎNG, không trích xuất được gì" nếu không có tác giả khác không thì "-> ổn")

Chạy lệnh biến sự thất bại lặng im trở nên vang dội:

text Copy
bộ chọn đã lưu '.author-name' khớp với: 0 -> HỎNG, không trích xuất được gì

Giai đoạn 3: Sửa chữa với mô hình ngôn ngữ

Khi bộ chọn đã lưu không khớp với gì, hãy đưa DOM trực tiếp cho mô hình và yêu cầu một sự thay thế. Mô hình sẽ đọc cấu trúc thực tế — các thẻ, lớp và lồng ghép được xác định bởi tiêu chuẩn CSS Selectors — và trả về một bộ chọn được hình thành theo trang trước mặt nó, không phải theo một trang mà nó đã thấy trong quá trình huấn luyện. Hạn chế câu trả lời thành một đối tượng JSON giữ cho câu trả lời là một chuỗi bộ chọn duy nhất thay vì một lời giải thích:

python Copy
import os, json, requests

def propose_selector(html_fragment, target):
    prompt = (f'Một bộ chọn CSS để thu thập thông tin trên web đã hỏng. Từ đoạn HTML này, hãy trả về JSON '
              f'{{"selector": "<css>"}} cho phần tử chứa {target}. Chỉ trả về JSON.\n\n'
              f"HTML:\n{html_fragment}")
    r = requests.post(
        "https://openrouter.ai/api/v1/chat/completions",
        headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}", "Content-Type": "application/json"},
        json={"model": "deepseek/deepseek-v4-flash",
              "messages": [{"role": "user", "content": prompt}],
              "response_format": {"type": "json_object"}, "temperature": 0},
        timeout=120,
    )
    r.raise_for_status()
    return json.loads(r.json()["choices"][0]["message"]["content"])["selector"]

Gửi một khối .quote đã được render là đủ ngữ cảnh và giữ cho số token — và chi phí — nhỏ.

Lấy khóa API của bạn trên kế hoạch miễn phí: app.scrapeless.com

Giai đoạn 4: Xác thực và trích xuất

Một bộ chọn được đề xuất là một gợi ý cho đến khi DOM xác nhận. Chạy bộ chọn đã được sửa chữa với cùng một soup và đếm số lần khớp: một sửa chữa thực sự sẽ trả về các hàng, và một dự đoán sai sẽ trả về số không và bị loại bỏ trước khi nó có thể ghi dữ liệu trống. Vòng lặp hoàn chỉnh gắn kết ba cuộc gọi lại với nhau:

python Copy
import os, json, requests
from bs4 import BeautifulSoup

def fetch(url):
    r = requests.post(
        "https://api.scrapeless.com/api/v2/unlocker/request",
        headers={"x-api-token": os.environ["SCRAPELESS_API_KEY"], "Content-Type": "application/json"},
        json={"actor": "unlocker.webunlocker", "input": {"url": url, "js_render": True}},
        timeout=120,
    )
    r.raise_for_status()
    return r.json()["data"]

def propose_selector(html_fragment, target):
    prompt = (f'Một bộ chọn CSS web-scraping đã bị hỏng. Từ đoạn HTML này, trả về JSON '
              f'{{"selector": "<css>"}} cho phần tử giữ {target}. Chỉ trả về JSON.\n\n'
              f"HTML:\n{html_fragment}")
    r = requests.post(
        "https://openrouter.ai/api/v1/chat/completions",
        headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}", "Content-Type": "application/json"},
        json={"model": "deepseek/deepseek-v4-flash",
              "messages": [{"role": "user", "content": prompt}],
              "response_format": {"type": "json_object"}, "temperature": 0},
        timeout=120,
    )
    r.raise_for_status()
    return json.loads(r.json()["choices"][0]["message"]["content"])["selector"]

def extract(soup, selector):
    return [e.get_text(strip=True) for e in soup.select(selector)]

soup = BeautifulSoup(fetch("https://quotes.toscrape.com/js/"), "html.parser")
authors = extract(soup, ".author-name")

if not authors:
    print("bộ chọn đã lưu '.author-name' khớp: 0 -> sửa chữa")
    sample = str(soup.select_one(".quote") or soup.body)[:1500]
    healed = propose_selector(sample, "tên tác giả của trích dẫn")
    print("mô hình đề xuất bộ chọn:", repr(healed))
    authors = extract(soup, healed)
    print("bộ chọn đã sửa khớp:", len(authors))

print("tác giả:", ", ".join(authors[:3]), "...")

Bộ chọn bị hỏng hồi phục và phần trích xuất lại đầy đủ:

text Copy
bộ chọn đã lưu '.author-name' khớp: 0 -> sửa chữa
mô hình đề xuất bộ chọn: 'small.author'
bộ chọn đã sửa khớp: 10
tác giả: Albert Einstein, J.K. Rowling, Jane Austen ...

Bởi vì bộ chọn đã hồi phục được kiểm tra với công cụ chọn CSS mà bộ phân tích của bạn đã sử dụng, việc xác thực là chính xác: nó chỉ khớp với các phần tử hoặc không, và chỉ có một khớp mới được chấp nhận.

Giai đoạn 5: Lưu bộ chọn đang hoạt động

Chữa một lần là sửa chữa; chữa hết lần chạy là lãng phí. Sau khi một bộ chọn được xác thực, hãy ghi lại vào một bộ nhớ cache nhỏ được khóa bởi trường, để lần chạy tiếp theo có thể đọc bộ chọn đang hoạt động trực tiếp từ đĩa và chỉ gọi mô hình khi mà bộ chọn đó bị lỗi:

python Copy
import json
from pathlib import Path

CACHE = Path("selectors.json")

def remember(field, selector):
    cache = json.loads(CACHE.read_text()) if CACHE.exists() else {}
    cache[field] = selector
    CACHE.write_text(json.dumps(cache, indent=2))

remember("quote_author", "small.author")   # lần chạy tiếp theo bắt đầu từ bộ chọn đã sửa

Bộ trích xuất giờ đây hoạt động một cách dẻo dai: nó dựa vào các bộ chọn được lưu khi chúng hoạt động và sửa chữa ngay khi một trang thay đổi, thay vì thất bại cho đến khi ai đó nhận thấy và chỉnh sửa mã.

Kết luận

Một trình thu thập tự phục hồi biến một bộ chọn hỏng do sự cố thành một sự kiện thời gian chạy. API Thu thập Dữ liệu Đối Tượng Toàn Cầu không có Scrapeless cung cấp HTML đã được kết xuất mà mô hình cần để suy luận, mô hình đọc DOM trực tiếp đó và đề xuất một bộ chọn, và số lượng khớp với máy phân tích quyết định xem sửa chữa có thật hay không trước khi bất kỳ dữ liệu nào được ghi. Chạy trên — .author-name không khớp, small.author khớp mười — là vòng lặp thu nhỏ: lấy, phát hiện, phục hồi, xác thực, lưu trữ. Hãy điều chỉnh khối lượng yêu cầu bạn mong đợi so với trang giá trước khi bạn mở rộng, và đọc hướng dẫn về cách một trình duyệt phân tích HTML thành DOM nếu bạn muốn hiểu lý do tại sao việc lấy dữ liệu đã kết xuất lại quan trọng. Để có cái nhìn rộng hơn về các mô hình ngôn ngữ trong việc trích xuất, bài giải thích về LLM scraper là gì đặt ngữ cảnh.

Tham gia cộng đồng của chúng tôi để nhận một kế hoạch miễn phí và so sánh ghi chép với các nhà phát triển khác đang xây dựng các trình thu thập dữ liệu bền bỉ: Discord · Telegram.

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

Q: Thực sự điều gì bị hỏng khi một trình thu thập "hỏng"?

Thường là bộ chọn, không phải là logic. Một trang web gửi một thiết kế lại đổi tên một lớp hoặc cấu trúc lại đánh dấu của nó, bộ chọn CSS đã lưu không còn khớp nữa, và việc trích xuất trả về một danh sách rỗng. Yêu cầu vẫn thành công, đó là lý do tại sao sự thất bại dễ bị bỏ lỡ.

Q: Tại sao lại lấy dữ liệu thông qua API Thu thập Dữ liệu Đối Tượng Toàn Cầu thay vì một yêu cầu thông thường?

Bởi vì mô hình chỉ có thể phục hồi dựa trên HTML mà nó được hiển thị. Một trang đã được trình diễn trên máy khách sẽ trả về một container rỗng cho một máy khách HTTP trần, vì vậy mô hình sẽ suy luận về đánh dấu không có dữ liệu trong đó. API Thu thập Dữ liệu Đối Tượng Toàn Cầu kết xuất trang ở phía máy chủ và trả về DOM hoàn chỉnh, cung cấp cho mô hình cấu trúc thực tế để làm việc.

Q: Làm thế nào để bạn ngăn mô hình tạo ra một bộ chọn không khớp với bất kỳ điều gì?

Bạn xác thực trước khi tin tưởng. Bộ chọn đã phục hồi được thực hiện trên DOM đã phân tích và số lượng khớp được đếm; một bộ chọn không trả về phần tử nào sẽ bị loại bỏ chứ không phải được sử dụng. Mô hình đề xuất, nhưng số lượng khớp quyết định.

Q: Liệu điều này có gọi mô hình trong mỗi lần chạy không?

Không. Khi một bộ chọn được xác thực, nó được lưu vào đĩa với khóa là trường, và các lần chạy sau sẽ đọc bộ chọn đang hoạt động trực tiếp. Mô hình chỉ được gọi khi một bộ chọn đã lưu ngừng khớp, điều này giúp giữ độ trễ và chi phí liên quan đến sự hỏng hóc thực tế thay vì cho mọi lần thu thập.

Q: Liệu việc thu thập theo cách này có hợp pháp không?

Vòng lặp phục hồi không thay đổi những gì bạn có quyền thu thập. Thu thập dữ liệu từ các trang công khai, tôn trọng các điều khoản của trang web và các chỉ thị robots của nó, tránh dữ liệu cá nhân mà bạn không có cơ sở để xử lý, và giữ tỷ lệ yêu cầu ở mức hợp lý.

Tại Scrapless, chúng tôi chỉ truy cập dữ liệu có sẵn công khai trong khi tuân thủ nghiêm ngặt các luật, quy định và chính sách bảo mật trang web hiện hành. Nội dung trong blog này chỉ nhằm mục đích trình diễn và không liên quan đến bất kỳ hoạt động bất hợp pháp hoặc vi phạm nào. Chúng tôi không đảm bảo và từ chối mọi trách nhiệm đối với việc sử dụng thông tin từ blog này hoặc các liên kết của bên thứ ba. Trước khi tham gia vào bất kỳ hoạt động cạo nào, hãy tham khảo ý kiến ​​cố vấn pháp lý của bạn và xem xét các điều khoản dịch vụ của trang web mục tiêu hoặc có được các quyền cần thiết.

Bài viết phổ biến nhất

Danh mục