Katana to crawler do wykrywania endpointów od ProjectDiscovery — binarka napisana w Go, na licencji MIT, która bierze adres docelowy i zwraca URL-e oraz endpointy dla kolejnego narzędzia w łańcuchu. Działa w trybie HTTP bez przeglądarki albo z -headless, który uruchamia Chromium. Oficjalne wskazówki przedstawiają tryb headless jako opcję o szerszym pokryciu; ten test pokazuje, że równie ważny jak liczba jest rodzaj endpointu.
Zbudowałem małą witrynę z trzema celowo różnymi klasami endpointów i sprawdziłem, który tryb wykrywa każdy z nich w v1.6.1 przy -d 4. Zwykły HTML był widoczny w 4/4 linków i w pełnym trzyetapowym łańcuchu we wszystkich czterech konfiguracjach. Różnice pojawiły się przy endpointach ujawnianych przez źródło JavaScript albo przez zmiany DOM w czasie działania.
W tym teście headless znalazł klasę opartą o runtime DOM, której nie zauważyły badane tryby bez przeglądarki, natomiast standardowy tryb z -jc znalazł literały w plikach JavaScript, których nie wykryły oba uruchomienia headless. Żaden wiersz w macierzy czterech komend nie pokrył obu klas naraz. Dodatkowe granice praktyczne wyznaczały scope, resume i zachowanie known-files.
Czym właściwie jest katana
Crawler katana — projectdiscovery/katana na GitHubie — jest napisany w Go i wydany na licencji MIT. Testowałem v1.6.1 27 lipca 2026 r.; wersja ma znaczenie, bo wnioski o pokryciu i known-files poniżej są obserwacjami zależnymi od konkretnego buildu.
Tu kategoria ma większe znaczenie niż zwykle. Crawler do wykrywania endpointów nie jest ekstraktorem danych. Jeśli szukasz go web crawlera, który zwróci nazwy produktów i ceny jako uporządkowany JSON, katana jest zupełnie nie tym narzędziem — z przyjemnością powie Ci, że /products/1138 istnieje, ale nie powie nic o tym, co znajduje się na tej stronie. Tak właśnie ma działać, a ocenianie jej pod kątem ekstrakcji byłoby jak recenzowanie wykrywacza metalu za to, czy umie wycenić biżuterię.
To narzędzie jest stworzone do reconu w security ofensywnej i automatyzacji pipeline’ów: STDIN do środka, URL-e na wyjściu, a potem przekazanie wyników do kolejnego narzędzia. I tu od razu ważne zastrzeżenie — wszystkie pomiary wykonałem na lokalnym fikcyjnym serwerze na 127.0.0.1, który sam napisałem. Używaj katany wyłącznie wobec hostów, do których masz prawo testów i które są przez Ciebie autoryzowane. Nie chodzi tu o obchodzenie zabezpieczeń — tylko o to, ile powierzchni endpointów dany wynik rzeczywiście wylicza.
Trzy tryby i to, co każdy z nich widzi
Tryb standardowy to klient HTTP w Go. Pobiera strony, parsuje HTML, podąża za href i nigdy nie uruchamia przeglądarki. Jest szybki, tani i ślepy na wszystko, co pojawia się dopiero po wykonaniu JavaScriptu.
-jc (-js-crawl) dokłada parser JavaScript do tej ścieżki bez przeglądarki. Pobiera podłączone pliki .js i wyciąga ze źródła stringi wyglądające jak URL-e. Bez wykonywania kodu — tylko odczyt. Jest też -jsl (jsluice), opisany w README jako cięższy i bardziej pamięciożerny parser — nie testowałem go, więc nie mogę powiedzieć, czy zmienia obraz pokrycia.

-headless uruchamia Chromium i wykonuje skrypty strony. W tym teście był to jedyny badany tryb Katany, który odzyskał ścieżkę złożoną z fragmentów i wstrzykniętą do runtime DOM. Ten wynik nie przesądza, co odzyska każdy parser albo przyszły tryb Katany.
Jest jeszcze model scope — i to jest część, którą warto zapamiętać zanim wpisze się cokolwiek produkcyjnego.
| Flaga | Co kontroluje | Wartości / domyślne |
|---|---|---|
-fs (field scope) | które hosty wchodzą w grę | dn, rdn, fqdn albo własny regex — domyślnie rdn |
-cs i -cos | regexy URL-i, które filtrują wewnątrz tego field scope | — |
-kf | known files: robots.txt i sitemap.xml | w README podano, że wymaga minimalnej głębokości 3 |
-d | głębokość | domyślnie 3 |
-resume | wznawia przerwany crawl | — |
Kolejność nie jest kosmetyczna: decyduje, czy regex hosta rozszerzy crawl, czy po cichu wyczyści go do zera.
Konfiguracja: jedna binarka, jeden gwiazdkowy wyjątek
Są trzy drogi instalacji i tylko jedna wymaga toolchaina:
| Sposób instalacji | Wymaganie |
|---|---|
Ze źródeł: go install github.com/projectdiscovery/katana/cmd/katana@latest | wymagana jest wersja Go 1.25 lub nowsza |
| Prekompilowane binarki na stronie release | bez toolchaina |
| Obraz Docker | bez toolchaina |
U mnie binarka trafiła do ~/go/bin/katana i przy każdym uruchomieniu raportowała Current version: v1.6.1. Na tym etapie klasyczna przyjemna historia Go: jeden plik, bez runtime.
Gwiazdkowy wyjątek dotyczy headless, gdzie przeglądarka jest osobnym wymaganiem względem binarki:
Gdzie działa -headless | Czego potrzebuje |
|---|---|
| Na mojej maszynie | katana automatycznie wykryła już zainstalowane Chromium; nie zapisałem wersji przeglądarki ani nie podawałem ścieżki |
| Na czystym serwerze, zgodnie z instrukcjami projektu dla Ubuntu | najpierw apt install google-chrome-stable, zanim headless zacznie działać |
| W wariancie Docker | działa headless z -system-chrome |
Na czystym serwerze ta wygoda znika. Gdy tylko -headless pojawia się w poleceniu, trzeba budżetować nie tylko binarkę, ale i przeglądarkę.
Jeszcze jedna drobna rzecz, warta uwagi, jeśli uruchamiasz to w CI: katana przy starcie sprawdza wersję na GitHubie. -duc wyłącza ten check. Na laptopie to szum, ale na runnerze bez dostępu do sieci albo z limitem żądań to dodatkowy round trip sieciowy przy każdym uruchomieniu, o który nie prosiłeś. W moich pomiarach czasu użyłem -duc, żeby liczby odzwierciedlały crawl, a nie telefon do domu.
Jak to testowałem
Trzy klasy endpointów, dobrane specjalnie tak, by rozdzielały tryby. Całość działa na lokalnym serwerze testowym, a stan referencyjny został zapisany zanim wystartował jakikolwiek crawl, więc recall liczy się względem stałego zestawu, a nie tego, co akurat wypisała katana.
- Klasa A — zwykły HTML.
/page/a,/page/b,/page/coraz trzyetapowy łańcuch/depth/1 → /depth/2 → /depth/3. Każdy crawler powinien to złapać. - Klasa B — literały w pliku JavaScript.
/api/js-endpoint-7i/api/js-endpoint-8istnieją tylko jako stringi w podłączonym/static/app.js. Da się je odczytać bez przeglądarki, o ile coś w ogóle czyta JS. - Klasa C — tylko runtime DOM. Ścieżka składana w czasie działania z fragmentów (
'endpoint' + (6 * 7)) i wstrzykiwana do DOM przez skrypt. String/runtime-only/endpoint42nie występuje ciągiem w żadnym bajcie wysyłanym przez serwer — ani w HTML, ani w źródle JS. Tylko wykonanie ujawnia tę ścieżkę.
Do tego robots.txt, sitemap.xml z dwoma endpointami <loc> niewystępującymi nigdzie indziej, route zwracający 500, martwy link i link poza zakresem prowadzący do drugiego serwera na innym hostname.
Równie ważny jak fixture jest instrument: serwer liczy to, co faktycznie zostało pobrane, więc twierdzenia o scope i resume opierają się na prawdzie z hit logów, a nie na stdout Katany. Surowe uruchomienia są w repo benchmarkowym, jeśli chcesz sprawdzić moje rachunki.
Różnica w pokryciu, której nikt nie mierzy

Macierz, tryb względem klasy endpointu, przy -d 4:
| Tryb | Linki HTML (A) | Łańcuch głębokości (A) | Literały z pliku JS (B) | Runtime DOM (C) |
|---|---|---|---|---|
| standard | 4/4 | 3/3 | 0/2 | nie znaleziono |
standard -jc | 4/4 | 3/3 | 2/2 | nie znaleziono |
-headless | 4/4 | 3/3 | 0/2 | znaleziono |
-headless -jc | 4/4 | 3/3 | 0/2 | znaleziono |
Jeśli czytać dwie ostatnie kolumny razem, problem od razu wychodzi na wierzch. Klasa B została znaleziona tylko przez jedną konfigurację: standardowy tryb z -jc. Klasa C została znaleziona tylko przez dwie: oba uruchomienia headless. Nie ma wiersza, który trafia w obie kolumny naraz. Pełna macierz jest w discovery-summary.json, gdzie obliczone pole headless_jc_covers_both ma wartość false.
Praktyczny wniosek jest taki, że „po prostu użyj headless, będzie lepsze pokrycie” okazało się tu zbyt uproszczone. Headless nie dodał klasy B do wyniku standardowego; odzyskał klasę C, ale nie znalazł klasy B. Pokrycie wszystkich zasadzonych klas w tym fixture wymagało dwóch crawlów i scalenia:
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
Wiersz -headless -jc to ten, na który najchętniej zobaczyłbym odpowiedź upstream. Dodanie parsera JavaScript do uruchomienia headless nie odzyskało niczego — dalej 0/2 dla klasy B, w każdym uruchomieniu, także po świeżej reprodukcji. Raportuję zachowanie, nie twierdzę, że prześledziłem mechanizm; nie instrumentowałem wnętrza Katany, by sprawdzić, dlaczego ścieżka przeglądarkowa przestaje wnosić literały z plików JS. Traktuj to jako powtarzalną obserwację i dobry issue na GitHubie, a nie diagnozę. (Warto przy tym odnotować, że kombinacja -hl -jc kończyła się poprawnie z kodem 0 w v1.6.1 na macOS ARM, co historycznie nie zawsze było prawdą.)
Oficjalna dokumentacja opisuje headless jako dający lepsze pokrycie, i faktycznie tak było dla klasy renderowanej w runtime w tym teście. Zweryfikowane wskazówki nie opisały jednak tego rozdziału source-literal/runtime-DOM, więc potraktuj tę macierz jako powód, by przetestować obie ścieżki na własnych klasach endpointów, a nie jako uniwersalną taksonomię.
Ile kosztuje headless w czasie wall clock
Trzy kolejne uruchomienia na tryb na inaczej bezczynnej maszynie:
| Tryb | p50 | min–max | średnia |
|---|---|---|---|
| standard | 13.08s | 13.07–13.17s | 13.11s |
-headless | 66.82s | 66.78–67.68s | 67.09s |
To stosunek 5,1x, a zakresy nawet się do siebie nie zbliżają — mój najwolniejszy run standardowy (13.17s) nadal był o ponad 53 sekundy szybszy od mojego najszybszego uruchomienia headless (66.78s) (cost-summary.json). To nie jest szum pomiarowy.
Jedno zastrzeżenie do tych 13 sekund: moja fixture celowo zawiera route 500 i martwy link, a tryb standardowy spędza przy obu domyślny ogon ponownych prób -timeout 10. Nie stroiłem timeoutu tak, by ładniej wyglądał szybki tryb, co oznacza, że po optymalizacji standardowy run prawdopodobnie jeszcze bardziej rozszerzyłby różnicę, a nie ją zmniejszył.
Ten współczynnik to lokalny sygnał pojemności, nie prognoza produkcyjna. Prawdziwe cele różnią się opóźnieniami, liczbą błędów, pracą skryptów i planowaniem zadań, podczas gdy ta fixture zawiera domyślny ogon timeoutu. Użyj zmierzonej różnicy 5,1x, żeby zdecydować, czy headless zasługuje na osobny budżet i podzbiór celów, a potem sprawdź ten plan na reprezentatywnych, autoryzowanych hostach.
Scope się utrzymał, ale jedna flaga po cichu nic nie zrobiła
Test scope używał dwóch serwerów: głównego na 127.0.0.1 oraz drugiego dostępnego jako localhost na innym porcie, serwującego ścieżkę, która istnieje wyłącznie tam. Trafienie w tę ścieżkę dowodzi więc, że host poza zakresem został rzeczywiście pobrany, a nie tylko wypisany.
| Konfiguracja | Host poza zakresem pobrany? | Trafienia na drugim serwerze |
|---|---|---|
domyślna (-fs rdn) | nie | 0 |
-fs fqdn | nie | 0 |
-cs localhost | nie | 0 |
| `-fs '(127.0.0.1 | localhost)'` | tak |
Dyscyplina scope to dobra wiadomość: domyślnie katana zostawała w granicach, a rozszerzenie wymagało świadomego działania. To właściwy default dla narzędzia, które trafia do cudzej infrastruktury.
Najciekawszy jest wiersz -cs localhost. Nie rozszerzył on crawl’a na drugi host — i jednocześnie zwrócił zero URL-i. Ponieważ -cs filtruje wewnątrz field scope, a field scope nadal wskazywał na główny host, regex nie dopasował niczego i crawl zwrócił pusty zbiór zamiast błędu. Jeśli kiedyś pisałeś regex scope’u na host, który chciałeś dołączyć, i patrzyłeś w pusty plik wyjściowy, to właśnie ten mechanizm (scope-summary.json). Żeby dodać host, ustaw -fs. Żeby zawęzić już posiadane hosty, użyj -cs/-cos.
Resume jest bardziej zgrubne, niż sugeruje flaga
README opisuje flagę jako -resume string resume scan using resume.cfg, co brzmi jak plik lądujący w katalogu roboczym. Tak nie jest. Na mojej maszynie checkpoint był zapisywany do ~/.config/katana/resume-<xid>.cfg — zmierzone, nie odczytane z dokumentacji, bo dokumentacja nie podaje ścieżki.
Zawartość jest jeszcze ciekawsza. Plik trzymał mapę InFlightUrls zawierającą dokładnie jedną rzecz: seed URL. Nie odwiedzone URL-e, nie frontiera. I właśnie tak wyglądało to, gdy przerwałem crawl SIGINT-em po trzech sekundach i potem go wznowiłem:
| Uruchomienie | Unikalne ścieżki |
|---|---|
| Pełny crawl bazowy | 11 |
| Pobranie przed przerwaniem | 10 |
| Ponownie pobrane przez wznowienie | wszystkie 11, w tym wszystkie 10 już ukończonych |
Resume doprowadziło do tego samego finalnego zestawu endpointów, więc nic nie jest zepsute. Ale ziarnistość checkpointu jest na seed wejściowy, nie na pojedynczy URL — filtr deduplikacji w pamięci nie jest nigdzie utrwalany, więc wznowienie crawl’a z jednym seedem przechodzi przez ten seed od zera (resume-summary.json). Jeśli podasz katanie listę 500 hostów, resume powinno oszczędzić hosty, które zakończyły się w całości; to zachowanie wieloseedowe wynika z tego, jak przechowywany jest stan, ale ja mierzyłem tylko przypadek z jednym seedem. Jeśli siedzisz głęboko w jednej ogromnej witrynie, resume daje Ci poprawność, nie czas.
Known files: poproszone, a potem porzucone

-kf all -d 3 rzeczywiście pobrało oba pliki — robots.txt i sitemap.xml pojawiły się w logu hitów serwera — a potem odzyskało 0 z 2 endpointów wypisanych w elementach <loc> tej sitemap. Recall 0.0.
Zanim uznałem to za ograniczenie, próbowałem zrobić z tego swój błąd. Każda wariacja dawała to samo:
| Przetestowana wariacja | Odzyskane endpointy z 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 |
z dodanym -jc | 0/2, recall 0.0 |
z seedem bezpośrednio na /sitemap.xml | 0/2, recall 0.0 |
Wymóg opisany w dokumentacji — użyj -kf i wejdź przynajmniej trzy poziomy głębokości — był spełniony za każdym razem. To nie jest historia o brakującej fladze.
Najpierw decyzja praktyczna: na tej testowej konfiguracji opartej na IP nie zakładaj, że samo zażądanie known files oznacza, że URL-e z <loc> zostały dołączone do crawl’a. Sprawdź recall albo wyciągnij i zasiej te URL-e samodzielnie.
Kod v1.6.1 jest spójny z tą obserwacją, ale nie instrumentowałem go podczas uruchomienia. W sitemapxml.go w v1.6.1 NewNavigationRequestURLFromResponse buduje requesty na podstawie <loc> bez wypełnionego RootHostname. Następnie request trafia do ValidateScope; w scope.go w v1.6.1 gałąź dla IP-literal porównuje host URL-a z tym pustym rootem i może go odrzucić. Własna komenda -fs '(127.0.0.1|localhost)' w innym teście scope przeszła inną gałąź walidacji, więc jest to przewidziane ze źródła obejście, a nie zmierzony workaround dla -kf. Próba potwierdzenia została zablokowana przez sporadyczne problemy z dialowaniem w kliencie known-files na tym hoście; raportowany wynik pozostaje więc 0/2.
W praktyce, dopóki ktoś nie potwierdzi tej flagi, zrobiłbym tak: pobrać sitemapę samemu, wyciągnąć URL-e z <loc> i podać je katanie jako listę seedów. Dwie linie shellowe, bez walidacji scope po drodze.
Jedna rzecz zachowywała się dokładnie tak, jak powinna, i zasługuje na zdanie: route 500 i martwy link zostały pobrane, zalogowane i pominięte. Każde uruchomienie bez przeglądarki kończyło się kodem 0. Crawler, który pada na pierwszym błędnym response, jest bezużyteczny bez nadzoru, a katana taki nie jest.
Jak przed wdrożeniem sprawdzić pokrycie dla konkretnego targetu
Macierz z fixture jest najbardziej użyteczna jako szablon do testowania własnych autoryzowanych targetów. Zdefiniuj klasy endpointów przed uruchomieniem Katany: zwykłe linki, literały w podłączonych skryptach, ścieżki tworzone dopiero po wykonaniu kodu i wpisy z known files to cztery rozsądne początkowe koszyki. Dla każdej klasy trzymaj mały zestaw ground truth. Bez takiej wcześniejszej listy większy stdout może wyglądać jak lepsze pokrycie, nawet jeśli jakaś klasa po prostu zniknęła.
Najpierw uruchom osobno ścieżki browserless i headless jako niezależne pomiary. Zapisz dokładne komendy, wersję Katany, wersję przeglądarki, kody zwrotne i output. Normalizuj i porównuj zbiory endpointów, zamiast zestawiać same liczby linii. Jeśli standardowe -jc nie wnosi nic unikalnego na Twoim próbku, polityka tylko-headless może wystarczyć; jeśli zbiory rozchodzą się tak jak tutaj, trzymaj oba przebiegi osobno i scal po zebraniu. Nie zakładaj, że dodanie obu flag do jednej komendy daje sumę, dopóki diff specyficzny dla targetu tego nie potwierdzi.
Zweryfikuj scope danymi spoza outputu Katany. Umieść canary URL na hoście, który ma pozostać wykluczony, i sprawdź log żądań po stronie serwera. Jeśli crawl ma się rozszerzyć na drugi host, przetestuj również ten docelowy drugi host. Uruchomienie -cs localhost tutaj wygenerowało pusty output, bo filtrowanie content-scope nie rozszerzyło field scope; własne wywołanie -fs '(127.0.0.1|localhost)' rzeczywiście połączyło się z drugim serwerem. Zapisanie dokładnego wyrażenia regularnego ma znaczenie, bo zmiana jednego znaku może zmienić regex, a nie tylko jego zapis.
Testuj przerwanie i known files osobno od recallu discovery. Dla resume przerwij reprezentatywny seed po kilku stronach, zapisz wygenerowaną ścieżkę checkpointu i policz, ile już ukończonych URL-i zostało pobranych ponownie. Dla -kf potwierdź zarówno to, że robots/sitemap zostały zażądane, jak i to, że zasiane URL-e z <loc> faktycznie trafiły do harmonogramu. To są różne twierdzenia. W tym teście pliki zostały pobrane, ale oba endpointy z sitemap zniknęły, więc do zobaczenia granicy potrzebne były zarówno logi żądań, jak i output endpointów.
Na koniec ustanów lokalny baseline kosztu, wykonując kolejne uruchomienia na bezczynnej maszynie, a potem powtórz to na reprezentatywnych hostach. Zachowaj minimum, maksimum i medianę, a nie tylko mnożnik. Współczynnik 5,1x tutaj uwzględnia błędy i timeout tej fixture; mówi Ci, że headless zasługuje na osobny budżet, a nie ile potrwa produkcyjny inventory crawl.
Plusy i minusy
Plusy:
- Perfekcyjny recall dla zwykłego HTML we wszystkich trybach — 4/4 linków i pełny łańcuch 3/3, bez dodatkowej konfiguracji.
-jcdziała naprawdę bez przeglądarki: 2/2 endpointów odzyskanych z literałów w podłączonym pliku JS, bez kosztu przeglądarki.-headlessjako jedyny znalazł endpoint złożony w czasie działania — klasę, która z definicji jest niewidoczna dla parsowania źródła.- Domyślne scope jest zachowawcze. Host poza zakresem nigdy nie został pobrany przy default,
-fs fqdnani-cs. - Jedna binarka Go, licencja MIT, prekompilowane buildy i obraz Docker, I/O w stylu pipeline.
- Odporność na błędy: 500 i martwe linki nie zatrzymują crawl’a.
Minusy:
- Żadna pojedyncza komenda nie pokryła jednocześnie endpointów z pliku JS i z runtime DOM. Pełne pokrycie wymaga dwóch uruchomień i scalenia.
-jcnie wnosił nic przy-headless— 0/2 dla klasy B w każdym uruchomieniu headless.- Headless kosztuje 5,1x czasu wall clock (p50 66.82s vs 13.08s, zakresy bez nakładania).
-resumeponownie pobiera ukończone strony w obrębie jednego seeda. Przywraca zestaw endpointów, nie Twój czas.- Known files zażądały robots.txt i sitemap.xml, ale odzyskały 0/2 endpointów
<loc>z sitemap wobec targetu opartego na IP. - Headless po cichu wymaga obecnego w systemie Chromium; historia o „jednej binarce” kończy się na przeglądarce.
- Tylko discovery. Bez ekstrakcji strukturalnej, bez konwersji treści, bez schematu pól.
Nie testowałem, więc poza zakresem tych liczb pozostaje: -jsluice, osobny test odcięcia głębokości na -d 1/-d 2, resume z wieloma seedami, automatyczne wypełnianie formularzy i jakakolwiek prawdziwa produkcyjna strona silnie oparta na JavaScript albo chroniona. Wszystkie liczby pochodzą z jednej maszyny (macOS arm64) i lokalnej fixture.
Dla kogo to jest, a kto powinien to pominąć
Jeśli Twoją pracą jest tworzenie inwentarza endpointów infrastruktury, do której masz prawo, katana ma właściwy kształt pipeline’u: plumbing STDIN/STDOUT, binarka do dystrybucji oraz tryby bez przeglądarki i z przeglądarką. W tym teście potrzebne było dwupasmowe scalanie; czy Twoje targety też tego wymagają, trzeba ustalić na reprezentatywnych stronach.
Pomiń to, jeśli chcesz danych, a nie adresów. Katana nigdy nie zwróci tabeli produktów; poda URL-e, pod którymi produkty mogą się znajdować, a do ekstrakcji służy coś innego. Pomiń też, jeśli potrzebujesz, by jedna komenda załatwiała wszystko — dwupasmowe scalanie jest w pipeline’ie w porządku, ale w promptcie bywa irytujące. A jeśli Twoje enumerate opiera się na endpointach <loc> z sitemap, a celem są adresy IP, sprawdź najpierw, co naprawdę dostajesz, zanim zaufasz wynikowi, bo na mojej fixture ta ścieżka zwracała nic.
Alternatywy i granica ekstrakcji
Katana jest darmowa, na licencji MIT i self-hostowana. To po Twojej stronie zostawia discovery, wybór trybu, deployment przeglądarki i scalanie wyników.
W open source sensowne porównania robi się raczej po zadaniu niż po języku. Colly to druga opcja w Go, ale jest biblioteką, którą kompilujesz z własnymi callbackami i w ogóle nie renderuje JavaScriptu. Crawl4AI uruchamia prawdziwą przeglądarkę i produkuje Markdown dla pipeline’ów LLM, co daje zupełnie inny output. Jeśli porównujesz kilka takich narzędzi naraz, nasze open-source scraper roundup zestawia te kategorie obok siebie.
Ujawnienie: Thunderbit to produkt wydawcy i nie był testowany w tej fixture Katany. Siedzi dalej w kategorii zarządzanej ekstrakcji, zamieniając strony na tekst lub rekordy strukturalne, zamiast wyliczać powierzchnię endpointów autoryzowanego targetu. Workflow może korzystać z obu kategorii, ale ten test dostarcza dowodów wyłącznie na zachowanie discovery Katany.
Wypróbuj Thunderbit do ekstrakcji danych z sieci
Werdykt
Użyj katany, gdy rezultatem ma być lista endpointów dla targetów, które wolno Ci crawlować, i możesz zweryfikować pokrycie trybów na tych właśnie celach. W tym teście standardowe -jc odzyskało zasiane literały z pliku JavaScript, a headless odzyskał endpoint wstawiany do runtime DOM; badane tryby bez przeglądarki nie odzyskały tej ścieżki runtime. Domyślny scope zatrzymał też drugi host poza pobraniem, a uruchomienia bez przeglądarki przechodziły dalej mimo 500 i martwego linku.
Zastrzeżenia są operacyjne: przy mieszanych klasach endpointów może być potrzebne scalanie z dwóch przebiegów, headless trwał lokalnie około pięć razy dłużej, single-seed resume ponownie pobierał ukończone ścieżki, a recall known-files wyniósł 0/2 wobec targetu IP. To są wyniki fixture v1.6.1, a nie gwarancje dla każdej strony. Wystarczą jednak, by zdefiniować kontrole, które powinno się powtórzyć w produkcyjnej ocenie.
Wypróbuj Thunderbit do ekstrakcji danych z sieci Get Started Free
FAQ
Gdzie katana zapisuje plik resume i czy wznawianie pomija strony, które już przeskanowałem?
Checkpoint trafił do ~/.config/katana/resume-<xid>.cfg, a nie do resume.cfg w katalogu roboczym, jak sugeruje help przy fladze. I nie, nie pomija ukończonych stron: plik przechowuje tylko seed URL-e w locie, więc wznowiony crawl z jednym seedem pobrał ponownie wszystkie 11 ścieżek bazowych, w tym 10 już ukończonych. Dostajesz ten sam finalny zestaw endpointów, tylko bez zaoszczędzonego czasu.
Dlaczego -kf all pobrało moją sitemap.xml, ale nie przeskanowało URL-i w środku?
Wobec targetu opartego na IP wynik ten jest spójny z granicą walidacji scope, a nie z pomyłką w fladze. W kodzie v1.6.1 parser sitemap Katany buduje każdy request <loc> bez przenoszenia dalej root hostname, a DNS-scope check dla hostów typu IP-literal może potem odrzucić taki URL; nie instrumentowałem uruchomienia, by to potwierdzić. Na każdej wersji flag, depth i seeda, które próbowałem, wynik pozostawał na 0 recall. Własny regex hosta w -fs przechodzi inną gałąź walidacji i to on, według źródeł, powinien pomóc — ale nie potwierdziłem tego przeciw -kf na swojej maszynie, więc traktuj to jako nieprzetestowane. Dzisiaj najbardziej ufałbym samodzielnemu wyciągnięciu URL-i z <loc> i podaniu ich katanie jako seedów.
Co powinienem zapisać, raportując test pokrycia Katany?
Zanotuj dokładną wersję Katany i komendę, łącznie z wyrażeniem -fs zapisanym znak w znak; zdefiniuj klasy endpointów przed uruchomieniem; zachowaj server-side hit logi oprócz stdout; i oddzielaj obserwowane zachowanie od hipotez opartych na źródle. Dla uruchomień headless zapisuj też build przeglądarki — w tym teście tego nie zrobiono, co ogranicza reprodukcję.
Czy używać -jc, -headless, czy obu?
Wybór zależy od klas endpointów, których potrzebujesz. W tej fixture standardowe -jc znalazło literały zapisane w pliku JavaScript, a headless znalazł endpoint wstawiony do runtime DOM. Żaden z tych trybów nie pokrył samodzielnie obu klas, więc dwupasmowe uruchomienie z deduplikacją było tutaj najrozsądniejsze dla mieszanego targetu.
Czy jeden błędny URL zatrzyma crawl? W tym kontrolowanym uruchomieniu nie. Katana kontynuowała po odpowiedzi 500 i martwym linku, a mimo to zwróciła pozostałe osiągalne ścieżki. To nie zastępuje produkcyjnego rozliczania błędów: trzymaj logi nieudanych requestów i ustal akceptowalny poziom błędów, żeby częściowo udany crawl nie został pomylony z pełnym pokryciem.


