Fetch 路由
Fetch 如何在 headless 與有頭瀏覽器之間選擇,以及唯一會嘗試兩次的情況。
決策流程
graph TB
A[Fetch] --> B{Option.Headless}
B -->|true| C[headless,不重試]
B -->|false| D{session domain 且有 GUI}
D -->|是| E[headed,不重試]
D -->|否| F[headless 首發]
F --> G{403 / 429 / 503}
G -->|否| H[回傳結果]
G -->|是| I{有 GUI}
I -->|否| H
I -->|是| J[headed 重試一次]
| 分支 | 條件 | 嘗試次數 |
|---|---|---|
| 強制 headless | Option.Headless 為 true |
1 次,headless |
| Session 網域 | 主機在 session 清單內且有 GUI | 1 次,有頭 |
| 預設 | 其他情況 | 先 headless,僅被擋時以有頭重試一次 |
什麼算被擋
只有三個狀態碼:403、429、503,來源可以是 *Error 也可以是 Result.Status。404、204、no article extracted 都不觸發重試——那些是頁面不存在或萃取失敗,換一種瀏覽器模式不會改變結果。非 *Error 的錯誤(啟動失敗、逾時)直接回傳,不重試。
just a moment 這類驗證頁標題會在此判斷前先轉成 403,Cloudflare 類型的中介頁因此能進入有頭重試。
Session 網域
headless 下鮮少能渲染出有效內容的社群網站,在有 GUI 時直接走有頭瀏覽器。清單內嵌於 core/embed/session_domains.json,主機等於清單項目或以 . 加項目結尾即命中:
facebook.com、fb.com、fb.watch、twitter.com、x.com、instagram.com、linkedin.com、threads.net、tiktok.com、pinterest.com、snapchat.com、weibo.com、weibo.cn、xiaohongshu.com、xhs.link、douyin.com、bilibili.com、zhihu.com、discord.com、telegram.org、t.me、vk.com、quora.com
沒有 GUI 時這些主機走預設分支。
GUI 判定
hasDisplay() 在 macOS 恆為真;Linux 上 DISPLAY 或 WAYLAND_DISPLAY 非空即為真。容器與無桌面的伺服器因此永遠不會嘗試啟動有頭瀏覽器。有頭視窗放在 -32000,-32000,位於螢幕外。