🎯 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ác tác nhân LangChain có thể truy cập web trực tiếp: Xây dựng quy trình dữ liệu AI với Scrapeless

Isabella Garcia
Isabella Garcia

Web Data Collection Specialist

05-May-2026

Những điểm chính:

  • Tích hợp LangChain bên thứ nhất. Gói langchain-scrapeless trên PyPI cung cấp năm công cụ sẵn sàng sử dụng — ScrapelessDeepSerpGoogleSearchTool, ScrapelessDeepSerpGoogleTrendsTool, ScrapelessUniversalScrapingTool, ScrapelessCrawlerCrawlTool, và ScrapelessCrawlerScrapeTool — có thể gắn thẳng vào một tác nhân LangChain hoặc LangGraph. Không cần ống dẫn phụ, không cần dây CDP tùy chỉnh.
  • Quy trình gồm bốn bước: Khám phá → Hiển thị → Trích xuất → Lưu trữ. Tìm kiếm trên Google bằng công cụ deep-SERP, hiển thị bất kỳ URL nào bằng công cụ scraping toàn cầu (Scraping Browser ở chế độ nền), trích xuất các bản ghi có kiểu với PydanticOutputParser, và tùy chọn nhúng vào một kho vector cho RAG hạ nguồn.
  • Tác nhân quyết định khi nào cần quét. Hàm create_agent của LangChain (thời gian chạy LangGraph ở chế độ nền) cho phép LLM chọn giữa việc trả lời từ ngữ cảnh trước đó và gọi công cụ Scrapeless — trình duyệt đám mây chỉ khởi động khi dữ liệu mới thực sự cần thiết, điều này giữ chi phí và độ trễ ở mức hợp lý trong các phiên làm việc thường xuyên.
  • Bản ghi kiểu, không phải HTML thô. Đối chiếu ScrapelessUniversalScrapingTool (phản hồi markdown) với một lược đồ Pydantic biến các trang đã quét thành các bản ghi Product / Article / JobListing được xác thực mà mã hạ nguồn có thể dựa vào mà không cần thêm thao tác phân tích.
  • Trình duyệt đám mây chống phát hiện, proxy dân cư tại hơn 195 quốc gia. Scrapeless Scraping Browser xử lý việc hiển thị JavaScript, thoát proxy dân cư, và ngẫu nhiên hóa dấu vân tay (UA, múi giờ, WebGL, canvas) trên mỗi phiên, vì vậy tác nhân chỉ tập trung vào lý luận thay vì các ống dẫn lẩn tránh.

Giới thiệu: Các quy trình dữ liệu AI nhìn thấy web trực tiếp

Một LLM đơn giản trả lời từ dữ liệu đào tạo. Đối với hầu hết các quy trình làm việc agentic thực sự được triển khai — thông tin giá cả cạnh tranh, nghiên cứu khách hàng tiềm năng, theo dõi thị trường, tiếp nhận tin tức có cấu trúc, RAG từ web trực tiếp — mô hình cần phải thấy trang ngay bây giờ. Các thời điểm ngừng đào tạo, tường phí, SPA tải lười biếng, và hiển thị cá nhân hóa tất cả đẩy câu trả lời vào lãnh thổ mà LLM chưa bao giờ thấy, và phản hồi hoặc trích dẫn một con số lỗi thời hoặc lịch sự từ chối.

LangChain cộng với một trình duyệt đám mây là câu trả lời tiêu chuẩn. Mô hình lý luận; trình duyệt lấy dữ liệu; tác nhân kết nối hai thứ lại với nhau. Điểm ma sát mà hầu hết các đội nhóm gặp phải nằm dưới tác nhân: proxy dân cư, hiển thị JavaScript, phân tích dấu vân tay chống phát hiện, và vòng đời phiên đều cần được giải quyết trước khi tác nhân có thể làm gì hữu ích. Playwright trực tiếp qua một VPN dân cư hoạt động cho một lần chạy trên laptop duy nhất; nó không sống sót qua lịch trình sản xuất.

Scrapeless Scraping Browser giải quyết bốn lo ngại đó ở cấp nền tảng, và gói langchain-scrapeless trên PyPI cho phép sử dụng chúng như các công cụ LangChain gốc. Bài viết này hướng dẫn cách kết hợp các công cụ đó thành một quy trình dữ liệu AI bốn bước Khám phá → Hiển thị → Trích xuất → Lưu trữ, với một ví dụ nghiên cứu cạnh tranh, phản hồi kiểu Pydantic, và kết nối đồng thời có giới hạn.


Những gì bạn có thể xây dựng

Năm công cụ được gói langchain-scrapeless cung cấp bao gồm các mẫu quy trình dữ liệu AI phổ biến nhất:

  • Thông tin giá cả cạnh tranh. Tìm kiếm một danh mục, hiển thị các trang nhà bán lẻ hàng đầu, trích xuất một bản ghi Product có kiểu với giá, xếp hạng, và số lượng đánh giá.
  • Theo dõi SERP. Theo dõi vị trí từ khóa và sự thay đổi snippet qua các khu vực với ScrapelessDeepSerpGoogleSearchTool được tham số hóa bởi glhl.
  • Theo dõi xu hướng thị trường. Lấy dữ liệu interest_over_time và các truy vấn liên quan với ScrapelessDeepSerpGoogleTrendsTool để định kích thước danh mục (quyền truy cập điểm cuối Trends phụ thuộc vào cấp độ gói Scrapeless của bạn).
  • Trích xuất chi tiết sản phẩm quy mô lớn. Cung cấp một danh sách URL vào ScrapelessCrawlerScrapeTool và nhận lại định dạng markdown sẵn sàng cho một công cụ trích xuất LLM.
  • Tạo khách hàng tiềm năng từ các thư mục. Quét các trang danh sách doanh nghiệp bằng ScrapelessCrawlerCrawlTool, phân tích các hàng liên hệ thành các bản ghi kiểu, loại bỏ trùng theo miền.
  • Tiếp nhận tin tức có cấu trúc cho RAG. Hiển thị các trang của nhà xuất bản thành markdown sạch, trích xuất các bản ghi Article, nhúng vào kho vector LangChain, và truy vấn với các chuỗi tăng cường truy hồi.

Tất cả sáu quy trình đều kết hợp các nguyên tố giống nhau — tìm kiếm, hiển thị, trích xuất, lưu trữ — và ví dụ thực tế bên dưới bao phủ toàn bộ chuỗi từ đầu đến cuối.


Tại sao chọn Scrapeless Scraping Browser

Scrapeless Scraping Browser là một trình duyệt đám mây tùy chỉnh, chống phát hiện, được thiết kế cho các công cụ quét web và các tác nhân AI. Đối với các tác nhân LangChain cụ thể, nó mang lại:

  • Proxy dân cư tại hơn 195 quốc gia — các truy vấn giới hạn địa lý trả về các danh sách mà người dùng địa phương sẽ thấy, và việc quay vòng là tự động trên mỗi phiên.
  • Kết xuất JavaScript bên đám mây — đầy đủ Chromium với trang được khôi phục trước khi trích xuất, do đó các SPAs, luồng cuộn vô tận và các bảng tải chậm là những đối tượng hàng đầu.
  • Chủng dấu vân tay chống phát hiện trên mỗi phiên — UA, múi giờ, ngôn ngữ, độ phân giải màn hình, WebGL và canvas được ngẫu nhiên hóa theo từng phiên, với một API dấu vân tay tùy chỉnh cho các danh tính cố định khi nhất quán là quan trọng.
  • Bảo trì phiên tại lớp trình duyệt đám mây thông qua sessionTTL (60–900s) và sessionName — có sẵn khi điều khiển điểm cuối WSS trực tiếp; các lệnh gọi langchain-scrapeless phân bổ một phiên mới cho mỗi invoke, đây là mặc định đúng cho một quy trình nghiên cứu.
  • Tích hợp LangChain từ bên đầu tiênpip install langchain-scrapeless hiển thị trình duyệt đám mây như các công cụ LangChain gốc; không có bọc subprocess, không có hệ thống CDP, không có bộ tuần tự hóa tùy chỉnh.

Nhận mã API của bạn trên gói miễn phí tại Scrapeless. Tích hợp đầy đủ được tài liệu hóa tại github.com/scrapeless-ai/langchain-scrapeless.

Nhận gói miễn phí của bạn và bắt đầu cạo:

Tham gia cộng đồng sôi động của Scrapeless để nhận gói miễn phí trị giá $5-10 và kết nối với những đổi mới khác:

Cộng đồng Discord Chính thức của Scrapeless
Cộng đồng Telegram Chính thức của Scrapeless


Điều kiện tiên quyết

  • Python 3.10 trở lên.
  • Tài khoản Scrapeless và mã API — đăng ký tại Scrapeless và sao chép mã từ Cài đặt → Quản lý mã API.
  • Mã API cho mô hình trò chuyện — các ví dụ dưới đây sử dụng OpenAI (OPENAI_API_KEY); mã đại lý giống nhau hoạt động với langchain-anthropic, langchain-google-genai, langchain-ollama, hoặc bất kỳ mô hình trò chuyện LangChain nào bằng cách thay thế dòng ChatOpenAI.
  • Quen thuộc cơ bản với pipvenv.

Cài đặt

Cài đặt đầy đủ gồm bốn bước con. Mỗi bước có thể được xác minh độc lập, bạn có thể tạm dừng và xác nhận trước khi tiếp tục.

1. Tạo một venv và cài đặt các gói

bash Copy
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install langchain langchain-scrapeless langchain-openai langgraph pydantic

langchain-scrapeless kéo theo langchain-core và SDK Python Scrapeless như là các phụ thuộc cấp độ. Gói meta langchain cung cấp langchain.agents.create_agent, runtime đại lý ReAct hiện đại (không lỗi thời); langgraph cung cấp runtime CompiledStateGraph nền tảng; langchain-openai là nhà cung cấp mô hình trò chuyện được sử dụng trong các ví dụ. Đổi sang langchain-anthropic hoặc nhà cung cấp khác nếu bạn thích.

2. Cấu hình mã API của bạn

Xuất cả hai mã cho phiên shell hiện tại:

bash Copy
export SCRAPELESS_API_KEY="your_api_token_here"
export OPENAI_API_KEY="your_openai_token_here"

Để cài đặt vĩnh viễn, thêm các dòng tương tự vào ~/.bashrc / ~/.zshrc, hoặc sử dụng một bộ tải .env (python-dotenv) và tải tệp trong khi quá trình khởi động. Các công cụ Scrapeless tự động đọc SCRAPELESS_API_KEY từ môi trường — bạn không cần phải truyền nó như một tham số tạo.

3. Xác minh cài đặt

Một bài kiểm tra ngắn để kiểm tra công cụ tìm kiếm và in ra một kết quả ngắn. API Scrapeless thỉnh thoảng trả về một 400 tạm thời trong lần gọi đầu tiên sau khi khởi động lạnh; bài kiểm tra lặp lại lên tới ba lần để một lần thử lại có thể khôi phục những trường hợp đó:

python Copy
# verify.py
import time
from langchain_scrapeless import ScrapelessDeepSerpGoogleSearchTool

tool = ScrapelessDeepSerpGoogleSearchTool()
for attempt in range(3):
    try:
        result = tool.invoke({"q": "scrapeless scraping browser",
                              "hl": "en", "gl": "us"})
        print(str(result)[:300])
        break
    except ValueError as e:
        print(f"tạm thời (thử {attempt + 1}): {e}")
        time.sleep(3)

Chạy: python verify.py. Một lần chạy thành công in ra một chuỗi kết quả tìm kiếm trong vài giây. Nếu tất cả ba lần thử đều gây ra ValueError với failed with status 401, thì mã API bị thiếu hoặc sai; hãy kiểm tra lại echo $SCRAPELESS_API_KEY trong cùng một shell. Nếu gặp 400 bền vững sau ba lần thử, có thể tài khoản không có quyền truy cập vào điểm cuối yêu cầu — xem FAQ về lỗi tạm thời.

4. (Tùy chọn) Ghi chú các phiên bản phụ thuộc

Để các bản xây dựng có thể lặp lại, ghi lại các phiên bản bạn đã thử nghiệm. Sự kết hợp được giải quyết bởi pip install langchain langchain-scrapeless langchain-openai langgraph pydantic trên môi trường Python 3.12 sạch là:

Copy
langchain==1.2.17
langchain-core==1.3.2
langchain-openai==1.2.1
langchain-scrapeless==0.1.3
langgraph==1.1.10
pydantic==2.13.3
scrapeless==1.1.1

Lưu ý: Siêu dữ liệu của gói langchain-scrapeless 0.1.3 tuyên bố langchain-core <0.4.0, nhưng pip lại giải quyết thành langchain-core 1.3.2langchain-openai yêu cầu nó; việc nhập tại thời gian chạy vẫn hoạt động. Để hoàn toàn tránh cảnh báo của bộ giải quyết, hãy cài đặt langchain-scrapeless và chỉ nhà cung cấp mô hình trò chuyện bạn cần (ví dụ: bỏ langchain-openai và truyền một thể hiện ChatAnthropic thay thế). LangGraph 1.0 đã ra mắt vào tháng 10 năm 2025; chuỗi 1.1.x là phiên bản ổn định hiện tại.


Cách bạn thực sự sử dụng điều này: lên lịch giao tiếp với đại lý của bạn

Sau khi cài đặt, bạn xây dựng các quy trình bằng cách trò chuyện với đại lý — không phải bằng cách tái tạo CSS selectors mỗi lần trang web mục tiêu thay đổi DOM. Đại lý chịu trách nhiệm cho vòng lặp phát hiện → render → trích xuất và LLM chọn công cụ phù hợp cho mỗi lượt.

Các thông điệp bạn có thể dán

Bạn gõ Đại lý làm gì
"Tìm 5 máy pha espresso bỏ túi hàng đầu và trả về tên, giá, xếp hạng dưới dạng JSON." Tìm kiếm Google, render kết quả hàng đầu, trích xuất một Product[] đã đánh dấu.
"Cho tôi biết sự quan tâm hiện tại của Google Trends trong 12 tháng qua cho vector database tại Mỹ." Gọi ScrapelessDeepSerpGoogleTrendsTool với data_type="interest_over_time" (cần truy cập theo cấp kế hoạch vào điểm cuối Trends).
"Crawl https://example.com/docs đến độ sâu 2 và trả về markdown cho mỗi trang." Gọi ScrapelessCrawlerCrawlTool với limit=....
"Render https://www.amazon.com/dp/B08N5WRWNW dưới dạng markdown." Gọi ScrapelessUniversalScrapingTool với response_type="markdown".
"Tìm kiếm các công ty khởi nghiệp Series A trong fintech 2026, liệt kê các công ty và quy mô vòng tài trợ của họ." Tìm kiếm → render → trích xuất đã đánh dấu tự động được kết nối.
"Rút homepage và trang giá từ ba đối thủ SaaS này và tóm tắt sự khác biệt." Multi-URL ScrapelessCrawlerScrapeTool → tóm tắt LLM.
"Theo dõi trang nghề nghiệp Greenhouse này và cho tôi biết các vai trò nào phù hợp với kỹ sư nhân viên hoặc hạ tầng." Render → bộ lọc từ khóa → hàng JSON.
"Những kết quả hữu cơ hàng đầu cho langchain scrapeless tutorial từ một địa chỉ ở Vương quốc Anh là gì?" ScrapelessDeepSerpGoogleSearchTool với gl="uk", hl="en".

Ví dụ đã làm

Bạn gõ:

Tìm 3 máy pha espresso bỏ túi hàng đầu cho du lịch dưới 150 đô la. Đối với mỗi máy, trả về tên, giá, xếp hạng trung bình, số lượng đánh giá và ba tính năng chính. Đặt tìm kiếm vào một địa chỉ ở Mỹ.

Kế hoạch của đại lý (bằng tiếng Anh đơn giản):

  1. Gọi ScrapelessDeepSerpGoogleSearchTool với q="best portable espresso makers under 150", gl="us", hl="en", num=5 để lấy URL kết quả hữu cơ.
  2. Đối với 3 URL kết quả hàng đầu, gọi ScrapelessUniversalScrapingTool với response_type="markdown" để render mỗi trang thành markdown sạch.
  3. Truyền mỗi trang đã render cho LLM với một PydanticOutputParser liên kết với một lược đồ Product; từ chối bất kỳ trang nào mà bộ phân tích không trích xuất được têngiá.
  4. Tổng hợp ba bản ghi Product thành một mảng JSON và trả lại.

Những gì bạn nhận được:

json Copy
[
  {
    "name": "Wacaco Nanopresso",
    "price": 79.95,
    "rating": 4.7,
    "review_count": 12483,
    "key_features": [
      "Hoạt động bằng bơm tay thủ công",
      "Lên đến 18 bar áp suất trích xuất",
      "Tương thích với cà phê xay hoặc viên NS thông qua bộ chuyển đổi"
    ],
    "url": "https://example.com/p/wacaco-nanopresso"
  },
  {
    "name": "Flair NEO Flex",
    "price": 119.00,
    "rating": 4.5,
    "review_count": 2104,
    "key_features": [
      "Vận hành bằng cần kéo, không cần điện",
      "Áp suất espresso cấp với bộ lọc dưới đáy",
      "Có thể tháo rời cho việc di chuyển"
    ],
    "url": "https://example.com/p/flair-neo-flex"
  },
  {
    "name": "Outin Nano",
    "price": 129.99,
    "rating": 4.6,
    "review_count": 5871,
    "key_features": [
      "Có bộ phận gia nhiệt tích hợp",
      "Chu trình tự làm sạch",
      "Sạc USB-C, ~3 phút để làm nóng"
    ],
    "url": "https://example.com/p/outin-nano"
  }
]
// Lược đồ phản ánh chính xác những gì bộ phân tích Bước 4 phát ra. Giá trị của các trường là minh họa.

Định hình các thông điệp

Cách diễn đạt Hiệu ứng
"Sử dụng địa chỉ ở Đức (gl=de)." Đặt công cụ tìm kiếm vào khu vực nhà cung cấp; kết quả trả về những gì người dùng Berlin sẽ thấy.
"Lấy thông tin dưới dạng markdown." response_type="markdown" trên công cụ thu thập thông tin toàn cầu — chi phí LLM thấp hơn, ổn định hơn về lựa chọn so với HTML.
"Giới hạn quá trình thu thập thông tin còn 25 trang." limit=25 trên ScrapelessCrawlerCrawlTool.
"Bỏ qua các trang không có giá." Bộ phân tích trả về None cho các trường thiếu; đại lý lọc lựa.
"Chạy ba URL song song." Chuyển giao cho mẫu độ trễ giới hạn trong Bước 6 dưới đây.

Các bước 1–6 dưới đây là tài liệu tham khảo về chức năng bên trong. Đọc chúng một lần để xem cách phát hiện → render → trích xuất → lưu trữ kết hợp; sau đó tin tưởng vào đại lý để áp dụng nó cho bất kỳ truy vấn nào mà người điều hành đưa ra.


Kiến trúc

Copy
┌──────────────────────────────────────────────────────────────────────┐
│  LangChain create_agent  (LangGraph runtime)                         │
│                                                                      │
│  ┌───────────────────────┐     ┌────────────────────────────────┐    │
│  │  Mô hình trò chuyện    │ ──► │  Công cụ (langchain-scrapeless) │    │
│  │  (OpenAI / Anthropic  │     │   • Tìm kiếm Google DeepSerp    │    │
│  │   / Gemini / Ollama)  │ ◄── │   • Scraping Toàn cầu           │    │
│  └───────────────────────┘     │   • CrawlerCrawl               │    │
│            ▲                   │   • CrawlerScrape              │    │
│            │                   │   • Xu hướng Google DeepSerp    │    │
│            │                   └──────────────┬─────────────────┘    │
│            │                                  │                      │
│            │           Bộ phân tích đầu ra PydanticOutputParser     │                      │
│            │           (các bản ghi kiểu)        ▼                      │
│            │                ┌──────────────────────────────────┐     │
│            │                │  Trình duyệt đám mây không scrapeless│     │
│            │                │  • proxy dân cư (195+)          │     │
│            │                │  • dấu vân tay chống phát hiện  │     │
│            │                │  • kết xuất JS Chromium          │     │
│            │                │  • thời gian phiên TTL 60–900s   │     │
│            │                └──────────────────────────────────┘     │
│            │                                  │                      │
│            └──────────────────────────────────┘                      │
│            các bản ghi kiểu chảy trở lại tác nhân                      │
└──────────────────────────────────────────────────────────────────────┘
                              │
                              ▼ (tùy chọn)
                  ┌───────────────────────────┐
                  │  Lưu trữ Vector (Chroma / │
                  │  PGVector / pgvector)     │
                  └───────────────────────────┘

Ba lớp, tách biệt rõ ràng: LLM suy luận qua cuộc trò chuyện, các công cụ langchain-scrapeless bao bọc trình duyệt đám mây qua các giao diện LangChain gốc, và trình duyệt đám mây xử lý mọi vấn đề không phải suy luận. Mỗi lớp có thể được thay thế — mô hình trò chuyện, prompt, thậm chí công cụ cơ bản — mà không cần viết lại những phần còn lại.


Bước 1 — Định nghĩa sơ đồ đầu ra kiểu

Pydantic là phần tử chịu tải mà biến markdown đã scrape thành một thứ mà mã bên dưới có thể dựa vào. Định nghĩa bản ghi mục tiêu một lần và liên kết nó với bộ trích xuất LLM ở Bước 4.

python Copy
# schema.py
from typing import Optional
from pydantic import BaseModel, Field, HttpUrl

class Product(BaseModel):
    name: str = Field(...,
        description="Tên sản phẩm. Luôn yêu cầu — sử dụng tiêu đề trang hoặc H1 nếu không có tên sản phẩm rõ ràng.")
    price: Optional[float] = Field(None, description="Giá số trong USD; null nếu không có")
    rating: Optional[float] = Field(None, description="Đánh giá trung bình, 0–5; null nếu không có")
    review_count: Optional[int] = Field(None, description="Số lượng đánh giá; null nếu không có")
    key_features: list[str] = Field(default_factory=list, description="3–5 gạch đầu dòng đặc điểm ngắn")
    url: HttpUrl = Field(..., description="URL sản phẩm chuẩn")

Đánh dấu mọi trường có thể thiếu với Optional và mặc định danh sách là rỗng — các trang trung gian chống bot, sự khác biệt trong bố cục khu vực và các nút DOM lazy-hydrated có nghĩa là các trang thường xuyên bỏ qua một hoặc hai trường, và một sơ đồ không phải tùy chọn từ chối các dòng mà lẽ ra sẽ hữu ích. Giữ name là yêu cầu và đưa ra một gợi ý trong mô tả của nó (tiêu đề trang, H1) để bộ trích xuất không bao giờ trả về null cho trường yêu cầu; gợi ý đơn lẻ đó cho phép sơ đồ hấp thụ các trang ồn ào mà không làm dấy lên lỗi.


Bước 2 — Khám phá với ScrapelessDeepSerpGoogleSearchTool

Công cụ deep-SERP trả về kết quả Google tự nhiên cho một truy vấn, được tham số hóa theo ngôn ngữ (hl), quốc gia (gl), và số lượng kết quả (num). Đây là phần tử khám phá — tìm kiếm mở rộng vũ trụ URL trước khi bạn cam kết bất kỳ ngân sách kết xuất nào trên mỗi trang.

python Copy
# discover.py
from langchain_scrapeless import ScrapelessDeepSerpGoogleSearchTool

search = ScrapelessDeepSerpGoogleSearchTool()
results = search.invoke({
    "q": "những máy pha cà phê espresso di động tốt nhất 2026 dưới 150",
    "hl": "vi",
    "gl": "vn",
    "num": 5,
})
print(results)

hl kiểm soát ngôn ngữ kết quả và gl kiểm soát quốc gia xuất cảnh — chúng là các cần gạt vùng miền. Để theo dõi SERP trên các vùng, chạy cùng một truy vấn với các giá trị gl khác nhau (us, de, jp, br) và so sánh các danh sách kết quả. Các phản hồi ValueError tạm thời (HTTP 400/503 đã được công cụ đóng gói) hoặc TimeoutError là bình thường ở phía cao của khối lượng truy vấn; bọc cuộc gọi bằng trình trang trí thử lại từ Bước 6 trước khi mở rộng quy mô.


Bước 3 — Kết xuất với ScrapelessUniversalScrapingTool

Công cụ quét toàn diện là Scrapeless Scraping Browser. Nó chấp nhận một URL và trả về trang đã được xử lý dưới dạng markdown (hoặc HTML, hoặc ảnh chụp màn hình). Markdown là định dạng rẻ nhất để đưa vào một trình trích xuất LLM — nó loại bỏ quảng cáo, thanh điều hướng và kiểu nội tuyến, chỉ để lại nội dung mà trang thực sự nói đến.

python Copy
# render.py
from langchain_scrapeless import ScrapelessUniversalScrapingTool

scrape = ScrapelessUniversalScrapingTool()
markdown = scrape.invoke({
    "url": "https://example.com/p/wacaco-nanopresso",
    "response_type": "markdown",
})
print(markdown[:600])

Mỗi invoke cấp phát một phiên trình duyệt đám mây mới, điều này là mặc định đúng cho một quy trình nghiên cứu — các phiên mới cho mỗi URL đơn giản và kiên cố hơn với trạng thái chống bot mỗi phiên. (Việc tái sử dụng phiên gọi chéo thông qua sessionName là một tính năng cấp CDP; nếu quy trình của bạn cần cookie ấm và trạng thái đăng nhập trên các trang, hãy điều khiển trình duyệt đám mây trực tiếp qua điểm cuối WSS thay vì thông qua công cụ LangChain này.) Công cụ cũng chấp nhận response_type="html" khi bạn cần chạy các bộ chọn của riêng mình, response_type="plaintext" cho bối cảnh LLM rẻ nhất, hoặc response_type="png" / "jpeg" cho các quy trình kiểm tra hình ảnh.


Bước 4 — Trích xuất với PydanticOutputParser

Sơ đồ đã được gán ở Bước 1 kết nối trực tiếp vào một chuỗi Ngôn ngữ Biểu thức LangChain (LCEL) mà nhận markdown đã được xử lý và trả về một Product đã được đánh loại. Trình phân tích sẽ chèn sơ đồ JSON vào trong prompt và xác thực phản hồi của LLM dựa trên nó.

python Copy
# extract.py
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

from schema import Product

parser = PydanticOutputParser(pydantic_object=Product)

prompt = ChatPromptTemplate.from_messages([
    ("system",
     "Bạn trích xuất hồ sơ sản phẩm từ các trang web đã được xử lý.\n"
     "Đầu ra phải hoàn toàn khớp với sơ đồ này:\n{format_instructions}"),
    ("human",
     "URL nguồn: {url}\n\nMarkdown đã được xử lý:\n{markdown}\n\n"
     "Trả về một hồ sơ sản phẩm. Trường `name` là bắt buộc — sử dụng tiêu đề trang "
     "hoặc H1 nếu không có tên sản phẩm rõ ràng. Chỉ đặt các trường tùy chọn "
     "(giá, đánh giá, số lượng đánh giá) thành null khi không có."),
]).partial(format_instructions=parser.get_format_instructions())

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
extract_chain = prompt | llm | parser

product = extract_chain.invoke({"url": "https://example.com/p/wacaco-nanopresso",
                                "markdown": "<markdown đã được xử lý từ Bước 3>"})
print(product.model_dump())

Lỗi phân tích rất hiếm khi xảy ra khi ba điều kiện được thỏa mãn: (1) mọi trường không chắc chắn đều được đánh dấu là Optional[...], (2) prompt rõ ràng thông báo cho mô hình rằng chỉ các trường tùy chọn có thể là null, và (3) mọi trường bắt buộc đều có thông tin dự phòng trong mô tả của nó (ví dụ: name sử dụng tiêu đề trang hoặc H1). Khi có ba điều kiện đó, hợp đồng nullable của sơ đồ xử lý các trường tùy chọn thiếu, và hướng dẫn trong prompt giữ cho LLM không đặt các trường bắt buộc thành null trên các trang ồn ào — vì vậy chuỗi hoạt động trơn tru mà không có bất kỳ vòng lặp thử lại nào.


Bước 5 — Kết hợp vào một create_agent

Ajent kết nối ba công cụ lại với nhau và cho phép LLM quyết định cái nào sẽ gọi cho bất kỳ thông điệp người dùng nào. langchain.agents.create_agent là môi trường thực thi chính thức kể từ langchain 1.2 (nó thay thế langgraph.prebuilt.create_react_agent đã bị lỗi thời và sử dụng cùng một đồ thị trạng thái LangGraph ở phía sau).

python Copy
# agent.py
from langchain.agents import create_agent
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langchain_scrapeless import (
    ScrapelessDeepSerpGoogleSearchTool,
    ScrapelessUniversalScrapingTool,
    ScrapelessCrawlerCrawlTool,
)
from tenacity import (retry, stop_after_attempt,
                       wait_exponential, retry_if_exception_type)

# Các công cụ chính thuộc quyền sở hữu đầu tiên
_search   = ScrapelessDeepSerpGoogleSearchTool()
_scrape   = ScrapelessUniversalScrapingTool()
_crawl    = ScrapelessCrawlerCrawlTool()

# Bộ trang trí thử lại mà ajent sẽ thấy trên mọi cuộc gọi công cụ.
# Các công cụ Scrapeless đưa ra lỗi API tạm thời dưới dạng ValueError, vì vậy đó là bộ lọc.
_retry = retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=2, max=15),
    retry=retry_if_exception_type(ValueError),
)

def _check(payload: str) -> str:
    # Trình duyệt đám mây đôi khi trả về HTTP 200 với một JSON ERR_ nhúng.
    # Hiển thị nó như một ValueError để bộ trang trí @_retry ở trên có tác dụng.
    if isinstance(payload, str) and payload.startswith('{"statusCode"') and "ERR_" in payload:
        raise ValueError(f"Lỗi trình duyệt đám mây: {payload[:200]}")
    return payload

@tool
@_retry
def google_search(q: str, hl: str = "en", gl: str = "us", num: int = 5) -> str:
    """Tìm kiếm trên Google và trả về các kết quả hữu cơ hàng đầu dưới dạng JSON."""
    return _check(str(_search.invoke({"q": q, "hl": hl, "gl": gl, "num": num})))
python Copy
@tool
@_retry
def render_page(url: str) -> str:
    """Bố trí một URL với trình duyệt đám mây Scrapeless và trả về markdown sạch."""
    return _check(_scrape.invoke({"url": url, "response_type": "markdown"}))

@tool
@_retry
def crawl_site(url: str, limit: int = 10) -> str:
    """Thu thập thông tin từ một trang web với số lượng trang giới hạn, trả về markdown cho mỗi trang."""
    return _check(str(_crawl.invoke({"url": url, "limit": limit})))

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

system_prompt = (
    "Bạn là một đại lý nghiên cứu xây dựng bộ dữ liệu sản phẩm có kiểu. "
    "Khi đưa ra một truy vấn danh mục, bạn: "
    "(1) gọi google_search để lấy các URL hữu cơ hàng đầu, "
    "(2) gọi render_page với mỗi URL hứa hẹn, "
    "(3) trích xuất một bản ghi Sản phẩm cho mỗi trang theo sơ đồ, "
    "(4) trả về một mảng JSON các bản ghi. "
    "Ghim gl='us' và hl='en' trừ khi người dùng yêu cầu khác. "
    "Nếu một trang thiếu giá, hãy bỏ qua hàng đó trong mảng cuối cùng."
)

agent = create_agent(llm,
                     [google_search, render_page, crawl_site],
                     system_prompt=system_prompt)

for chunk in agent.stream(
    {"messages": [("human",
                   "Tìm 3 máy pha cà phê espresso di động hàng đầu dưới 150 đô la "
                   "và trả về tên, giá, đánh giá, số lượng nhận xét, tính năng chính, url.")]},
    stream_mode="values",
):
    chunk["messages"][-1].pretty_print()

Phần trang trí @tool + @retry là yếu tố chịu tải cho sản xuất. Các công cụ langchain-scrapeless bọc các lỗi ScrapelessError thành ValueError trước khi ném ra, vì vậy các phản hồi 400/503 tạm thời bên trong vòng lặp gọi công cụ của đại lý sẽ nổi lên là ValueError. Nếu không có retry, một lỗi tạm thời đơn lẻ sẽ làm sập toàn bộ quá trình chạy của đại lý; với ngăn xếp trang trí, mỗi cuộc gọi công cụ sẽ thử lại tối đa ba lần với phương pháp lùi thời gian theo cấp số nhân trước khi bỏ cuộc. (Phương thức Runnable.with_retry() trong langchain-core trả về một RunnableRetry, mà create_agent không chấp nhận — hàm được trang trí bằng @tool ở trên là con đường tạo ra một StructuredTool thực sự bên trong lớp vỏ retry.)

agent.stream(..., stream_mode="values") phát ra trạng thái thông điệp đầy đủ trong mỗi bước, vì vậy người vận hành có thể theo dõi các cuộc gọi công cụ của đại lý và lý lẽ trung gian theo thời gian thực. Để có một câu trả lời cuối cùng duy nhất, hãy chuyển sang agent.invoke(...). Để chuyển đường Product[] từ Bước 4 thông qua đại lý, hãy công khai chuỗi trích xuất như một hàm @tool khác và thêm nó vào danh sách — đại lý sẽ gọi nó sau mỗi lần bố trí. README của langchain-scrapeless vẫn chỉ ra from langgraph.prebuilt import create_react_agent; con đường đó hoạt động nhưng phát ra cảnh báo LangGraphDeprecatedSinceV10, vì vậy việc nhập hiện đại bên trên là con đường được đề xuất.


Bước 6 — Tăng cường sản xuất

Một kịch bản nghiên cứu hoạt động trên ba URL trong sổ tay không tồn tại ở ba mươi nghìn. Bốn mẫu tăng cường biến quy trình trên thành một cái gì đó mà một trình lập lịch có thể chạy không giám sát.

Tính đồng thời giới hạn

python Copy
# concurrent_render.py
import asyncio
from langchain_scrapeless import ScrapelessUniversalScrapingTool

scrape = ScrapelessUniversalScrapingTool()
SEM = asyncio.Semaphore(3)            # giới hạn 3 lần render đồng thời mỗi máy chủ

async def render(url: str) -> str:
    async with SEM:
        return await scrape.ainvoke({"url": url, "response_type": "markdown"})

async def render_all(urls: list[str]) -> list[str]:
    return await asyncio.gather(*(render(u) for u in urls))

Ba lần render đồng thời mỗi máy chủ là điểm ngọt — đủ cao để phân bổ chi phí khởi động mỗi phiên, đủ thấp để ở dưới hầu hết giới hạn tỷ lệ truy cập theo IP của các trang web. Giới hạn ở cấp độ máy chủ, không giới hạn toàn cầu; mười máy chủ khác nhau với ba công nhân mỗi máy là tốt, mười công nhân tất cả truy cập cùng một nhà bán lẻ thì không.

Thử lại khi có lỗi tạm thời

python Copy
# retry.py
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
from langchain_scrapeless import ScrapelessUniversalScrapingTool

scrape = ScrapelessUniversalScrapingTool()

def _raise_on_embedded_error(payload: str) -> str:
    # Scrapeless đôi khi trả về HTTP 200 với một nội dung JSON mô tả một
    # lỗi bên trong phía trình duyệt (đường hầm bị reset, ERR_CONNECTION_RESET, …).
    # Công cụ không ném lỗi trên điều đó; bề mặt nó như ValueError để thử lại được kích hoạt.
    if payload.startswith('{"statusCode"') and "ERR_" in payload:
        raise ValueError(f"Lỗi trình duyệt đám mây: {payload[:200]}")
    return payload

@retry(
    stop=stop_after_attempt(4),
    wait=wait_exponential(multiplier=1, min=2, max=20),
    retry=retry_if_exception_type((ValueError, TimeoutError)),
)
def render_with_retry(url: str) -> str:
    return _raise_on_embedded_error(
        scrape.invoke({"url": url, "response_type": "markdown"}))

Trình duyệt Scraping đôi khi gặp sự cố theo hai hình thức khác nhau: một lỗi HTTP 400/503 (bộ bao bọc langchain-scrapeless kích hoạt điều này như một ValueError) và một lỗi HTTP 200 với một đối tượng JSON mô tả lỗi bên phía trình duyệt như ERR_TUNNEL_CONNECTION_FAILED (bộ bao bọc không kích hoạt — lỗi JSON quay lại dưới dạng chuỗi). Bảo vệ _raise_on_embedded_error ở trên sẽ bắt lỗi hình thức thứ hai và chuyển đổi nó thành một ValueError để áp dụng cùng một chính sách thử lại. Với cả hai hình thức được xử lý, backoff mũ lũy thừa với bốn lần thử sẽ bắt được phần trăm cao ở giữa 90 mà không làm chôn vùi những trường hợp thất bại thực sự (các khối chống bot, 404) dưới các lần thử lại. Đối với các cuộc gọi do tác nhân điều khiển, hãy sử dụng ngăn xếp trang trí @tool + @retry từ Bước 5 — các hàm bao bọc ở đó nên áp dụng cùng một kiểm tra _raise_on_embedded_error trước khi trả về.

Quan sát với LangSmith

bash Copy
export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY="your_langsmith_key"
export LANGCHAIN_PROJECT="scrapeless-research-agent"

Với ba biến môi trường đó được thiết lập, mọi cuộc gọi công cụ, mọi cuộc gọi LLM và mọi lỗi phân tích đều hiển thị trong LangSmith với thời gian, chi phí và chính xác prompt mà mô hình đã thấy. Đối với một lần chạy sản xuất, đây là thay đổi có sức ảnh hưởng lớn nhất — nó biến "tác nhân làm điều gì đó kỳ quặc" thành một dấu vết có thể nhấp chuột.

Lưu vào kho vector

python Copy
# embed.py
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.documents import Document

docs = [Document(page_content=p.model_dump_json(), metadata={"url": str(p.url)})
        for p in products]
vs = Chroma.from_documents(docs, OpenAIEmbeddings(), persist_directory=".chroma")

Kho vector là giai đoạn thứ tư trong quy trình. Nó có lợi khi tác nhân là dịch vụ RAG hoạt động lâu dài trả lời các câu hỏi xuống dòng trên tập dữ liệu; nó là không cần thiết đối với một kịch bản nghiên cứu một lần. Chọn kho dựa trên sở thích hoạt động — langchain_chroma cho các tệp cục bộ, langchain_postgres cho Postgres được quản lý + pgvector, langchain_pinecone cho một CSDL vector được lưu trữ.


Những gì bạn nhận được

json Copy
[
  {
    "name": "Wacaco Nanopresso",
    "price": 79.95,
    "rating": 4.7,
    "review_count": 12483,
    "key_features": [
      "Hoạt động bằng tay, bơm thủ công",
      "Lên đến 18 bar áp suất chiết xuất",
      "Tương thích với cà phê xay hoặc viên nén NS thông qua bộ chuyển đổi"
    ],
    "url": "https://example.com/p/wacaco-nanopresso"
  },
  {
    "name": "Flair NEO Flex",
    "price": 119.00,
    "rating": 4.5,
    "review_count": 2104,
    "key_features": [
      "Điều khiển bằng cần, không cần điện",
      "Áp suất đạt tiêu chuẩn espresso với bộ lọc đáy không có",
      "Có thể tháo rời để du lịch"
    ],
    "url": "https://example.com/p/flair-neo-flex"
  },
  {
    "name": "Outin Nano",
    "price": 129.99,
    "rating": 4.6,
    "review_count": 5871,
    "key_features": [
      "Yếu tố sưởi ấm tích hợp",
      "Chu trình tự làm sạch",
      "Sạc USB-C, ~3 phút làm nóng"
    ],
    "url": "https://example.com/p/outin-nano"
  }
]
// Lược đồ phản ánh chính xác những gì trình phân tích Bước 4 phát ra. Các giá trị trường là mẫu minh họa.

Một vài quan sát chân thật về những gì có thể mong đợi khi điều này chạy trên web trực tiếp:

  • Thời gian hydrat hóa thay đổi theo từng trang. Công cụ scraping toàn cầu chờ domcontentloaded theo mặc định; đối với các SPA mà dưỡng nước giá thông qua một XHR thứ hai, markdown có thể đến trước khi giá hiển thị. Render lại một lần với một độ trễ ngắn hoặc quay lại response_type="html" và một bộ chọn tùy chỉnh nếu trường luôn null.
  • Các trường tùy chọn vẫn giữ nguyên là tùy chọn. Một số trang sản phẩm bỏ qua đánh giá rõ ràng hoặc số lượng đánh giá, đặc biệt trên các trang thương mại trực tiếp đến người tiêu dùng. Hãy coi các giá trị null như thông tin hơn là chế độ thất bại và lọc xuống dòng.
  • Việc trích xuất LLM chiếm ưu thế về độ trễ. Cuối cùng, quy trình mất khoảng 1–3 giây cho cuộc gọi tìm kiếm, 2–4 giây cho mỗi lần render trang và 3–5 giây cho mỗi lần trích xuất LLM. Sự đồng thời ở giai đoạn render và trích xuất là yếu tố lớn nhất.
  • Các interstitial chống bot xuất hiện dưới dạng ValueError. Khi một trang web tải trước một thử thách Cloudflare hoặc Akamai mà trình duyệt đám mây không thể hoàn thành một cách minh bạch, bộ bao bọc langchain-scrapeless sẽ kích hoạt ValueError thay vì trả về một trang thay thế một cách im lặng. Bộ trang trí thử lại sẽ bắt các trường hợp tạm thời; các trường hợp dai dẳng nhất thì tốt nhất là xử lý bằng cách mở rộng dấu vân tay hoặc ghim một vùng proxy khác.
  • Giai đoạn kho vector là tùy chọn. Đối với một quy trình nghiên cứu trả về các bản ghi có kiểu cho một người tiêu dùng xuống dòng, hãy bỏ qua hoàn toàn. Thêm nó khi cùng một tập dữ liệu sẽ trả lời nhiều câu hỏi xuống dòng theo thời gian.

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

Tôi có cần một proxy dân cư không?
Có, đối với bất kỳ trang nào có bảo vệ chống bot có ý nghĩa, mà hầu hết các nhà bán lẻ, thị trường và điểm cuối SERP. ScrapelessUniversalScrapingTool và các công cụ deep-SERP mặc định đi qua pool proxy dân cư của Scrapeless; tham số gl trên công cụ tìm kiếm chỉ định quốc gia xuất khẩu.

Còn những lỗi tạm thời như 400 hoặc 503 thì sao?
Các công cụ langchain-scrapeless hiển thị các lỗi API tạm thời dưới dạng ValueError (lỗi cơ sở ScrapelessError được bao bọc trước khi được ném lại). Đối với các cuộc gọi trực tiếp, sử dụng trang trí tenacity từ Bước 6 với retry_if_exception_type=(ValueError, TimeoutError). Đối với các cuộc gọi do tác nhân điều khiển bên trong create_agent, bao bọc mỗi công cụ với ngăn xếp trang trí @tool + @retry từ Bước 5 — điều này tạo ra một StructuredTool thực sự mà tác nhân chấp nhận và áp dụng chính sách thử lại cho mỗi cuộc gọi công cụ. Nếu không có một trong những điều này, một lỗi tạm thời 400 duy nhất sẽ làm sập toàn bộ cuộc chạy của tác nhân.

Một trang trả về Access Denied. Giờ thì sao?
Đầu tiên hãy thử lại bằng cách sử dụng trang trí Bước 6. Nếu trang liên tục chặn, hãy mở rộng phiên bằng cách thay đổi gl thành một quốc gia khác, hoặc thêm một chút await asyncio.sleep(...) giữa các lần thử để cho trạng thái phiên nguội đi. Đối với các trang có khối ở cấp độ IP nhất quán, hãy liên hệ với hỗ trợ của Scrapeless để xác nhận rằng khối nằm ở cấp độ nền tảng chứ không phải ở cấp độ tài khoản.

Các bộ chọn cứ bị hỏng. Làm sao tôi có thể sống sót qua việc quay vòng DOM?
Sử dụng response_type="markdown" trên ScrapelessUniversalScrapingTool thay vì phân tích HTML bằng các bộ chọn CSS. Markdown giảm thiểu các phần điều hướng và đa số các trôi layout, vì vậy bộ trích xuất LLM ở Bước 4 thấy một đại diện ổn định của nội dung ngay cả khi DOM cơ sở thay đổi.

Có bao nhiêu worker đồng thời cho mỗi máy chủ?
Ba là trần đã được tài liệu hóa cho các lần chạy ổn định. Giới hạn ở cấp độ máy chủ (asyncio.Semaphore(3) trong Bước 6); các worker trên các máy chủ khác nhau có thể chạy độc lập.

Tôi có thể sử dụng điều này mà không có LangGraph không?
Có. ScrapelessUniversalScrapingTool().invoke({...}) là một callable thông thường — gọi nó từ bất kỳ script Python nào, route FastAPI, hoặc task Celery. LangGraph thêm vòng lặp tác nhân lên trên, nhưng các công cụ thực tế không phụ thuộc vào khung nào cả.

Tôi có thể thay OpenAI bằng Claude, Gemini hoặc một mô hình cục bộ không?
Có. Thay thế ChatOpenAI(model="gpt-4o-mini") bằng ChatAnthropic(model="claude-sonnet-4-6"), ChatGoogleGenerativeAI(model="gemini-2.5-pro"), ChatOllama(model="llama3.1"), hoặc bất kỳ mô hình chat LangChain nào khác. Danh sách tools, đoạn prompt và bộ phân tích không thay đổi.

Làm thế nào tôi có thể thêm bộ nhớ đa lượt?
Chuyển một kiểm tra MemorySaver vào create_agent(llm, tools, checkpointer=MemorySaver()) và cung cấp một thread_id cho mỗi lần gọi. LangGraph lưu trữ trạng thái cuộc trò chuyện qua các lượt, vì vậy tác nhân có thể tham khảo lại các tìm kiếm trước đây mà không cần chạy lại chúng.

Tôi có thể xem các dấu vết yêu cầu ở đâu?
Đặt LANGCHAIN_TRACING_V2=true, LANGCHAIN_API_KEY, và LANGCHAIN_PROJECT (Bước 6). Mỗi cuộc gọi công cụ, cuộc gọi LLM và chạy bộ phân tích đều xuất hiện trong LangSmith với thời gian, chi phí, và đoạn prompt chính xác — sự thay đổi quan sát có lợi nhất cho một triển khai sản xuất.

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