Większość osób trafia na Crawlee, gdy próbuje odpowiedzieć na inne pytanie: „której przeglądarki headless powinienem użyć?”. To jednak złe pytanie — i właśnie dlatego istnieje Crawlee. To nie jest przeglądarka. To framework dla Node/TypeScript, który korzysta z przeglądarki wtedy, gdy jest potrzebna, a omija ją, gdy nie ma takiej potrzeby.
Przez kilka dni testowałem Crawlee 3.17.0 na kontrolowanym zestawie fixture’ów i kilku publicznych stronach demo, na Node v22.22.3 i macOS. Główna obietnica — jedna biblioteka, jedno API, a pod spodem albo crawler HTTP, albo prawdziwa przeglądarka — była tym, co najbardziej chciałem sprawdzić, bo to właśnie ona decyduje, czy Crawlee warto dodać do swojego stacku, czy lepiej od razu sięgnąć bezpośrednio po Playwright. Krótko: historia o dwóch silnikach się broni, choć z kilkoma zastrzeżeniami, do których za chwilę przejdę.
Czym Crawlee naprawdę jest, a czym nie jest
Crawlee opisuje się jako bibliotekę do web scrapingu i automatyzacji przeglądarki dla Node.js, stworzoną z myślą o niezawodnych crawlerach. Oficjalne pozycjonowanie jest szerokie: wyciąganie danych dla AI, LLM-ów, RAG czy GPT; pobieranie HTML, PDF, JPG, PNG i innych plików; współpraca z Puppeteer, Playwright, Cheerio, JSDOM oraz surowym HTTP; tryb z interfejsem lub headless; wbudowana rotacja proxy. To ogromny zakres możliwości, więc warto też powiedzieć, czym Crawlee nie jest.
To nie jest silnik renderujący. Nie ma własnej przeglądarki. Gdy potrzebujesz uruchomić JavaScript, Crawlee steruje Playwrightem albo Puppeteerem, a te z kolei uruchamiają Chromium (lub inną przeglądarkę). To również nie jest usługa hostowana, do której dzwonisz po sieci — to zależność, którą instalujesz i uruchamiasz samodzielnie. Tym, czym Crawlee jest dokładnie, jest warstwa nad pobieraniem treści: klasy crawlerów, kolejka żądań, storage, logika podążania za linkami. Myśl o nim jak o frameworku do crawlowania z wymiennym silnikiem pod spodem.
Dla porządku: testowana przeze mnie wersja to 3.17.0 (wydana 2026-06-04), projekt jest w TypeScript, licencja to Apache-2.0, a repozytorium miało około 24,6 tys. gwiazdek na dzień 2026-07-09 w apify/crawlee. Liczba gwiazdek zmienia się z czasem — w ciągu dwóch dni obserwacji repo zyskało 53 — więc traktuj to jako migawkę, a nie stałą wartość.
Dwa silniki: CheerioCrawler vs PlaywrightCrawler
Tu właśnie projekt pokazuje swoją wartość — i tu spędziłem najwięcej czasu.
CheerioCrawler to ścieżka HTTP. Pobiera surowy HTML przez sieć i parsuje go przez Cheerio — bez przeglądarki, bez wykonywania JavaScriptu, bez renderowania. Jest szybki i tani. PlaywrightCrawler to ścieżka przeglądarkowa. Uruchamia prawdziwy Chromium, renderuje stronę wraz z całym JavaScriptem budującym DOM i potrafi nawet zrobić screenshot.
Dwa różne silniki, z realnie różnymi możliwościami. Rzecz, którą komunikuje Crawlee, polega na tym, że oba wyglądają prawie tak samo z zewnątrz. Oba przyjmują requestHandler. Oba mają run(). Oba podążają za linkami przez enqueueLinks. Przejście z jednego silnika na drugi to podmiana klasy, a nie przepisanie kodu — potwierdziłem to, zachowując logikę ekstrakcji w 100% identyczną i zmieniając wyłącznie klasę crawlera, która ją opakowywała.

Trzeba jednak precyzyjnie zaznaczyć miejsce, gdzie ta zgodność się kończy: uchwyt do treści jest inny. W handlerze CheerioCrawler dostajesz $ — statyczny, już sparsowany DOM, po którym zapytania wykonujesz jak w jQuery. W handlerze przeglądarkowym dostajesz żywy obiekt page. Kolejka, routing i całe „wrzuć te dane, podążaj za tamtymi linkami” pozostają takie same, ale miejsce, w którym faktycznie czytasz stronę, zmienia kształt. Sama dokumentacja Crawlee mówi o tym wprost — wspólny interfejs obejmuje operacje crawl, a dostęp do treści jest tym elementem, który się różni.
| Silnik | Sposób pobierania | Wykonuje JavaScript? | Mój test (1 dynamiczna strona) | Najlepszy do |
|---|---|---|---|---|
CheerioCrawler | Surowy HTTP + parsowanie Cheerio | Nie | ~0,035 s | Statyczny HTML, API JSON, szybkość |
PlaywrightCrawler | Prawdziwy Chromium przez Playwright | Tak | ~4,967 s | Strony renderowane przez JS, screenshoty |
Te czasy pochodzą z jednej maszyny i jednego uruchomienia — to nie benchmark, tylko ilustracja kompromisu. Ścieżka przeglądarkowa kosztowała na tym samym URL-u około dwa rzędy wielkości więcej czasu. To cena renderowania, dlatego nie wybiera się jej domyślnie.
Test: ten sam URL, 0 kontra 8/8
Obietnice są tanie. Powód, dla którego ufam historii o dwóch silnikach, jest prosty: udało mi się ją złamać, a potem naprawić, podmieniając jedną klasę.
Zbudowałem lokalny dynamiczny fixture — stronę katalogu, na której karty produktów są wstrzykiwane przez JavaScript po załadowaniu, czyli dokładnie tak, jak działają dziś ogromne połacie współczesnego internetu. Podpiąłem pod nią CheerioCrawler. Wynik: 0 kart produktów. To nie błąd; to fizyka. Cheerio nigdy nie uruchomił JavaScriptu, więc karty nie istniały w HTML-u, który parsował. Potem podłączyłem do tego samego URL-a PlaywrightCrawler, nie zmieniając niczego innego, i renderowanie zwróciło 8 z 8 produktów oraz zrobiło screenshot jako dowód.

Żeby upewnić się, że to nie był tylko kaprys mojego własnego fixture’a, powtórzyłem ten sam schemat na publicznej stronie — demo Quotes to Scrape, które buduje cytaty po stronie klienta. Wynik był identyczny w tym samym kierunku: CheerioCrawler zobaczył 0 cytatów, PlaywrightCrawler odzyskał 10.

Chcę jasno powiedzieć, co to dowodzi. To czysta reprodukcja tego, co Crawlee już dokumentuje — framework od wersji 3.0 dzieli tę samą klasę bazową i wspólny interfejs pomiędzy typami crawlerów. To więc weryfikacja, nie odkrycie. Ale właśnie dlatego to ma wartość: marketingowe hasło „jedno API, HTTP albo przeglądarka” jest prawdziwe, a tutaj masz dowód 0 → pełne dane zarówno na fixture’ze, nad którym mam kontrolę, jak i na stronie, której nie kontroluję.
Gdzie wygrywa ścieżka HTTP
Łatwo byłoby po przeczytaniu powyższego dojść do wniosku: „zawsze używaj przeglądarki”. Nie rób tego. Cały sens tej dwu-silnikowej architektury polega na tym, że przeglądarka jest kosztownym planem awaryjnym, a nie domyślnym wyborem.
Na treściach statycznych CheerioCrawler był szybki i dokładny. Mój statyczny fixture katalogu zwrócił 12 z 12 produktów z pełnym recall, przechodząc przez paginację za pomocą enqueueLinks({ selector: '.next-page' }), w około 0,155 sekundy. Strona artykułu oddała tytuł i wszystkie 3 z 3 akapitów, a cały boilerplate dotyczący logowania/subskrypcji/praw autorskich został czysto oddzielony od właściwej treści.
Najważniejszy przykład: strona, której dane ładowane są przez JavaScript, często ma za sobą zwykłe JSON API. Dane mojego dynamicznego fixture’a były dostępne pod endpointem i kiedy skierowałem CheerioCrawler bezpośrednio na to API, odzyskał 8 z 8 produktów — bez przeglądarki, w około 0,035 sekundy. Te same dane, które ścieżka browserowa renderowała prawie pięć sekund. Wniosek jest stary, ale wciąż aktualny: jeśli możesz odtworzyć żądanie źródłowe, zrób to zamiast uruchamiać Chromium. Crawlee pozwala podjąć tę decyzję osobno dla każdego crawlera, bez zmiany frameworka.
Część frameworka do crawlowania — czyli dlaczego wybrać Crawlee zamiast samej biblioteki do przeglądarki
Gdyby chodziło tylko o wyrenderowanie jednej strony, Crawlee nie byłoby potrzebne — wystarczyłby sam Playwright albo Puppeteer. Czego zwykła biblioteka przeglądarkowa nie daje, to crawl: kolejki, deduplikacji, kontroli głębokości, ponawiania prób. I właśnie o tę część Crawlee chodzi równie mocno jak o silniki.
Uruchomiłem crawl po tej samej domenie, startując od rootowego fixture’a i używając enqueueLinks z kontrolą głębokości. Crawlee przeszedł przez 11 stron na poziomach {0:1, 1:3, 2:7} — jedna strona główna, trzy strony jeden klik dalej, siedem stron dwa kliki dalej — i respektował maxRequestsPerCrawl jako warunek zatrzymania. Za bookkeeping odpowiadał RequestQueue. Gdy skierowałem request na stronę zwracającą HTTP 500, Crawlee ponowił próbę, a potem wystawił błąd przez failedRequestHandler zamiast cicho go połknąć albo wywalić cały run.

To najmocniejszy argument za Crawlee w porównaniu z samodzielnym narzędziem do przeglądarki: orkiestracja crawl jest wbudowana i — co kluczowe — taka sama niezależnie od tego, czy pod spodem działa HTTP, czy przeglądarka. Logikę kolejki i podążania za linkami piszesz raz. Osobno decydujesz, czy dany crawler ma renderować JavaScript.
Instalacja i ukryte pobieranie przeglądarki
Instalacja była w większości bezbolesna, z jednym pułapkowym miejscem, które zaskoczy początkujących.
npm install crawlee playwright przeszedł czysto — system nie zgłosił żadnych podatności. Ale PlaywrightCrawler nie uruchomi się, dopóki nie wykonasz także npx playwright install chromium, które pobiera binarkę Chromium o rozmiarze około 81,7 MiB. Sam pakiet crawlee nie ściąga przeglądarki. Jeśli pominiesz ten krok i od razu spróbujesz użyć crawlera browserowego, dostaniesz błąd uruchomienia, który nie jest oczywisty, jeśli nie znasz modelu pakietowania Playwrighta. To dziedziczone zachowanie Playwrighta, nie wada Crawlee, ale realny problem przy pierwszym uruchomieniu, o którym warto wiedzieć.

Jeszcze jedna uwaga operacyjna: domyślnie Crawlee zapisuje dane do lokalnego katalogu storage/. Mój harness testowy przekierowywał to do tymczasowego katalogu scratch i wyłączał trwałość, żeby było czyściej, ale zwykłe uruchomienie zostawi w projekcie folder storage/. To nie problem — po prostu warto o tym wiedzieć, zanim pojawi się w git status.
Krótko o trzecim silniku
Historia o parzystości w Crawlee nie kończy się na Cheerio i Playwright. Jest też PuppeteerCrawler i sprawdziłem, jak daleko sięga obietnica „tego samego interfejsu” — na poziomie klas i powierzchni API, bez uruchamiania live crawl.
Wszystkie trzy klasy crawlerów wywodzą się z tej samej bazy BasicCrawler. CheerioCrawler przechodzi przez HttpCrawler, a PlaywrightCrawler i PuppeteerCrawler korzystają ze wspólnego BrowserCrawler. Po introspekcji zainstalowanego pakietu wyszło, że 24 publiczne metody są wspólne dla wszystkich trzech silników, w tym operacje na kolejce i storage, na których opiera się cała konstrukcja — run, addRequests, pushData, getData, getDataset, exportData, getRequestQueue, useState, stop. PuppeteerCrawler i PlaywrightCrawler mają wręcz identyczny zestaw metod publicznych. Różnice między silnikami pojawiają się wyłącznie na styku HTTP kontra browser — dokładnie tam, gdzie się ich spodziewasz.
Warto jednak powiedzieć to wprost: nie uruchomiłem live crawl z PuppeteerCrawler. Zależność peer puppeteer jest opcjonalna i nie była zainstalowana w moim zestawie testowym, a jej użycie oznaczałoby kolejne pobranie przeglądarki. Zatem parzystość Puppeteera została tu potwierdzona strukturalnie — ta sama klasa bazowa, te same współdzielone metody, ten sam kształt kontekstu handlera — ale nie przez wykonany test. I nawet tam, gdzie interfejs się zgadza, zachowanie pod spodem nie jest identyczne: własne wytyczne Crawlee zaznaczają, że Playwright czeka na elementy automatycznie, a Puppeteer wymaga jawnego oczekiwania. To cecha silnika, nie wada Crawlee, ale oznacza, że „to samo API” nie znaczy „ten sam kod wewnątrz każdego handlera”.
Czego nie testowałem
Oto, czego ta runda celowo nie obejmowała, żeby nie czytać wyników szerzej, niż pozwalają dane.
- Skala. Wszystko działało na małych fixture’ach i krótkich crawlach po publicznych stronach. Nie było długiego runu na 100–1000 stron, więc nie mogę powiedzieć nic pewnego o autoskalowaniu Crawlee ani o stabilności pod realnym obciążeniem.
- Trwałość kolejki i wznawianie pracy. Nie zabijałem crawl’a w połowie, żeby sprawdzić, czy
RequestQueuepotrafi bezproblemowo wznowić pracę po awarii. To kluczowa funkcja przy długich zadaniach i tutaj nie została przetestowana. - Eksport Dataset i KeyValueStore. Eksport JSON/CSV napisałem ręcznie w harnessie. Wbudowane wygody eksportu z
Dataset/KeyValueStore— być może największa ergonomiczną zaleta frameworka — nie były przeze mnie użyte. - Proxy i pule sesji. Crawlee oferuje rotację proxy i fingerprinting. Traktuję to wyłącznie jako temat zgodności i operacji, a nie jako sprzedażową „metodę obejścia botów”, i nie testowałem tego w żadną stronę.
Do tego wszystkie czasy są z jednej maszyny i jednego uruchomienia. Pokazują kształt kosztu HTTP kontra browser. To nie benchmark i nie cytowałbym ich jako benchmarku.
Zalety i wady
Zalety
- Jedno API dla crawlowania HTTP i browserowego — podmiana silnika naprawdę sprowadza się do zmiany klasy, co potwierdziłem na lokalnym fixture’ze i na publicznej stronie, przechodząc z 0 do pełnych danych.
- Prawdziwy framework crawlujący:
RequestQueue,enqueueLinksz kontrolą głębokości, retry ifailedRequestHandler, a nie tylko renderer stron. - Dokładna ekstrakcja po HTTP (12/12 statycznych, 3/3 akapity artykułu, 8/8 przez JSON API), gdy JavaScript nie stoi na przeszkodzie.
- Ścieżka browserowa odzyskuje treści, których HTTP fizycznie nie widzi, i pozwala robić screenshoty.
- Apache-2.0, TypeScript, aktywnie rozwijany projekt.
Wady
- Crawler browserowy wymaga osobnego
npx playwright install chromium(~81,7 MiB), któregonpm install crawleenie załatwia — łatwo to przeoczyć. - Renderowanie w przeglądarce niesie realny koszt per strona (~5 s vs mniej niż sekunda w moim teście na jednej stronie).
- Domyślne tworzenie katalogu
storage/przy zwykłych uruchomieniach. - Skala, wznawianie po awarii i ergonomia eksportu Dataset nie zostały potwierdzone w moich testach.
- Funkcji proxy i fingerprintingu trzeba używać zgodnie z warunkami serwisu i prawem — to odpowiedzialność, a nie „feature”, na którym warto się opierać.
Kiedy wybrać Crawlee, a kiedy API zarządzane
Crawlee to narzędzie typu „zbuduj sam”, i dla wielu zespołów to dokładnie właściwy wybór. Sięgaj po nie, gdy chcesz mieć crawlera we własnym kodzie Node, łączyć crawling HTTP i browserowy w jednym projekcie bez zmiany frameworków i samemu kontrolować kolejkę oraz storage. Jeśli nie przeszkadza Ci uruchamianie i późniejsze skalowanie floty przeglądarek, Crawlee daje Ci czysty, dobrze zaprojektowany kręgosłup, na którym można to oprzeć.
Druga ścieżka to nieuruchamianie całej tej infrastruktury. Jeśli nie chcesz zajmować się pilnowaniem instancji Chromium, rotacją proxy i obsługą anty-botów, alternatywą jest zarządzane API — i właśnie tutaj pasuje nasz własny stack developerski w Thunderbit. Dla użytkowników technicznych Thunderbit to nie rozszerzenie Chrome; to AI scraping API, serwer MCP i CLI. Wysyłasz POST /distill, aby zamienić stronę w czysty Markdown gotowy dla LLM, albo POST /extract z JSON Schema, żeby dostać z powrotem ustrukturyzowane dane, a parametr renderMode ustawiasz na none, basic lub full, żeby samemu decydować, kiedy pełny render przeglądarki naprawdę ma sens. Serwer MCP pozwala agentowi AI (Claude, Cursor i innym klientom MCP) wykonywać scraping w trakcie zadania, a CLI działa z terminala albo CI:
Wypróbuj Thunderbit do pozyskiwania danych z sieci
npx -y @thunderbit/thunderbit-cli distill "<url>" -f markdown > out.md
Różnica ważna dla developerów: Crawlee daje Ci surowy materiał — wyrenderowany HTML, sparsowane węzły — a Ty budujesz własny pipeline; zarządzane API zwraca dopasowany do schematu, ustrukturyzowany JSON, a renderowanie JS, CAPTCHA i anty-boty są obsługiwane po stronie serwera. Inne zadania. Jeśli chcesz maksymalnej kontroli i nie przeszkadza Ci warstwa operacyjna, wybierz Crawlee. Jeśli chcesz po prostu mieć dane bez utrzymywania floty przeglądarek, wybierz ścieżkę zarządzaną. Wiele zespołów kończy z używaniem obu: jednego do niestandardowych crawlów, drugiego do przypadków „po prostu daj mi te dane”. Różnicę kosztową widać też na cenniku Thunderbit.
Werdykt
Czy warto używać Crawlee? Tak — jeśli jesteś developerem Node lub TypeScript i chcesz mieć jeden framework obejmujący zarówno crawl HTTP, jak i browserowy, z prawdziwą kolejką crawl pod spodem. Obietnica dwóch silników jest głównym powodem, by po niego sięgnąć, i w moich testach zadziałała bez problemu: ten sam URL przeszedł z 0 do pełnych danych po podmianie jednej klasy, ekstrakcja statyczna była szybka i dokładna, a crawl z kolejką i głębokością działał dokładnie tak, jak opisuje dokumentacja.
Warto jednak pamiętać o dwóch rzeczach. Po pierwsze, zaplanuj miejsce na ukryte pobranie przeglądarki przy pierwszym użyciu PlaywrightCrawler. Po drugie, nie zakładaj, że elementy, których nie testowałem — skala, wznawianie po awarii, wbudowane eksporty — będą działać równie dobrze jak to, co sprawdziłem, dopóki nie uruchomisz ich na własnym workloadzie. Jako fundament do budowy własnego crawlera Crawlee jest mocnym, dobrze zaprojektowanym kawałkiem inżynierii. Jako gotowy, całkowicie bezobsługowy pipeline danych — to punkt startowy, nie cel.
Wypróbuj Thunderbit do pozyskiwania danych z sieci Get Started Free
FAQ
Czy Crawlee jest darmowe i na jakiej licencji działa?
Tak. Crawlee jest open source na licencji Apache-2.0 i instaluje się przez npm (npm install crawlee). Testowana przeze mnie wersja to 3.17.0. Uruchamianie crawlerów browserowych wymaga osobnego pobrania Chromium przez Playwrighta, które również jest darmowe, ale dodaje około 81,7 MiB do konfiguracji.
CheerioCrawler vs PlaywrightCrawler — którego użyć?
Użyj CheerioCrawler, gdy dane są w surowym HTML-u albo w ukrytym JSON API — jest dużo szybszy i nigdy nie uruchamia przeglądarki. Użyj PlaywrightCrawler, gdy treść jest renderowana przez JavaScript, co rozpoznasz po tym, że ścieżka HTTP zwraca puste wyniki. W moich testach silnik HTTP zwrócił 0 elementów na stronie renderowanej przez JS, a silnik browserowy zwrócił wszystko. Ponieważ oba korzystają z tego samego API, przełączenie to zmiana klasy, nie przepisanie kodu.
Czy Crawlee potrzebuje przeglądarki, żeby działać?
Tylko w przypadku crawlerów browserowych. CheerioCrawler nie potrzebuje żadnej przeglądarki. PlaywrightCrawler (i PuppeteerCrawler) wymagają binarki przeglądarki — zainstalujesz ją przez npx playwright install chromium. Pamiętaj, że samo npm install crawlee nie pobiera przeglądarki, co jest najczęstszą pułapką przy pierwszym uruchomieniu.
Czy Crawlee obsługuje paginację i crawl wielu stron?
Tak, i to jeden z głównych powodów, dla których warto wybrać je zamiast samodzielnej biblioteki do przeglądarki. enqueueLinks podąża za linkami, także za selektorami paginacji typu .next-page, RequestQueue deduplikuje i zarządza crawl’em, a Ty dostajesz kontrolę głębokości i limity maxRequestsPerCrawl. W testach crawl w obrębie jednej domeny przeszedł przez 11 stron na głębokościach 0–2, a błędne requesty trafiały do failedRequestHandler.
Jak Crawlee wypada na tle hostowanego API do scrapingu?
Crawlee jest self-hosted: sam piszesz i uruchamiasz crawlera, a także odpowiadasz za skalowanie, proxy i anty-boty. Zarządzane API, takie jak endpointy distill/extract Thunderbit, zwraca czysty Markdown albo ustrukturyzowany JSON zgodny ze schematem, a renderowanie i obsługa anty-botów dzieją się po stronie serwera. Dostęp jest przez API, serwer MCP i CLI. Wybierz Crawlee, jeśli chcesz maksymalnej kontroli nad własnym pipeline’em; wybierz API zarządzane, jeśli nie chcesz samodzielnie utrzymywać i skalować infrastruktury przeglądarek.


