Katana 是 ProjectDiscovery 的端點探索爬蟲——一個以 Go 撰寫、MIT 授權的二進位工具,會接收目標並回傳 URLs 與端點,交給流程中的下一個工具接手。它可以在無瀏覽器的 HTTP 模式下運作,也可以搭配 -headless 驅動 Chromium。官方建議把 headless 模式視為覆蓋率較高的選擇;但這個測試案例也顯示,端點的「類型」和「數量」一樣重要。
我搭了一個小型網站,刻意放進三種不同類型的端點,並在 v1.6.1、-d 4 下測試各種模式分別能找到哪些內容。一般 HTML 在四種組合下都能完整找到 4/4 連結與完整的三層深度鏈。真正出現差異的是:一種端點存在於 JavaScript 原始碼中,另一種則是執行時才動態出現在 DOM 裡。
在這個測試案例中,headless 找到了瀏覽器模式漏掉的 runtime DOM 類端點;而標準模式搭配 -jc 則找到了兩個 headless 也漏掉的 JavaScript 檔字串常值。四個指令組合裡,沒有任何一列能同時覆蓋這兩類端點。除此之外,範圍控制、續跑,以及 known-files 行為,也各自形成了實務上的邊界。
Katana 到底是什麼
Katana 爬蟲——GitHub 上的 projectdiscovery/katana——以 Go 撰寫,並採用 MIT 授權。我在 2026 年 7 月 27 日測試的是 v1.6.1;版本很重要,因為下方關於覆蓋率與 known-files 的觀察,都是特定版本的行為結果。
這裡的分類名稱比平常更值得注意。端點探索爬蟲不是欄位擷取器。如果你想要的是能把產品名稱和價格整理成結構化 JSON 的 Go 網頁爬蟲,那 Katana 根本不屬於這個用途——它只會很樂意告訴你 /products/1138 存在,卻不會告訴你那個頁面上有什麼。這本來就是它的設計目標;若拿「資料擷取」的標準來評價它,就像拿金屬探測器去評估珠寶鑑價能力一樣不對題。
它最擅長的是滲透測試前期偵察與自動化流程:從 STDIN 輸入,輸出 URLs,接到下一個工具。也因此,先把最重要的提醒放前面——這裡所有測試都只跑在我自己寫的 127.0.0.1 本地測試環境上。請只對你擁有或被明確授權測試的主機使用 Katana,不要用在其他地方。這篇不是在談如何繞過防禦,而是在看某個指令到底能列舉出網站端點表面的多少範圍。
三種模式,各自能看到什麼
標準模式就是 Go 的 HTTP client。它會抓取頁面、解析 HTML、追蹤 href 連結,但完全不啟動瀏覽器。速度快、成本低,但凡是要等 JavaScript 跑完才會出現的內容,它都看不到。
-jc(-js-crawl)則是在這條無瀏覽器路徑上再加一層 JavaScript 解析。它會下載連結到的 .js 檔,從原始碼裡擷取像 URL 的字串常值。這不是執行,而是純閱讀。README 裡還有 -jsl(jsluice),描述為更重、較吃記憶體的解析器——我沒有測,所以無法評論它是否會改變覆蓋率結果。

-headless 會驅動 Chromium 並執行頁面腳本。在這個測試案例裡,它是唯一一個能找回由片段組成、再注入 runtime DOM 的路徑的 Katana 模式。這個結果不代表所有 parser 或未來的 Katana 模式都會有相同能力。
接著是範圍模型,這部分我會建議你在真正碰 production 之前先熟記。
| 旗標 | 控制項目 | 值 / 預設 |
|---|---|---|
-fs(field scope) | 哪些主機納入範圍 | dn、rdn、fqdn,或自訂 regex — 預設為 rdn |
-cs 與 -cos | 在這個 field scope 內進一步篩選的 URL regex | — |
-kf | known files:robots.txt 與 sitemap.xml | README 說至少需要 depth 3 |
-d | 深度 | 預設 3 |
-resume | 接續中斷的爬取 | — |
這個順序不是裝飾,而是決定某個主機 regex 是會擴大爬取範圍,還是讓結果悄悄變成空集合。
安裝方式:一個二進位,外加一個小註記
有三種安裝方式,其中只有一種需要工具鏈:
| 安裝方式 | 前置條件 |
|---|---|
從原始碼安裝:go install github.com/projectdiscovery/katana/cmd/katana@latest | 官方說明要求 Go 1.25 或更新版本 |
| 下載 release 頁面上的預編譯 binary | 不需要工具鏈 |
| Docker 映像 | 不需要工具鏈 |
我的是安裝在 ~/go/bin/katana,每次執行都回報 Current version: v1.6.1。到這裡為止,都是標準且讓人滿意的 Go 作品:單一檔案、執行時零負擔。
那個小註記出現在 headless:瀏覽器是與二進位檔分開的另一個前置條件。
-headless 的執行位置 | 需要什麼 |
|---|---|
| 我的機器 | katana 自動偵測到已安裝的 Chromium;我沒有記錄瀏覽器版本,也沒有手動指定 browser path |
| 一台乾淨的伺服器,依專案 Ubuntu 安裝說明 | 必須先 apt install google-chrome-stable,headless 才能運作 |
| Docker 路徑 | 會搭配 -system-chrome 以 headless 方式執行 |
到了乾淨伺服器上,這種便利性就沒了。只要命令列裡出現 -headless,你就要同時預留瀏覽器成本,而不只是 binary。
如果你會在 CI 裡跑,還有一件小事值得知道:katana 啟動時會向 GitHub 發版本檢查請求。-duc 可以關掉它。對筆電來說這只是雜訊;但對離線或有速率限制的 runner 來說,這是你沒要求的每次執行額外網路往返。我在計時測試時都加上 -duc,這樣數字測到的是爬取本身,而不是回呼外網的時間。
我的測試方式
我刻意設計了三類端點,讓不同模式的差異可以被看出來。所有內容都在一個 本地測試伺服器 上,而真實答案是在任何爬取之前就先寫好,因此回想率是對照固定集合,而不是 Katana 剛好印出了什麼。
- Class A — 一般 HTML。
/page/a、/page/b、/page/c,以及一條三層鏈/depth/1 → /depth/2 → /depth/3。任何爬蟲理應都能抓到。 - Class B — JavaScript 檔字串常值。
/api/js-endpoint-7和/api/js-endpoint-8只存在於連結到的/static/app.js原始碼字串裡。如果有工具願意讀 JS,不靠瀏覽器也看得到。 - Class C — 只有 runtime DOM 才會出現。 一條在執行時由片段組合而成(
'endpoint' + (6 * 7))並由腳本注入 DOM 的路徑。字串/runtime-only/endpoint42不會連續出現在伺服器送出的任何位元組裡——HTML 沒有,JS 原始碼也沒有。只有執行後才看得見。
另外還有 robots.txt、一個帶有兩個 <loc> 端點的 sitemap.xml、一條回傳 500 的路由、一個死連結,以及一個指向不同 hostname 上第二台伺服器的 out-of-scope 連結。
工具本身和測試案例一樣重要:伺服器會統計實際被抓取的內容,因此 scope 與 resume 的說法都以命中記錄為準,而不是只看 Katana 的 stdout。原始執行結果都放在 benchmark repo 裡,如果你想核對我的計算,可以直接看。
沒有人量化過的覆蓋率差異

在 -d 4 下,不同模式與端點類別的對照如下:
| 模式 | HTML 連結 (A) | 深度鏈 (A) | JS 檔字串常值 (B) | Runtime DOM (C) |
|---|---|---|---|---|
| standard | 4/4 | 3/3 | 0/2 | not found |
standard -jc | 4/4 | 3/3 | 2/2 | not found |
-headless | 4/4 | 3/3 | 0/2 | found |
-headless -jc | 4/4 | 3/3 | 0/2 | found |
把最後兩欄一起看,問題就很明顯了。Class B 只有一種組合找到:標準模式加 -jc。Class C 則有兩種:兩個 headless 組合都找到了。沒有任何一列同時命中兩欄。完整矩陣在 discovery-summary.json,其中計算欄位 headless_jc_covers_both 的值是 false。
實務上的結論就是:「直接用 headless,覆蓋率比較好」這句話,放到這個測試裡是不完整的。Headless 並沒有在標準結果之上再補上 Class B;它找回的是 Class C,卻漏掉 Class B。要在這個測試案例中完整涵蓋所有植入類別,需要兩次爬取再合併:
katana -u https://target.example -jc -d 4 -silent -o pass-jc.txt
katana -u https://target.example -headless -d 4 -silent -o pass-headless.txt
sort -u pass-jc.txt pass-headless.txt > endpoints.txt
-headless -jc 這個組合,是我最希望上游能正式說明的一個點。把 JavaScript parser 加到 headless 執行裡,完全沒有補到任何東西——Class B 依舊是 0/2,而且每次重現都一樣。我是在回報這個行為,不是在推測機制;我沒有去 instrument Katana 內部來查為什麼瀏覽器路徑不再提供 JS 檔字串常值。把它當成可重現觀察與一個值得提的 GitHub issue,而不是診斷結論。(另外值得一提的是:-hl -jc 在 macOS ARM 上的 v1.6.1 可以乾淨完成,回傳碼是 0;歷史上這點並不總是如此。)
官方文件 把 headless 描述成能提供更好覆蓋率,而在這裡它確實對 runtime rendered 類別有幫助。不過文件並沒有清楚說明這種 source-literal 與 runtime DOM 的差異,所以這個矩陣更適合拿來提醒你:要用自己的端點類型實測兩條路徑,而不是把它視為放諸四海皆準的分類法。
Headless 的時間成本
在一台閒置機器上,每種模式連跑三次的結果如下:
| 模式 | p50 | 最小–最大 | 平均 |
|---|---|---|---|
| standard | 13.08s | 13.07–13.17s | 13.11s |
-headless | 66.82s | 66.78–67.68s | 67.09s |
這是 5.1 倍 的差距,而且區間完全沒有重疊——我最慢的 standard 跑法(13.17s)仍然比我最快的 headless(66.78s)快了超過 53 秒(見 cost-summary.json)。這不是測量雜訊。
關於那 13 秒,有一個小註記:我的測試案例刻意包含一條 500 路由與一個死連結,而 standard 模式在兩者上都會等預設的 -timeout 10 重試尾端。我沒有刻意把 timeout 調小來讓標準模式看起來更快,這表示如果把 timeout 調校過,standard 與 headless 的差距很可能還會更大,而不是縮小。
這個倍率只是本機容量訊號,不是 production 預測。真實目標的延遲、失敗率、腳本運算與排程都不同,而這個測試案例本身就含有預設 timeout 的尾巴。你可以用這個測得的 5.1 倍差距,來判斷 headless 是否值得獨立預算與獨立目標子集;接著再在具代表性、且經授權的主機上做基準測試。
範圍有守住,但有個旗標其實默默沒作用
範圍測試使用兩台伺服器:主要伺服器在 127.0.0.1,另一台則是可透過不同 port 以 localhost 存取,且只在那台伺服器上才存在某個路徑。因此,只要那條路徑被命中,就代表 out-of-scope 主機真的被抓到了,而不是單純被印出來。
| 設定 | 是否抓到 out-of-scope 主機? | 第二台伺服器上的命中數 |
|---|---|---|
預設(-fs rdn) | 否 | 0 |
-fs fqdn | 否 | 0 |
-cs localhost | 否 | 0 |
| `-fs '(127.0.0.1 | localhost)'` | 是 |
這裡的好消息是 scope 控制很保守:預設狀況下 Katana 不會越界,而要擴大範圍必須明確操作。對於會被拿去掃別人基礎架構的工具來說,這才是對的預設。
有意思的是 -cs localhost 這一列。它沒有把爬取範圍擴到第二台主機,甚至還輸出了 0 個 URL。原因在於 -cs 是在 field scope 內做篩選,而 field scope 仍然只有主機一,所以 regex 沒有匹配到任何東西,結果是空集合而不是錯誤。若你曾經寫過一個 scope regex,名稱明明是你想納入的主機,卻只看到空輸出檔,這就是原因(見 scope-summary.json)。要新增主機,請設定 -fs。要在既有主機範圍內再縮小,則用 -cs / -cos。
Resume 比旗標文字看起來更粗粒度
README 將這個旗標描述成 -resume string resume scan using resume.cfg,看起來像是會在工作目錄裡放一個檔案。但事實不是。我的機器上,檢查點被寫到 ~/.config/katana/resume-<xid>.cfg——這是實測結果,不是從文件直接讀出來的,因為文件本身沒有寫路徑。
更重要的是裡面的內容。這個檔案只包含一個 InFlightUrls map,而且裡面只有一件事:seed URL。沒有 visited set,也沒有 frontier。所以當我在三秒後用 SIGINT 中斷爬取、再接著 resume 時,發生的是:
| 執行 | 不同路徑數 |
|---|---|
| 完整 baseline crawl | 11 |
| 中斷前已抓到 | 10 |
| resume 重新抓取 | 全部 11,包含已經完成的 10 個 |
Resume 最後仍然到達相同的端點集合,所以功能沒有壞。但 checkpoint 的粒度是每個輸入 seed,不是每個 URL——而且記憶體中的去重 filter 不會被持久化,因此對單一 seed 的 resume 其實等於從頭重新爬一次 seed(見 resume-summary.json)。如果你把 500 個主機丟給 Katana,resume 理論上可以幫你省下那些已經完整完成的主機;這種多 seed 行為是由狀態儲存方式推導出來的,但我只實測了單 seed 的情況。如果你正在處理一個超大型站點,resume 帶來的是正確性,不是時間節省。
Known files:有請求,但最後被丟掉

-kf all -d 3 的確有請求到兩個檔案——robots.txt 與 sitemap.xml 都出現在伺服器的命中紀錄中——但最後對 sitemap 裡 <loc> 元素列出的端點,只恢復了 0/2。回想率是 0.0。
在把它判定為限制之前,我先試著排除是我自己的設定有問題。每種變化結果都一樣:
| 嘗試的變化 | 恢復的 sitemap <loc> 端點 |
|---|---|
-kf all | 0/2,recall 0.0 |
-kf sitemapxml | 0/2,recall 0.0 |
-kf robotstxt | 0/2,recall 0.0 |
| depth 3 | 0/2,recall 0.0 |
| depth 4 | 0/2,recall 0.0 |
| depth 5 | 0/2,recall 0.0 |
加上 -jc | 0/2,recall 0.0 |
直接以 /sitemap.xml 作為 seed | 0/2,recall 0.0 |
文件上要求的條件——使用 -kf,而且至少跑到三層深度——每次都滿足了。這不是漏旗標的問題。
真正有用的判斷先講在前面:對這個以 IP literal 為目標的測試案例,不要以為請求 known files 就代表 sitemap 裡的 <loc> URL 也會被加入爬取。請自己驗證 recall,或者直接把那些 URL 擷取出來再餵給 Katana。
v1.6.1 的程式路徑和這個觀察是一致的,但我在執行過程中沒有進一步 instrument。根據 v1.6.1 的 sitemapxml.go,NewNavigationRequestURLFromResponse 會從 response 建立 <loc> 導航請求,但沒有帶上已填入的 RootHostname。之後請求會進入 ValidateScope;在 v1.6.1 的 scope.go 裡,IP-literal 分支會把 URL host 和那個空的 root 進行比較,然後可能把它拒絕掉。至於自訂 -fs '(127.0.0.1|localhost)' 則是在另一個 scope 測試裡走了不同分支,所以它是根據原始碼推測出的可行修復,不是我在 -kf 上實測過的 workaround。由於在這台主機上 known-files client 偶爾會出現連線不穩,確認嘗試也被中斷了,因此目前結果仍維持 0/2。
如果換作我在 production 上實作,我會先自己抓 sitemap,擷取 <loc> URL,然後把它們當成 seed list 交給 Katana。兩行 shell 就夠,而且完全不牽涉 scope validation。
有一件事則完全照預期運作,值得特別說一句:那條 500 路由與死連結都被抓取、被記錄,而且被跳過了。每一次無瀏覽器執行最後都以回傳碼 0 完成。遇到第一個壞 response 就掛掉的爬蟲,沒人能放心交給它長時間跑;Katana 不會這樣。
上線前先做一個針對目標的覆蓋率檢查
這個測試矩陣最有價值的地方,是可以直接拿來當你自己授權目標的測試模板。請在跑 Katana 前先定義端點類別:一般連結、連結腳本中的字串常值、只有執行後才會出現的路由,以及 known-file 條目,都是合理的起始分類。每一類都先保留一小份 ground truth。若沒有這份清單,stdout 再長也可能看起來像覆蓋率變好了,但其實某一類早就消失了。
先分開量測 browserless 與 headless 路徑。保留精確命令、Katana 版本、瀏覽器版本、回傳碼與輸出。不要只比行數,而要正規化後比對端點集合。如果在你的樣本裡,標準 -jc 沒有任何獨特貢獻,那麼只用 headless 或許就夠;如果兩者像這裡一樣結果分歧,就保留兩次流程分開跑,最後再合併。不要想當然地以為單一指令同時加上兩個旗標,就等於兩者結果的聯集,除非你在目標上實際比對過。
也要用 Katana 輸出之外的證據驗證 scope。放一個應該被排除的主機 canary URL,並檢查那台伺服器的 request log。如果預期會擴到第二台主機,也要實測那台主機。這裡的 -cs localhost 之所以產生空輸出,是因為 content scope 篩選沒有擴大 field scope;而 -fs '(127.0.0.1|localhost)' 則確實聯到了第二台伺服器。記錄精確的 regex 很重要,因為只差一個字元,改變的可能是 regex 本身,而不只是顯示結果。
resume 與 known files 要分開測,不要混在 discovery recall 裡一起看。resume 的做法是:中斷一個具代表性的 seed,在跑到幾個頁面後停下來,保存生成的 checkpoint 路徑,然後計算有多少已完成的 URL 又被重新抓了一次。對 -kf 而言,除了確認 robots/sitemap 有被請求,也要確認 sitemap 裡植入的 <loc> URL 真的有被排程。這是兩件不同的事。在這個測試案例裡,檔案是有被抓到,但 sitemap 內的兩個端點沒有被納入,所以必須同時看 request logs 和 endpoint output 才看得出邊界。
最後,先在本地閒置機器上用連續執行建立成本基準,再到代表性主機上重複測一次。請保留最小值、最大值與中位數,不要只留倍率。這裡的 5.1 倍包含了測試案例裡的失敗與 timeout 行為;它告訴你 headless 值得獨立預算,但不能告訴你 production inventory 會花多久。
優點與缺點
優點:
- 在所有模式下,對一般 HTML 的回想率都完美——4/4 連結與完整 3/3 深度鏈,完全不需要額外設定。
-jc在無瀏覽器模式下確實有用:從連結 JS 檔中的字串常值找回 2/2 端點,而且沒有瀏覽器成本。-headless是唯一找出 runtime 組裝端點的模式——這類端點天生無法靠原始碼解析看見。- 預設 scope 很保守。第二台主機在預設、
-fs fqdn或-cs下都沒有被抓到。 - 單一 Go binary、MIT 授權、可直接下載預編譯版本,也有 Docker 映像,輸入輸出方式也很適合接 pipeline。
- 失敗處理穩健:500 與死連結都不會讓爬取中斷。
缺點:
- 沒有任何一次執行能同時涵蓋 JS 檔與 runtime DOM 端點。要完整覆蓋,需要兩次執行再合併。
-jc在-headless下沒有任何額外貢獻——每一次 headless 執行,Class B 都是 0/2。- Headless 的執行時間約為 5.1 倍(p50 為 66.82s 對 13.08s,而且區間完全不重疊)。
-resume會把 seed 內已完成的頁面重新爬一次。它恢復的是端點集合,不是你省下的時間。- known files 有請求 robots.txt 與 sitemap.xml,但對 IP 目標的 sitemap
<loc>端點回想率是 0/2。 - Headless 其實還是需要機器上有 Chromium;「一個 binary 就夠」這句話只到瀏覽器之前。
- 它只做 discovery,不做結構化擷取、內容轉換,也不提供欄位 schema。
未測試、因此也不包含在這些數字中的項目:-jsluice、-d 1 / -d 2 的專門深度截斷測試、多 seed resume、自動表單填寫,以及任何真正 JavaScript-heavy 或受保護的 production site。所有數字都來自一台機器(macOS arm64)對一個本地測試案例的測試。
它適合誰,不適合誰
如果你的工作是為你有授權處理的基礎架構產出端點清單,那 Katana 的流程形狀是對的:STDIN/STDOUT 串接、可分發 binary、同時支援 browserless 與 browser-backed 模式。這個測試案例裡的植入類別,的確需要兩次流程合併;至於你的目標是否也需要兩次,應該由代表性頁面來決定。
如果你要的是資料而不是地址,就跳過它。Katana 永遠不會直接給你產品表;它給你的是產品可能存在的 URL,而真正的擷取要靠別的工具。若你需要一個指令就完成所有事,也可以跳過;兩次流程再合併很適合 pipeline,但在互動式命令列下會有點煩。再者,如果你的枚舉依賴 sitemap <loc> 端點,而且目標又是 IP,請先確認實際拿到的是什麼再相信輸出,因為在我的測試案例裡,那條路徑最後是空的。
替代方案,以及擷取邊界
Katana 是免費、MIT 授權、可自架的工具。它把 discovery、模式選擇、瀏覽器部署與結果合併都留在你這一側。
在開源工具裡,最有意義的比較方式不是語言,而是工作內容。另一個 Go 選項是 Colly,但它是個你要自己寫 callback 才能使用的 library,而且完全不渲染 JavaScript。Crawl4AI 則會跑真正的瀏覽器,並為 LLM pipeline 輸出 Markdown,屬於完全不同的輸出形式。如果你同時在比較好幾個工具,我們的 開源爬蟲總整理 會把這些類別並列整理。
揭露: Thunderbit 是本文發佈方的產品,並未在這個 Katana 測試案例中測試。它屬於受控擷取類別,負責把頁面轉成文字或結構化記錄,而不是列舉授權目標的端點表面。一個流程可以同時用到這兩類工具,但這篇評測只提供 Katana discovery 行為的證據。
結論
當你的交付物是授權爬取目標的端點清單,而且你可以針對那些目標驗證不同模式的覆蓋率時,就用 Katana。在這個測試案例裡,標準 -jc 找回了植入的 JavaScript 檔字串常值,而 headless 找回了 runtime DOM 端點;Katana 經過測試的 browserless 模式則沒能找回那條 runtime 路徑。預設 scope 也確實沒有抓到第二台主機,而 browserless 執行則能繼續越過 500 與死連結。
操作上的限制也很明確:混合端點類別可能需要兩次流程合併、headless 的本機時間大約是五倍、單 seed resume 會重新抓已完成路徑,而 known-files 對 IP 目標的回想率是 0/2。這些都是 v1.6.1 測試案例的結果,不代表所有網站都一樣,但已足夠定義 production 評估時應重複的檢查項目。
使用 Thunderbit 進行網頁資料擷取 Get Started Free
常見問題
Katana 的 resume 檔案存在哪裡?恢復時會跳過我已經爬過的頁面嗎?
檢查點實際寫在 ~/.config/katana/resume-<xid>.cfg,而不是旗標說明文字看起來像的工作目錄 resume.cfg。也不會跳過已完成頁面:檔案裡只存 in-flight 的 seed URL,所以單一 seed 的 resume 會把 11 條 baseline 路徑全部重新抓一次,包括那 10 條已完成的。你會拿到相同的最終端點集合,只是省不到時間。
為什麼 -kf all 會請求我的 sitemap.xml,卻沒有爬其中的 URL?
對 IP 目標來說,這是 scope validation 的邊界問題,而不是少了某個旗標。Katana 的 sitemap parser 在建立每個 <loc> 請求時,沒有把 root hostname 一起帶過去;接著針對 IP-literal host 的 DNS-scope 檢查會把 URL 的 host 拿去和那個空的 root 比較,結果失敗並把 URL 丟掉。我試過的所有旗標、深度與 seeding 變化都維持 0 recall。自訂 -fs host regex 會走不同的驗證分支,而這正是原始碼預測的修補方向——但我沒能在這台機器上用 -kf 實測成功,所以先把它視為未驗證。今天最可信的做法,仍然是自己擷取 <loc> URL,再把它們當 seed 餵給 Katana。
回報 Katana 覆蓋率測試時,我應該保留哪些資訊?
請記錄精確的 Katana 版本與命令,包括逐字不差的 -fs 表達式;在執行前先定義端點類別;保留伺服器端的命中日誌,不只看 stdout;並把實測行為與基於原始碼的假設分開。若是 headless 執行,也要記錄瀏覽器版本——這次沒有記,所以重現性會受限。
我該用 -jc、-headless,還是兩個都用?
請依你需要的端點類型來選。這個測試案例裡,標準 -jc 找到了存放在 JavaScript 檔裡的字串常值,而 headless 找到了插入 runtime DOM 的端點。單用任何一個模式都無法同時涵蓋兩類,所以在混合目標上,先跑兩次再去重會是比較站得住腳的做法。
只要有一個 URL 失敗,爬取就會停掉嗎? 在這次受控測試裡不會。Katana 在遇到 500 response 和死連結後都持續往下爬,最後仍然回傳其他可達路徑。當然,這不代表你可以不用做 production 錯誤統計:請保留失敗請求日誌,並定義可接受的失敗率,避免把部分成功的爬取誤判成完整覆蓋。


