Jak scrapuję Craigslist w Pythonie (bez bana)

Ostatnia aktualizacja: April 16, 2026
Jak scrapuję Craigslist w Pythonie (bez bana)

Craigslist nadal przyciąga około 108 milionów wizyt miesięcznie w blisko 700 lokalnych serwisach — i wciąż nie oferuje publicznego API. Jeśli potrzebujesz uporządkowanych danych z ofert mieszkań, aut używanych, ogłoszeń o pracę czy zleceń, scraping to w praktyce jedyna opcja.

Problem w tym, że Craigslist ma własny, bezlitosny system anty-botowy. To nie jest Cloudflare ani DataDome — serwis korzysta z autorskiego limitera opartego na nginx, dopracowywanego od ponad dekady. Źle trafisz i dostaniesz twarde 403, zanim zdążysz dopić pierwszą kawę. Spędziłem sporo czasu, testując różne podejścia do zabezpieczeń Craigslist, a ten poradnik jest efektem tych prac: aktualny na 2025 rok, niezależny od kategorii tutorial w Pythonie, który obejmuje metodę ekstrakcji JSON-LD (największy krok naprzód względem przestarzałych wpisów), uczciwe strategie unikania blokad, kontekst prawny oraz alternatywę no-code dla osób, które chcą tylko danych, a nie pisania kodu.

Co właściwie znaczy scrapować Craigslist w Pythonie?

Web scraping Craigslist polega na tym, że skrypty Pythona programowo odwiedzają strony Craigslist, wyciągają interesujące Cię dane strukturalne — tytuły, ceny, opisy, zdjęcia, lokalizacje, daty publikacji — i zapisują je do arkusza, bazy danych albo pliku JSON.

Python jest tu językiem pierwszego wyboru ze względu na bogaty ekosystem bibliotek. Dzięki requests, BeautifulSoup, lxml i curl_cffi możesz zbudować działający scraper Craigslist w mniej niż 100 liniach. Społeczność jest ogromna, więc kiedy Craigslist coś zmienia (a zmienia często), ktoś zwykle już znalazł rozwiązanie.

Kluczowa rzecz do zapamiętania: Craigslist nie udostępnia publicznego API do odczytu. Jedynym oficjalnym interfejsem programistycznym jest Bulk Posting Interface (BAPI), ale działa on wyłącznie do zapisu — pozwala zatwierdzonym, płacącym ogłoszeniodawcom dodawać wpisy, a nie je pobierać. Każdy produkt typu „Craigslist API” widoczny na platformach zewnętrznych to nieoficjalny scraper, a nie autoryzowany endpoint. Jeśli chcesz dane masowo, scrapujesz.

Dlaczego warto scrapować Craigslist? Przykłady z realnego świata

Craigslist to nie tylko miejsce na znalezienie używanej kanapy. To ogromny, stale aktualizowany zbiór danych obejmujący dziesiątki kategorii. Oto kto rzeczywiście korzysta na jego scrapowaniu:

Przypadek użyciaKto zyskujeJakie dane pobierasz
Monitorowanie cen mieszkań i najmuAgenci nieruchomości, najemcy, firmy PropTechCena, metraż, liczba sypialni, dzielnica, szer./dł. geograficzna
Analiza rynku aut używanychSalony samochodowe, aplikacje konsumenckie, badaczeCena, marka, model, rocznik, przebieg, stan
Badania rynku pracyRekruterzy, ekonomiści pracy, analitycy rynkuTytuł, wynagrodzenie, typ zatrudnienia, data publikacji
Generowanie leadówZespoły sprzedaży, firmy usługoweDane kontaktowe, nazwy firm, obszar działania
Analiza konkurencyjnych cenLokalni usługodawcy, e-commerce, operacje handloweCenniki usług, opisy, obsługiwane obszary

Najczęściej cytowanym przykładem akademickim jest „Used Cars Dataset” na Kaggle — około 500 000 ogłoszeń o autach używanych w USA z 26 zmiennymi, które posłużyły jako baza dla dziesiątek publikacji, w tym badania z 2024 roku na ResearchGate dotyczącego dynamiki amerykańskiego rynku aut używanych. Fundusze hedgingowe kupowały zagregowane dane o najmie z Craigslist do badań trendów czynszowych. Zespoły sprzedażowe regularnie scrapują kategorie usług i zleceń w celu pozyskiwania leadów.

Matematyka jest prosta: 8 godzin ręcznego kopiowania i wklejania kontra około 10 minut pracy dobrze napisanego scrapera.

craigslist_stats_55285c3a34.png

Scrapowanie Craigslist w Pythonie: każda kategoria, nie tylko samochody

Prawie każdy poradnik o scrapowaniu Craigslist, jaki znalazłem, dotyczy wyłącznie samochodów na sprzedaż — to tak, jakby pisać tutorial do Google, który obejmuje tylko wyszukiwanie obrazów. Craigslist ma dziesiątki kategorii, a wzorce URL różnią się między nimi.

Struktura zawsze wygląda tak: https://{city}.craigslist.org/search/{category_slug}

Zmieniasz subdomenę miasta i slug, a trafiasz w zupełnie inny segment. Oto tabela referencyjna najpopularniejszych kategorii (zweryfikowana w kwietniu 2025):

KategoriaSlug URLTypowe pola do pobrania
Mieszkania / najem/search/apaCena, metraż, sypialnie, lokalizacja, zasady dotyczące zwierząt
Samochody i ciężarówki/search/ctaCena, marka, model, rocznik, przebieg
Praca/search/jjjTytuł, firma, wynagrodzenie, typ zatrudnienia
Usługi/search/bbbTytuł, opis, numer telefonu, obszar
Zlecenia / gig/search/gggTytuł, wynagrodzenie, data, kategoria
Na sprzedaż (ogólnie)/search/sssTytuł, cena, stan, lokalizacja

Możesz też dodawać parametry zapytań do filtrowania:

ParametrZastosowaniePrzykład
querySłowo kluczowe full-text?query=studio
min_price / max_priceZakres cen&min_price=1500&max_price=3000
hasPicTylko ogłoszenia ze zdjęciami&hasPic=1
postedTodayOstatnie 24 godziny&postedToday=1
sortSortowanie&sort=priceasc
sOffset paginacji (120 na stronę)?s=120

Zatem adres taki jak https://newyork.craigslist.org/search/apa?min_price=1500&max_price=3000&hasPic=1 zwróci mieszkania w Nowym Jorku w cenie od 1 500 do 3 000 dolarów, ze zdjęciami. Każdy scraper w Pythonie z tego poradnika działa we wszystkich tych kategoriach — wystarczy podmienić slug.

Selektory HTML Craigslist w 2025: stare vs nowe (i skrót przez JSON)

Najczęstszym powodem, dla którego scrapery Craigslist przestają działać, są zmiany w strukturze HTML. Jeśli korzystasz z poradnika z 2022 roku, który każe targetować .result-row albo .result-info, Twój scraper jest już martwy.

Craigslist przepisał markup wyników wyszukiwania w latach 2023–2024. Stare klasy nadal występują wewnątrz nowych kontenerów, ale ich użycie na najwyższym poziomie DOM zwraca pustą listę. Oto co się zmieniło:

ElementStary selektor (przed 2024)Aktualny selektor (2025)
Kontener ogłoszenia.result-info.cl-search-result
Link tytułu.result-title.posting-title a
Cena.result-price.priceinfo
Metadane (obszar).result-hood.meta

Ale jest tu ważniejszy wniosek — i to on odróżnia aktualny scraper z 2025 roku od reszty: dla wyników wyszukiwania w ogóle nie musisz parsować HTML.

Craigslist osadza teraz każde widoczne ogłoszenie w tagu <script id="ld_searchpage_results"> jako ustrukturyzowane dane JSON-LD. Jedno wywołanie requests.get() zwraca pełny schemat ItemList ze schema.org dla wszystkich ogłoszeń na stronie — tytuł, cena, waluta, lokalizacja, URL obrazka, link do strony szczegółów. Bez renderowania JavaScriptu. Bez kruchej zależności od selektorów CSS.

Podejście JSON-LD jest szybsze, stabilniejsze i znacznie mniej podatne na awarie, gdy Craigslist zmienia interfejs. To rozwiązanie stosują aktywnie utrzymywane repozytoria na GitHubie i właśnie ono posłuży nam w dalszej części.

Jedno zastrzeżenie: blok JSON-LD jest dostępny dla kategorii z ceną — mieszkania (apa), oferty na sprzedaż (sss), auta (cta), housing (hhh). Często go nie ma albo jest ubogi dla pracy (jjj), gigów (ggg), community (ccc) i usług (bbb), ponieważ te ogłoszenia nie mają cen w schema.org/Offer. W takich przypadkach wracaj do ścieżki HTML .cl-search-result.

Wybór stacku Pythona: Requests + BS4 vs Selenium vs Playwright

To pytanie pojawia się na każdym forum o scrapingu: „Z jakiej biblioteki powinienem skorzystać?”. W przypadku Craigslist odpowiedź jest prostsza niż w większości serwisów.

Czynnikrequests + BeautifulSoupSeleniumPlaywright
Szybkość5–15 stron/s (ograniczenie sieciowe)0,3–1 strona/s0,5–2 strony/s
Treść renderowana przez JSNieTakTak
Pamięć~30–60 MB~400–700 MB~300–500 MB
Złożoność konfiguracjiNiskaŚredniaŚrednia
Odporność na anty-botaNiska (wymaga nagłówków/proxy)Średnia (prawdziwa przeglądarka)Średnio-wysoka
Najlepszy przypadek użycia na CraigslistWyniki wyszukiwania (JSON-LD)Strony szczegółów z dynamiczną treściąDuże, asynchroniczne scrapowanie
Krzywa naukiPrzyjazna dla początkującychUmiarkowanaUmiarkowana

Strony Craigslist są renderowane po stronie serwera. Blok JSON-LD znajduje się w początkowym HTML. Na ścieżkach odczytu nie ma żadnych wyzwań JavaScriptowych. Każdy aktywnie utrzymywany scraper Craigslist na GitHubie używa requests + BeautifulSoup albo Scrapy. Zero Selenium. Zero Playwrighta. To nie przypadek — framework do automatyzacji przeglądarki dodaje setki MB pamięci, spowalnia operacje 10–100 razy i tworzy bardziej widoczny fingerprint, nie dając w zamian żadnej korzyści.

Moja rekomendacja:

  • requests + BS4: zacznij tutaj. Idealnie współgra z ekstrakcją JSON-LD i pokrywa 95% potrzeb scrapingu Craigslist.
  • Selenium: tylko jeśli musisz wchodzić w interakcję z dynamiczną treścią na konkretnych stronach szczegółów (na Craigslist rzadko konieczne).
  • Playwright: jeśli skalujesz do tysięcy stron z asynchroniczną współbieżnością — ale szczerze, w Craigslist wąskim gardłem jest limiter, a nie przepustowość biblioteki.

Osobno omówiliśmy porównanie Playwright vs. Puppeteer oraz zestawienie najlepszych narzędzi Pythona do web scrapingu, jeśli chcesz pełniejszego obrazu.

Scrapuj Craigslist bez pisania Pythona Get Started Free

Alternatywa no-code: scrapuj Craigslist bez pisania Pythona

Krótki przystanek przed kodem — ta sekcja jest dla osób, które nie są programistami. Agenci nieruchomości, zespoły sprzedaży, menedżerowie operacyjni — jeśli po prostu chcesz dane i nie zależy Ci na pisaniu Pythona, istnieje szybsza droga.

Thunderbit to AI web scraper działający jako rozszerzenie Chrome. Potrafi scrapować Craigslist w około 2 kliknięcia, bez kodu. Oto jak wygląda proces:

  1. Wejdź na dowolną stronę wyników wyszukiwania Craigslist (mieszkania, auta, praca — dowolna kategoria).
  2. Kliknij „AI Suggest Fields” w panelu Thunderbit. AI odczytuje stronę i automatycznie wykrywa kolumny, takie jak tytuł ogłoszenia, cena, lokalizacja i link.
  3. Kliknij „Scrape” — dane zostaną pobrane w kilka sekund.
  4. Użyj Subpage Scraping, aby odwiedzić stronę szczegółów każdego ogłoszenia i wzbogacić dane o pełne opisy, numery telefonów, zdjęcia i atrybuty.
  5. Eksportuj bezpośrednio do Google Sheets, Excel, Airtable lub Notion — całkowicie za darmo.

Dla powtarzalnych zadań — na przykład codziennego monitorowania cen mieszkań albo cotygodniowych zrzutów ofert pracy — Scheduled Scraper w Thunderbit pozwala opisać harmonogram zwykłym językiem i uruchamia wszystko automatycznie. Bez cronów, bez konfiguracji serwera.

Thunderbit obsługuje też mechanizmy anty-botowe przez tryb Cloud Scraping, więc nie musisz martwić się o rotację proxy ani ręczne tworzenie nagłówków. Jeśli chcesz przetestować narzędzie, pobierz rozszerzenie Thunderbit do Chrome i sprawdź sam.

Jeśli zależy Ci na pełnej kontroli i personalizacji, czytaj dalej — poniżej znajdziesz instrukcję krok po kroku w Pythonie.

Krok po kroku: jak scrapować Craigslist w Pythonie (pełny poradnik)

  • Poziom trudności: średni
  • Szacowany czas: ~30 minut (konfiguracja + pierwszy scraping)
  • Czego potrzebujesz: Python 3.8+, przeglądarka Chrome (do podglądu stron), terminal

Krok 1: przygotuj środowisko Pythona

Zainstaluj wymagane biblioteki:

pip install requests beautifulsoup4 lxml

lxml jest opcjonalne, ale zauważalnie przyspiesza parsowanie BeautifulSoup. Jeśli później trafisz na problemy z fingerprintem TLS (więcej o tym w sekcji o blokadach), możesz też doinstalować curl_cffi:

pip install curl_cffi

Twój blok importów:

import requests
from bs4 import BeautifulSoup
import json
import csv
import time
import random

Masz teraz czyste środowisko Pythona ze wszystkimi zależnościami.

Krok 2: zbuduj URL Craigslist dla dowolnej kategorii

Twórz adres docelowy dynamicznie, używając miasta, slugu kategorii i opcjonalnych filtrów:

from urllib.parse import urlencode

BASE = "https://{city}.craigslist.org/search/{slug}"

def build_url(city, slug, **params):
    return f"{BASE.format(city=city, slug=slug)}?{urlencode(params)}"

# Przykład: mieszkania w Nowym Jorku, 1500–3000 USD, ze zdjęciami
url = build_url("newyork", "apa", min_price=1500, max_price=3000, hasPic=1)
print(url)
# https://newyork.craigslist.org/search/apa?min_price=1500&max_price=3000&hasPic=1

Zamień "apa" na "cta" (auta), "jjj" (praca), "bbb" (usługi) albo dowolny slug z tabeli kategorii wyżej. Zamień "newyork" na "sfbay", "chicago", "losangeles" itd.

Krok 3: pobierz stronę i wyodrębnij osadzony JSON

Wyślij żądanie GET z odpowiednimi nagłówkami, a potem sparsuj blok JSON-LD:

HEADERS = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/124.0.0.0 Safari/537.36"
    ),
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "Referer": "https://www.craigslist.org/",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "same-origin",
    "Upgrade-Insecure-Requests": "1",
}

session = requests.Session()
r = session.get(url, headers=HEADERS, timeout=20)
r.raise_for_status()

soup = BeautifulSoup(r.text, "html.parser")
tag = soup.select_one("script#ld_searchpage_results")
data = json.loads(tag.text) if tag else {"itemListElement": []}

Jeśli tag ma wartość None, blok JSON-LD nie występuje dla danej kategorii — wtedy wróć do parsowania HTML (patrz tabela selektorów wyżej). Dla mieszkań, aut i kategorii sprzedażowych blok JSON-LD jest zazwyczaj obecny.

Krok 4: zamień ogłoszenia na ustrukturyzowane rekordy

Przejdź po elementach JSON i pobierz potrzebne pola:

listings = []
for entry in data["itemListElement"]:
    item = entry["item"]
    offers = item.get("offers", {}) or {}
    addr = (offers.get("availableAtOrFrom") or {}).get("address", {})
    listings.append({
        "name":     item.get("name"),
        "url":      offers.get("url"),
        "price":    offers.get("price"),
        "currency": offers.get("priceCurrency"),
        "locality": addr.get("addressLocality"),
        "region":   addr.get("addressRegion"),
        "image":    item.get("image"),
    })

print(f"Znaleziono {len(listings)} ogłoszeń")

Powinieneś zobaczyć coś w stylu „Znaleziono 120 ogłoszeń” (Craigslist pokazuje 120 wyników na stronę). Część ogłoszeń może mieć None w polu ceny, jeśli autor jej nie podał — uwzględnij to w dalszej logice.

Krok 5: pobierz strony szczegółów, żeby uzyskać bogatsze dane

Wyniki wyszukiwania dają tylko podsumowanie. Pełne opisy, atrybuty (liczba sypialni, metraż, zasady dla zwierząt), współrzędne oraz zdjęcia znajdziesz dopiero na stronie szczegółów każdej oferty.

def fetch_detail(url, session):
    r = session.get(url, headers=HEADERS, timeout=20)
    r.raise_for_status()
    s = BeautifulSoup(r.text, "html.parser")
    body = s.select_one("#postingbody")
    mp = s.select_one("#map")
    return {
        "description": body.get_text("\n", strip=True) if body else None,
        "attributes":  [x.get_text(" ", strip=True) 
                        for x in s.select("p.attrgroup span, div.attrgroup .attr")],
        "lat": mp.get("data-latitude") if mp else None,
        "lng": mp.get("data-longitude") if mp else None,
        "images": [img["src"] for img in s.select("div.gallery img")],
    }

for item in listings:
    item.update(fetch_detail(item["url"], session))
    time.sleep(random.uniform(3, 6))  # kluczowe: losowy odstęp anty-botowy

time.sleep(random.uniform(3, 6)) nie jest opcjonalne. Pomiń to, a w ciągu kilkudziesięciu requestów dostaniesz 403. Strony szczegółów są renderowane po stronie serwera i mają stabilne selektory (#titletextonly, #postingbody, #map), które nie zmieniły się mniej więcej od 2017 roku — czyli należą do nielicznych rzeczy na Craigslist, na których można polegać.

Krok 6: obsłuż paginację i pobierz wszystkie wyniki

Craigslist używa parametru offsetu ?s=120 do paginacji. Każda strona pokazuje 120 wyników, a maksymalny offset to zwykle 2999.

def iter_all(city, slug, max_pages=25, **filters):
    for page in range(max_pages):
        offset = page * 120
        url = build_url(city, slug, s=offset, **filters)
        r = session.get(url, headers=HEADERS, timeout=20)
        r.raise_for_status()
        soup = BeautifulSoup(r.text, "html.parser")
        tag = soup.select_one("script#ld_searchpage_results")
        if not tag:
            break
        data = json.loads(tag.text)
        items = data.get("itemListElement", [])
        if not items:
            break
        for entry in items:
            item = entry["item"]
            offers = item.get("offers", {}) or {}
            yield {
                "name": item.get("name"),
                "url": offers.get("url"),
                "price": offers.get("price"),
            }
        time.sleep(random.uniform(2.5, 5.0))

Nie próbuj scrapować tysięcy stron jedna po drugiej. Limit Craigslist działa per IP, a zrównoważona przepustowość dla pojedynczego adresu IP zwykle kończy się w okolicach 0,3–0,5 requestu na sekundę, niezależnie od biblioteki. Ten limit ustala Craigslist, nie Python.

Krok 7: eksportuj dane Craigslist do CSV, JSON lub Google Sheets

Zapisz wyniki:

# CSV
with open("craigslist.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.DictWriter(f, fieldnames=listings[0].keys())
    w.writeheader()
    w.writerows(listings)

# JSON
with open("craigslist.json", "w", encoding="utf-8") as f:
    json.dump(listings, f, indent=2, ensure_ascii=False)

Jeśli wolisz w ogóle pominąć kod eksportu, Thunderbit oferuje darmowy eksport bezpośrednio z przeglądarki do Google Sheets, Excel, Airtable lub Notion. Ale w pipeline’ach Pythona standardem pozostają CSV i JSON. Możesz też przepuścić dane bezpośrednio do pandas do analizy albo do bazy danych z sqlite3.

Jak uniknąć bana podczas scrapowania Craigslist w Pythonie

Większość poradników traktuje ten temat bardzo powierzchownie. System anty-botowy Craigslist jest autorski, a nie gotowy „z półki”, i ma kilka specyficznych pułapek.

craigslist_antibot_ae9ddc3f54.png

Używaj realistycznych nagłówków requestów

Craigslist weryfikuje kolejność i kompletność nagłówków. Żądanie bez Sec-Fetch-Dest albo ze starym User-Agentem zostanie oznaczone zanim dotrze do treści. Pełny zestaw nagłówków z Chrome 120+ (pokazany w kroku 3) to minimum. Rotuj User-Agent na poziomie sesji pomiędzy 5–10 aktualnymi desktopowymi stringami Chrome/Firefox — ale nie zmieniaj go w trakcie jednej sesji, bo wygląda to nienaturalnie.

Brak nagłówków Sec-Fetch-* to najczęstszy powód natychmiastowych blokad u początkujących scraperów.

Dodawaj losowe przerwy między requestami

Konsensus społeczności z wielu źródeł (ScrapingBee, Scraperly, Oxylabs, Multilogin) wskazuje na losowe 2–5 sekund między pobieraniem stron wyników oraz 3–6 sekund między stronami szczegółów. Stałe odstępy wyglądają jak ruch bota. Używaj time.sleep(random.uniform(2, 5)) — nigdy time.sleep(2).

Rotuj proxy, jeśli scrapujesz na dużą skalę

Craigslist blokuje całe zakresy IP z AWS, GCP i Azure. Proxy z centrów danych często padają od razu. Przy wolumenie większym niż kilkaset stron potrzebujesz rotujących proxy residential, zmienianych co 20–30 requestów. Proxy mobilne mają najniższe ryzyko wykrycia, ale kosztują 8–30 USD/GB.

Typ proxyRyzyko wykrycia na CraigslistKoszt (2025)
DatacenterBardzo wysokie — często blokowane przy pierwszym requestie$0.50–2/GB
Residential rotatingNiskie — rekomendowane$5–15/GB
MobileNajniższe$8–30/GB

Tryb Cloud Scraping w Thunderbit automatycznie obsługuje rotację proxy, jeśli nie chcesz robić tego samodzielnie.

Obsługuj CAPTCHA z głową

CAPTCHA na Craigslist są rzadkie na ścieżkach odczytu — zwykle pojawiają się przy publikowaniu albo odpowiadaniu na ogłoszenia. Jeśli się pojawią: odczekaj co najmniej 60 sekund, zmień IP, wyczyść ciasteczka i zwolnij tempo. Uporczywe CAPTCHA to znak, że jedziesz za agresywnie, a nie łamigłówka do rozwiązywania siłą.

Szanuj limity i stosuj backoff

Craigslist zwraca 403, a nie 429, gdy przekroczysz limit. 403 oznacza, że bieżące IP trafiło na listę blokad — nie powtarzaj ślepo zapytania. Zmień IP, User-Agent i poczekaj.

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

retry = Retry(
    total=5,
    backoff_factor=1.5,  # 1.5, 3, 6, 12, 24 s
    status_forcelist=[429, 500, 502, 503, 504],
    allowed_methods={"GET"},
    respect_retry_after_header=True,
)
adapter = HTTPAdapter(max_retries=retry)
session.mount("https://", adapter)

Jeszcze jedna wskazówka: raporty społeczności konsekwentnie wskazują, że 2:00–6:00 rano czasu lokalnego docelowego miasta to najbezpieczniejsze okno scrapowania, z około 30–40% mniejszym odsetkiem blokad niż w ciągu dnia.

Fingerprinting TLS — ukryta pułapka

Warstwa botowa Craigslist analizuje TLS ClientHello. Biblioteka requests w Pythonie (oparta na OpenSSL) ma fingerprint JA3, który nie pasuje do żadnej prawdziwej przeglądarki. Idealny User-Agent połączony z niefizycznym fingerprintem TLS to łatwo wykrywalna niespójność. Obejściem jest curl_cffi z impersonate="chrome124", które emuluje handshake TLS Chrome:

from curl_cffi import requests as cffi_requests

r = cffi_requests.get(url, headers=HEADERS, impersonate="chrome124")

Jeśli dostajesz niewyjaśnione 403 mimo czystych residential IP i poprawnych nagłówków, fingerprint TLS jest niemal na pewno przyczyną.

robots.txt Craigslist, warunki korzystania i etyczny scraping

Większość poradników pomija ten temat albo wrzuca jedno zdanie do FAQ. Biorąc pod uwagę, że Craigslist wygrał wyrok na 60,5 mln USD przeciwko scraperowi (RadPad, 2017), temat zasługuje na coś więcej niż przypis.

Co tak naprawdę mówi robots.txt Craigslist?

Plik robots.txt jest zaskakująco krótki. Zawiera jeden blok User-agent: * i tylko siedem wykluczonych ścieżek:

Disallow: /reply
Disallow: /fb/
Disallow: /suggest
Disallow: /flag
Disallow: /mf
Disallow: /mailflag
Disallow: /eaf

Wszystkie siedem to interaktywne lub modyfikujące endpointy: odpowiedzi, zgłaszanie, sugestie, wysłanie maila do znajomego. Strony ogłoszeń (/search/..., pojedyncze URL-e postów) nie są wykluczone. Nie ma też dyrektywy Crawl-delay, choć Craigslist i tak egzekwuje ją przez blokady IP.

Subdomeny miast publikują sitemapę — np. https://newyork.craigslist.org/sitemap/index.xml — czyli oficjalnie dostępny punkt wejścia do ogłoszeń.

Precedens prawny: sprawy, które mają znaczenie

Craigslist v. 3Taps (2013, ugoda 2015): 3Taps scrapował ogłoszenia Craigslist i odsprzedawał je dalej. Gdy Craigslist wysłał wezwanie do zaprzestania i zablokował ich IP, 3Taps obejść blokadę przez rotujące proxy. Sąd uznał, że obchodzenie blokad IP po jednoznacznym cofnięciu zgody stanowi działanie „without authorization” w rozumieniu CFAA. 3Taps zawarł ugodę na 1 mln USD.

Meta v. Bright Data (2024): Nowszy wyrok stwierdził, że warunki korzystania Meta nie mogą zakazywać scrapowania publicznie dostępnych danych przez wylogowanego użytkownika. Sąd uznał, że scraper bez logowania jest „na miejscu zwykłego odwiedzającego”. To najważniejsze rozstrzygnięcie dla scraperów w latach 2024–2025 — jeśli nigdy nie zakładasz konta Craigslist, nigdy się nie logujesz i korzystasz wyłącznie z publicznie widocznych stron, warunki korzystania mogą nie być wobec Ciebie egzekwowalne jako umowa.

Praktyczny wniosek: ryzyko CFAA jest znacznie mniejsze po Van Buren (2021) i hiQ v. LinkedIn (2022) w przypadku stron publicznie dostępnych. Ale roszczenia z prawa stanowego — takie jak trespass-to-chattels czy misappropriation — nadal istnieją. To właśnie one doprowadziły zarówno do ugody 3Taps, jak i do wyroku na 60,5 mln USD wobec RadPad.

To materiał informacyjny, nie porada prawna. Jeśli planujesz komercyjnie scrapować Craigslist, porozmawiaj z prawnikiem.

Praktyczna checklista etycznego scrapingu

  • ✅ Respektuj każdy Disallow w robots.txt — szczególnie siedem endpointów akcji
  • ✅ Trzymaj się wyraźnie poniżej 1 000 stron na 24 godziny na jedno IP (warunki Craigslist przypisują powyżej tego progu 0,25 USD za stronę jako z góry określone odszkodowanie)
  • ✅ Pozostań wylogowany — nigdy nie twórz konta Craigslist wyłącznie do scrapowania
  • ✅ Nigdy nie obchodź blokad IP przez proxy po eksplicytnym banie (to pogrążyło 3Taps)
  • ✅ Dodawaj opóźnienia między requestami — minimum 2–5 sekund
  • ✅ Nie scrapuj danych kontaktowych do spamu
  • ✅ Nie redystrybuuj surowych danych Craigslist ani nie przedstawiaj ich jako własnej platformy
  • ✅ Używaj danych wyłącznie do legalnych badań, analiz lub użytku osobistego
  • ✅ Jeśli to możliwe, preferuj opublikowane sitemap y zamiast brutalnego crawl'owania
  • ✅ Usuwaj dane osobowe (PII), takie jak e-maile i numery telefonów, na etapie ingestii, jeśli je przechowujesz

Przygotowaliśmy też bardziej szczegółowy przewodnik o prawnych konsekwencjach web scrapingu, jeśli chcesz pełniejszy obraz.

Python vs no-code: które podejście będzie dla Ciebie lepsze?

CzynnikPython (requests + BS4)Thunderbit (no-code)
Czas konfiguracji30–60 min (instalacja, kod)2 minuty (instalacja rozszerzenia Chrome)
Wymagana wiedza technicznaPython na poziomie średnimBrak
PersonalizacjaPełna kontrola nad logiką, polami i przepływemAI automatycznie wykrywa pola; użytkownik może je dopasować
SkalaPraktycznie nieograniczona (z proxy i harmonogramem)Scheduled Scraper do zadań cyklicznych
Obsługa blokadRęczna (nagłówki, opóźnienia, proxy, TLS)Wbudowana (Cloud Scraping)
Opcje eksportuCSV, JSON (piszesz sam)Google Sheets, Excel, Airtable, Notion — za darmo
Najlepsze dlaProgramistów, data scientistów, niestandardowych pipeline’ówZespołów sprzedaży, agentów nieruchomości, menedżerów operacyjnych

Użyj Pythona, jeśli potrzebujesz pełnej personalizacji, planujesz integrację z większym pipeline’em danych albo chcesz dokładnie rozumieć, co dzieje się pod maską. Wybierz Thunderbit, jeśli zależy Ci na szybkich wynikach bez pisania i utrzymywania kodu. Oba podejścia są dobre — wszystko sprowadza się do Twojego scenariusza i do tego, czy wolisz spędzać czas w terminalu, czy w przeglądarce.

Podsumowanie

Craigslist to bogate, stale aktualizowane źródło danych o mieszkaniach, autach, pracy, usługach, zleceniach i wielu innych obszarach — a ponieważ nie ma publicznego API, scraping jest jedynym sposobem na pozyskanie danych strukturalnych na dużą skalę. Podejście, które działa w 2025 roku: wyciągaj osadzony JSON-LD z wyników wyszukiwania (zamiast kruchych selektorów CSS), używaj requests + BeautifulSoup (zamiast Selenium), dodawaj realistyczne nagłówki z polami Sec-Fetch-*, losuj opóźnienia i korzystaj z residential proxy, jeśli idziesz poza kilkaset stron.

Metoda JSON-LD to największa poprawa względem przestarzałych poradników. Jest szybsza, bardziej odporna na zmiany layoutu i nie wymaga żadnego renderowania JavaScriptu. Połącz ją z powyższymi strategiami unikania blokad, a ominiesz 403, które zatrzymują większość scraperów.

Jeśli wolisz całkowicie pominąć kod, rozszerzenie Thunderbit do Chrome może scrapować dowolną kategorię Craigslist w kilka kliknięć i eksportować dane bezpośrednio do preferowanego arkusza lub bazy danych. Jeśli chcesz wejść głębiej, nasze poradniki o web scrapingu w Pythonie i najlepszych praktykach web scrapingu omawiają fundamenty bardziej szczegółowo.

Wypróbuj Thunderbit do scrapowania Craigslist

Wypróbuj AI Web Scraper do danych z Craigslist Get Started Free

FAQ

Czy scrapowanie Craigslist jest legalne?

Warunki korzystania Craigslist zabraniają automatycznego scrapowania i zawierają klauzulę odszkodowawczą (0,25 USD za stronę powyżej 1 000 dziennie). Jednak nowsze orzeczenia sądowe — szczególnie Meta v. Bright Data (2024) i hiQ v. LinkedIn (2022) — ograniczyły odpowiedzialność CFAA w przypadku scrapowania publicznie dostępnych danych przez wylogowanego użytkownika. Roszczenia na gruncie prawa stanowego (trespass-to-chattels) nadal stanowią ryzyko, zwłaszcza przy komercyjnej redystrybucji. Respektuj robots.txt, pozostań wylogowany, dodawaj opóźnienia i nie rozpowszechniaj surowych danych. To ogólna informacja, nie porada prawna.

Czy Craigslist ma publiczne API?

Nie. Craigslist udostępnia wyłącznie interfejs Bulk Posting Interface (BAPI) do zapisu dla zatwierdzonych płacących ogłoszeniodawców. Nie ma publicznego API do odczytu, portalu deweloperskiego ani limitowanej warstwy do pobierania danych. Każdy produkt „Craigslist API” widoczny na platformach zewnętrznych to nieoficjalny scraper.

Dlaczego mój scraper Craigslist ciągle się psuje?

Prawie zawsze przez zmiany w HTML. Craigslist przepisał markup wyników wyszukiwania w latach 2023–2024, a poradniki używające starych selektorów, takich jak .result-row czy .result-info, nie działają. Przejdź na metodę osadzonego JSON-LD (parsowanie script#ld_searchpage_results), która jest dużo bardziej odporna. Sprawdź też, czy nagłówki zawierają pola Sec-Fetch-* — ich brak powoduje natychmiastowe blokady.

Czy mogę scrapować Craigslist bez Pythona?

Tak. Rozszerzenie Chrome Thunderbit AI web scraper działa na każdej stronie Craigslist — mieszkaniach, autach, pracy, usługach. Kliknij „AI Suggest Fields”, aby automatycznie wykryć kolumny, kliknij „Scrape”, aby pobrać dane, a następnie wyeksportuj je za darmo do Google Sheets, Excel, Airtable lub Notion. Bez kodu, bez konfiguracji, bez zarządzania proxy.

Jak często mogę scrapować Craigslist, żeby nie dostać bana?

Przy pojedynczym residential IP zrównoważona przepustowość to około 0,3–0,5 requestu na sekundę, przy losowych przerwach 2–5 sekund między stronami. Nie przekraczaj 1 000 stron na 24 godziny na jedno IP, aby uniknąć zarówno banów, jak i progu odszkodowań zapisanych w warunkach Craigslist. Scrapowanie poza godzinami szczytu (2:00–6:00 rano czasu lokalnego docelowego miasta) obniża odsetek blokad o około 30–40%. Przy większych wolumenach rotuj residential proxy co 20–30 requestów.

Dowiedz się więcej

Ke
Ke
CTO w Thunderbit | Starszy data scientist i ekspert ML Dzięki prawie dziesięciu latom doświadczenia w uczeniu maszynowym i data science, Ke Shen jest absolwentem Columbia University i byłym starszym data scientistą w Walmart Labs. Dysponując dogłębną, uznaną przez branżowych ekspertów wiedzą w zakresie Python, R, Java i statystyki, dzieli się sprawdzonymi w boju spostrzeżeniami na temat wdrażania złożonych algorytmów AI — od teorii po architekturę gotową do produkcji.
Spis treści

Zbierz dane ze strony, po prostu o to prosząc

Powiedz, czego potrzebujesz, prostym angielskim. A najlepiej: nie mów nic.

Wypróbuj Thunderbit darmowe
Wyodrębniaj dane dzięki AI
Łatwo przenoś dane do Google Sheets, Airtable lub Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week