LangChain 教學:文件載入器(Loader)— Unstructured 與 PlaywrightLoader
簡介
在使用 LangChain 建構聊天機器人或資訊檢索系統時,最關鍵的第一步往往是把原始資料轉換成 Document 物件,讓後續的向量化、檢索與生成流程得以順利進行。
現實世界的資料來源多樣:PDF 報告、網頁、電子郵件、甚至是動態渲染的單頁應用(SPA)。如果只能手動複製貼上,既耗時又容易遺漏關鍵資訊。
本篇將聚焦於兩個強大且彈性的 Loader:
- Unstructured — 透過
unstructured套件自動解析各種非結構化檔案(PDF、DOCX、HTML、圖片等)。 - PlaywrightLoader — 利用 Microsoft Playwright 瀏覽器自動化技術,抓取需要 JavaScript 渲染的網頁內容。
掌握這兩個工具,你就能快速把 多樣化的原始文件 轉成 LangChain 可直接使用的 Document,為後續的檢索與生成奠定堅實基礎。
核心概念
1. 為什麼需要 Loader?
- 抽象化:LangChain 把所有資料抽象為
Document(包含page_content與metadata),統一介面讓後續的向量化、分割、檢索都能無縫接軌。 - 自動化:手動整理文件既繁瑣又容易出錯。Loader 能自動辨識檔案格式、解析文字、保留重要的 meta 資訊(檔名、來源 URL、頁碼等)。
- 可擴充:LangChain 本身提供多種內建 Loader,開發者也可以自行繼承
BaseLoader來實作自訂的載入邏輯。
2. Unstructured Loader
unstructured 是一個專門處理 非結構化檔案 的 Python 套件,支援 PDF、Word、PowerPoint、HTML、圖片(OCR)等。LangChain 內建 UnstructuredFileLoader,只要傳入檔案路徑或檔案類型,即可得到 Document。
2.1 基本使用方式
from langchain.document_loaders import UnstructuredFileLoader
# 讀取本機 PDF
loader = UnstructuredFileLoader("data/report.pdf")
documents = loader.load()
print(f"共抓到 {len(documents)} 個 Document")
print(documents[0].page_content[:200]) # 顯示前 200 個字
load()會回傳 list[Document]。- 預設會把整個檔案視為單一 Document,若想依頁分割,可搭配
RecursiveCharacterTextSplitter。
2.2 多檔案批次載入
from pathlib import Path
from langchain.document_loaders import UnstructuredFileLoader
folder = Path("data/multi/")
all_docs = []
for file_path in folder.glob("*.*"): # 支援任意副檔名
loader = UnstructuredFileLoader(str(file_path))
docs = loader.load()
# 為每份文件加入檔名作為 metadata
for d in docs:
d.metadata["source"] = file_path.name
all_docs.extend(docs)
print(f"總共載入 {len(all_docs)} 份文件")
2.3 使用 OCR 解析圖片或掃描 PDF
from langchain.document_loaders import UnstructuredFileLoader
# 需要安裝 tesseract & pytesseract
loader = UnstructuredFileLoader(
"data/scanned_invoice.png",
strategy="ocr_only", # 只走 OCR
ocr_languages=["eng", "chi_tra"] # 支援英文與繁體中文
)
doc = loader.load()[0]
print(doc.page_content[:300])
小技巧:若 OCR 效能不佳,可先調整
ocr_kwargs(如 DPI、預處理參數),或使用pdfminer先嘗試文字抽取,失敗再回退 OCR。
3. PlaywrightLoader
許多現代網站採用 SPA(Single Page Application) 或大量 JavaScript 動態渲染,傳統的 requests 抓取方式只能拿到原始 HTML,無法取得最終呈現的內容。Playwright 是微軟開源的跨瀏覽器自動化框架,支援 Chrome、Firefox、WebKit,且可在無頭(headless)模式下執行。
LangChain 的 PlaywrightLoader 包裝了 Playwright 的基本操作,讓開發者只需要提供目標 URL,即可得到渲染後的文字。
3.1 安裝與基本設定
pip install playwright langchain[all] # 安裝 Playwright 與 LangChain
playwright install # 下載瀏覽器二進位
3.2 讀取單一網頁
from langchain.document_loaders import PlaywrightLoader
url = "https://www.ptt.cc/bbs/Tech_Job/M.1698421234.A.1B8.html"
loader = PlaywrightLoader(url)
# 預設會等到 networkidle,確保大部分資源載入完成
doc = loader.load()[0]
print(f"來源: {doc.metadata['source']}")
print(doc.page_content[:400])
metadata["source"]會自動記錄 URL。- 可透過
wait_until="load"、wait_for_selector等參數自訂等待條件。
3.3 抓取需要登入的網站
from langchain.document_loaders import PlaywrightLoader
from playwright.sync_api import sync_playwright
def login_and_load(url, username, password):
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://example.com/login")
page.fill('input[name="user"]', username)
page.fill('input[name="pass"]', password)
page.click('button[type="submit"]')
page.wait_for_load_state("networkidle")
# 登入成功後再載入目標頁面
page.goto(url, wait_until="networkidle")
content = page.content()
browser.close()
return content
# 使用 PlaywrightLoader 直接傳入 HTML 文字
html = login_and_load(
"https://example.com/secure/report",
"my_user",
"my_pass"
)
loader = PlaywrightLoader.from_html(html, source_url="https://example.com/secure/report")
doc = loader.load()[0]
print(doc.page_content[:200])
重點:對於必須先登入、點擊或滾動才能看到內容的網站,自行控制 Playwright 流程 再交給
PlaywrightLoader.from_html能取得最完整的文字。
3.4 大量抓取(爬蟲)
from langchain.document_loaders import PlaywrightLoader
from tqdm import tqdm # 進度條
urls = [
"https://news.ycombinator.com/item?id=35000000",
"https://news.ycombinator.com/item?id=35000123",
# ... 更多 URL
]
all_docs = []
for link in tqdm(urls, desc="抓取 Hacker News"):
loader = PlaywrightLoader(link, wait_until="networkidle")
docs = loader.load()
for d in docs:
d.metadata["source"] = link
all_docs.extend(docs)
print(f"共抓到 {len(all_docs)} 篇文章")
tqdm只是一個小幫手,正式爬蟲時請務必遵守 robots.txt 與網站的使用條款。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解法 / 最佳實踐 |
|---|---|---|
| 檔案過大 | 單次 load() 會一次把整個檔案讀入記憶體,易造成 OOM。 |
使用 UnstructuredFileLoader 搭配 chunk_size 或先手動分割檔案(如 PDF 每 10 頁切一次)。 |
| OCR 效能低 | 影像解析非常耗時,尤其大量掃描 PDF。 | 先使用 strategy="auto",讓套件先嘗試文字抽取,失敗才走 OCR;或使用多執行緒/分散式處理。 |
| 動態網頁未完整渲染 | Playwright 只等 load,但某些 AJAX 仍在背景執行。 |
改用 wait_until="networkidle",或自行 page.wait_for_selector() 等特定元素出現。 |
| 被網站封鎖 | 大量請求會觸發 Cloudflare、Rate‑limit 等防爬機制。 | 加入 隨機延遲、User‑Agent 變換,或使用 代理服務。 |
| metadata 缺失 | 從多檔案載入時,若未自行加入檔名或 URL,後續追蹤來源困難。 | 統一在 load 後補上 metadata["source"],例如檔名、頁碼、URL。 |
| 編碼問題 | 某些 PDF/HTML 內部編碼非 UTF‑8,導致亂碼。 | 在 UnstructuredFileLoader 中使用 encoding="utf-8" 或適當的 encoding 參數;若仍有問題,可先用 chardet 檢測。 |
小技巧
結合 TextSplitter:
from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) chunks = splitter.split_documents(documents)這樣可以在向量化前把長文件切成適當大小,提升檢索品質。
Cache Loader 結果:
若同一份文件會被多次載入,建議將Document物件序列化(pickle、json)後快取,減少重複解析時間。使用環境變數管理 Playwright 設定:
import os os.environ["PLAYWRIGHT_HEADLESS"] = "1" # CI/CD 時必須 headless
實際應用場景
| 場景 | 使用 Loader | 為何適合 |
|---|---|---|
| 企業內部文件搜尋 | UnstructuredFileLoader(PDF、Word、PPT) |
能一次處理多種辦公檔案,且支援 OCR,適合掃描合約、報告等。 |
| 即時新聞摘要 | PlaywrightLoader 抓取新聞網站 |
新聞網站多採用動態渲染,Playwright 能取得完整文章內容,配合 LLM 產生摘要。 |
| 法規條文檢索 | UnstructuredFileLoader + OCR |
法規 PDF 常為掃描檔,OCR 讓文字可被向量化。 |
| 產品說明書自動問答 | 結合兩者:先用 Playwright 抓取線上說明書(SPA),再用 Unstructured 處理 PDF 手冊 | 能同時涵蓋線上與離線資源,提供完整的知識庫。 |
| 教育平台課程筆記 | PlaywrightLoader 抓取課程網頁 + UnstructuredFileLoader 處理 PDF 講義 |
統一輸入格式,讓學生可透過聊天機器人快速查找課程重點。 |
總結
- Loader 是 LangChain 資料流的入口,負責把各式非結構化檔案或動態網頁轉為統一的
Document。 - Unstructured 提供了跨檔案類型的文字抽取能力,尤其在處理 PDF、Word、圖片(OCR)時表現穩定。
- PlaywrightLoader 則是面對現代 JavaScript‑heavy 網站的最佳選擇,能自動等待渲染完成、支援登入與互動。
- 透過 適當的切分、快取與 meta 資訊補齊,可以大幅提升後續向量化與檢索的效率與準確度。
掌握這兩個 Loader 後,你就能輕鬆構建 從文件到對話 的完整管線,讓 LLM 真正成為企業或個人知識的「智慧門戶」。祝開發順利,期待看到你把這些技巧應用在各種有趣的實務案例中!