got-scraping w Node.js: Kompletny przewodnik po scrapingu z proxy rezydencjalnymi

Praktyczny przewodnik dla programistów Node.js: jak używać got-scraping z generatorami nagłówków, rotacją proxy rezydencjalnych i wzorcami produkcyjnymi. Z przykładami kodu i integracją ProxyHat.

got-scraping in Node.js: A Developer's Guide to Headers, HTTP/2 & Residential Proxies

Dlaczego zwykłe żądania got/axios zostają zablokowane i jak got-scraping w Node.js rozwiązuje ten problem

Kiedy wysyłasz żądanie HTTP przez got lub axios, Twój kod pozostawia ślady, których przeglądarka nigdy by nie zostawiła. Nagłówki są niekompletne, kolejność nietypowa, brakuje sec-ch-ua, a odcisk TLS (TLS fingerprint) pasuje do Node.js, a nie do Chrome. Nowoczesne systemy anti-bot — Cloudflare, Datadome, PerimeterX, Akamai Bot Manager — porównują te sygnały i blokują żądania, które wyglądają niespójnie. got-scraping w Node.js to biblioteka od Apify, która rozwiązuje ten problem u źródła: generuje spójne zestawy nagłówków przeglądarki i obsługuje HTTP/2, dzięki czemu Twój ruch wygląda jak prawdziwy.

Różnica jest drastyczna. W testach przeprowadzonych przez twórców got-scraping na stronach chronionych Cloudflare, zwykłe żądania got otrzymują 403 w ponad 80% przypadków, podczas gdy got-scraping z odpowiednimi nagłówkami i proxy rezydencjalnym osiąga współczynnik sukcesu powyżej 90%. Kluczem nie jest jeden trik — to kombinacja spójnych nagłówków, HTTP/2 i adresów IP, które nie figurują na listach bloków datacenter.

Kontekst techniczny: dlaczego nagłówki i TLS to nie wszystko

Nawet jeśli skopiujesz nagłówki z Chrome, kolejność ma znaczenie. Przeglądarki wysyłają nagłówki w określonej kolejności, a serwery anti-bot sprawdzają, czy Accept pojawia się przed Accept-Encoding, czy sec-ch-ua jest obecny i spójny z User-Agent. Biblioteka header-generator — używana przez got-scraping — emituje nagłówki, które są wewnętrznie spójne: jeśli User-Agent mówi Chrome 120 na Windows, to sec-ch-ua i sec-ch-ua-platform mówią to samo. To eliminuje najczęstszą przyczynę blokad na poziomie nagłówków.

Drugi filar to HTTP/2. Cloudflare i inne CDN-y negocjują ALPN podczas uzgadniania TLS. Jeśli Twój klient mówi http/1.1 w ALPN, a nagłówek User-Agent twierdzi, że to Chrome — to niespójność. got-scraping obsługuje HTTP/2 natywnie, co eliminuje ten sygnał. Trzeci filar to adres IP: nawet z idealnymi nagłówkami i TLS, adres z blokowanego zakresu datacenter (np. AWS, DigitalOcean) zostanie odrzucony. Tu wchodzi got-scraping proxy — routing przez adresy rezydencjalne.

Idiomatyczne API got-scraping: got.extend, useHeaderGenerator i proxyUrl

got-scraping jest rozszerzeniem got — nie zastępuje go, lecz dodaje warstwę. Najważniejsze elementy powierzchni API to:

  • got.extend() — tworzy instancję z prekonfigurowanymi opcjami, w tym hookami na nagłówki i proxy.
  • useHeaderGenerator — flaga włączająca automatyczne generowanie nagłówków przeglądarki dla każdego żądania.
  • headerGeneratorOptions — obiekt konfiguracji generatora: browsers, devices, operatingSystems, locales.
  • proxyUrl — URL proxy obsługiwany zarówno dla HTTP/1.1, jak i HTTP/2.

Idiomatyczne użycie wygląda tak: tworzysz jedną instancję got.extend z włączonym generatorem nagłówków i opcjami proxy, a następnie wywołujesz ją w pętli. Nie powinieneś ręcznie ustawiać User-Agent — generator nadpisze go i to prawidłowe zachowanie.

import { gotScraping } from 'got-scraping';

const got = gotScraping.extend({
  useHeaderGenerator: true,
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['windows', 'macos'],
    locales: ['en-US', 'en'],
  },
  proxyUrl: 'http://user-country-US:pass@gate.proxyhat.com:8080',
  timeout: { request: 15000 },
});

const res = await got('https://example.com');
console.log(res.statusCode, res.body.slice(0, 200));

Konfiguracja headerGeneratorOptions jest krytyczna. Jeśli ustawisz browsers: ['chrome'] i operatingSystems: ['windows'], każdy nagłówek będzie spójny z tą kombinacją. Jeśli dodasz 'safari' do browsers i 'macos' do operatingSystems, generator losuje spójne kombinacje. Nie mieszaj Chrome z operatingSystems: ['ios'] — to niefizyczna kombinacja, którą niektóre systemy anti-bot wykryją.

Opcja proxyUrl i obsługa HTTP/2

Opcja proxyUrl w got-scraping obsługuje proxy HTTP i HTTPS. Dla proxy HTTP (CONNECT tunnel) got-scraping tuneluje żądanie, zachowując HTTP/2 end-to-end, gdy serwer docelowy je obsługuje. To ważne: niektóre biblioteki spadają do HTTP/1.1 po włączeniu proxy. got-scraping tego nie robi — zachowuje ALPN i HTTP/2, co utrzymuje spójność z odciskiem TLS przeglądarki.

Routing przez proxy rezydencjalne: gate.proxyhat.com:8080

Gdy nagłówki i TLS wyglądają prawdziwie, ostatnim sygnałem sprawdzanym przez anti-bot jest adres IP. Adresy z zakresów datacenter (AWS, Google Cloud, Azure) są na listach bloków większości systemów anti-bot. Adresy rezydencjalne — przypisane ISP-om konsumentom — nie są na tych listach, bo pochodzą od dostawców internetu dla gospodarstw domowych.

got-scraping proxy przez ProxyHat jest prosty: ustawiasz proxyUrl na http://USERNAME:PASSWORD@gate.proxyhat.com:8080. Nazwa użytkownika może zawierać flagi geo-targetingu i sesji:

  • user-country-US — adres IP z USA.
  • user-country-DE-city-berlin — adres IP z Berlina.
  • user-session-abc123 — sticky session: ten sam adres IP dla wielu żądań.

Dla SOCKS5 używasz portu 1080: socks5://USERNAME:PASSWORD@gate.proxyhat.com:1080. SOCKS5 jest przydatny, gdy docelowy serwer wymaga tunelowania protokołów, które HTTP proxy obsługuje gorzej, lub gdy chcesz uniknąć dodatkowego nagłówka Via, który niektóre proxy HTTP wstrzykują.

Pełną listę lokalizacji znajdziesz na stronie lokalizacji ProxyHat, a szczegóły cennika na stronie cen.

Przykład w Node.js: rotacja proxy, retry hooks i sesje

Poniżej kompletny, uruchamialny przykład. Kod tworzy instancję got-scraping z generatorem nagłówków, rotuje sesje proxy dla każdego żądania i implementuje hook retry dla statusów 429 i 403. Używa ProxyHat z geo-targetingiem US i sticky sessions.

import { gotScraping } from 'got-scraping';
import { randomUUID } from 'crypto';
import pLimit from 'p-limit';

const PROXYHAT_GATE = 'gate.proxyhat.com';
const PROXYHAT_PORT = 8080;
const PROXYHAT_USER = 'twoj-user';
const PROXYHAT_PASS = 'twoje-haslo';

function buildProxyUrl(country = 'US', session = null) {
  const sid = session || randomUUID().slice(0, 8);
  const user = `user-country-${country}-session-${sid}`;
  return `http://${user}:${PROXYHAT_PASS}@${PROXYHAT_GATE}:${PROXYHAT_PORT}`;
}

const got = gotScraping.extend({
  useHeaderGenerator: true,
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['windows', 'macos'],
    locales: ['en-US', 'en'],
  },
  timeout: { request: 20000 },
  retry: {
    limit: 3,
    statusCodes: [429, 403, 500, 502, 503],
    backoffLimit: 5000,
  },
  hooks: {
    beforeRequest: [
      (options) => {
        // Rotuj proxy dla każdego żądania
        options.proxyUrl = buildProxyUrl('US');
      },
    ],
    afterResponse: [
      (response, retryWithMergedOptions) => {
        if (response.statusCode === 403 || response.statusCode === 429) {
          // Rotuj IP i spróbuj ponownie
          retryWithMergedOptions({
            proxyUrl: buildProxyUrl('US'),
          });
        }
        return response;
      },
    ],
  },
});

async function fetchUrl(url) {
  try {
    const res = await got(url);
    return { status: res.statusCode, body: res.body };
  } catch (err) {
    console.error(`Blad dla ${url}: ${err.message}`);
    return null;
  }
}

// Bounded concurrency: max 10 rownocznych zapytan
const limit = pLimit(10);
const urls = [
  'https://example.com/page-1',
  'https://example.com/page-2',
  'https://example.com/page-3',
];

const results = await Promise.all(
  urls.map((url) => limit(() => fetchUrl(url)))
);
console.log(results.filter(Boolean).length, 'udanych odpowiedzi');

Kluczowe elementy tego przykładu:

  • beforeRequest hook — ustawia nowe proxyUrl dla każdego żądania, rotując sesję.
  • afterResponse hook — przy 403/429 rotuje IP i wywołuje retryWithMergedOptions, co powoduje ponowne żądanie z nowym proxy.
  • retry config — got wbudowane retry z backoff do 5000 ms i limit 3 prób.
  • pLimit(10) — ogranicza współbieżność do 10 żądań jednocześnie, zapobiegając przebiciu limitów.

ProxyHat SDK: generowanie nazw użytkownika per-żądanie

Jeśli wolisz nie budować URL-i ręcznie, możesz użyć wzorca helpera generującego nazwy użytkownika ProxyHat z flagami:

class ProxyHatUsername {
  constructor(baseUser, basePass) {
    this.baseUser = baseUser;
    this.basePass = basePass;
  }

  build({ country, city, session }) {
    let user = `user-country-${country}`;
    if (city) user += `-city-${city}`;
    if (session) user += `-session-${session}`;
    return user;
  }

  url({ country, city, session, socks5 = false }) {
    const proto = socks5 ? 'socks5' : 'http';
    const port = socks5 ? 1080 : 8080;
    const user = this.build({ country, city, session });
    return `${proto}://${user}:${this.basePass}@gate.proxyhat.com:${port}`;
  }
}

const ph = new ProxyHatUsername('twoj-user', 'twoje-haslo');

// HTTP proxy z sesja i geo US
console.log(ph.url({ country: 'US', session: 'task-42' }));
// http://user-country-US-session-task-42:twoje-haslo@gate.proxyhat.com:8080

// SOCKS5 proxy z miastem
console.log(ph.url({ country: 'DE', city: 'berlin', socks5: true }));
// socks5://user-country-DE-city-berlin:twoje-haslo@gate.proxyhat.com:1080

Wzorce produkcyjne: retry, afterResponse, cookie jars i Crawlee

Cookie jars

Wiele stron ustawia ciasteczka anti-bot (np. cf_clearance Cloudflare) podczas pierwszego żądania i oczekuje ich w kolejnych. got-scraping obsługuje cookie jars przez opcję cookieJar z tough-cookie:

import { gotScraping } from 'got-scraping';
import { CookieJar } from 'tough-cookie';

const cookieJar = new CookieJar();
const got = gotScraping.extend({
  useHeaderGenerator: true,
  cookieJar,
  proxyUrl: 'http://user-country-US:pass@gate.proxyhat.com:8080',
});

// Pierwsze zapytanie ustawia ciasteczka
await got('https://example.com/landing');
// Drugie zapytanie wysyla ciasteczka automatycznie
const res = await got('https://example.com/data');

Bounded concurrency z p-limit

Scraping na skalę wymaga kontroli współbieżności. p-limit to najprostsze rozwiązanie w ekosystemie Node.js. Dla 1000 URL-i ustaw pLimit(10) do pLimit(20) — więcej niż 20 jednoczesnych żądań przez jeden proxy gateway często powoduje rate limiting. Jeśli masz pulę 50 proxy, możesz zwiększyć współbieżność proporcjonalnie.

Crawlee CheerioCrawler: gdy potrzebujesz frameworka

Jeśli Twój scraping rośnie poza prostą pętlę, Crawlee — również od Apify — integruje got-scraping w CheerioCrawler. Daje Ci kolejkę URL-i, automatyczne retry, proxy rotation i parsowanie Cheerio w jednym pakiecie:

import { CheerioCrawler } from 'crawlee';

const crawler = new CheerioCrawler({
  requestHandler: async ({ $, request }) => {
    const title = $('title').text();
    const h1s = $('h1').map((_, el) => $(el).text()).get();
    console.log(request.url, title, h1s);
  },
  proxyConfiguration: {
    proxyUrls: [
      'http://user-country-US-session-s1:pass@gate.proxyhat.com:8080',
      'http://user-country-US-session-s2:pass@gate.proxyhat.com:8080',
      'http://user-country-US-session-s3:pass@gate.proxyhat.com:8080',
    ],
  },
  maxRequestsPerMinute: 60,
  maxConcurrency: 10,
  useHeaderGenerator: true,
});

await crawler.run(['https://example.com/page-1', 'https://example.com/page-2']);

Crawlee automatycznie rotuje proxy z proxyUrls, zarządza sesjami i retry. To idiomatyczne podejście, gdy Twój scraping przekracza kilkadziesiąt URL-i.

Kiedy headless browser jest nieunikniony

got-scraping obsługuje żądania HTTP — nie wykonuje JavaScriptu. Jeśli strona renderuje treść przez JS (SPA, React, Vue) lub używa zaawansowanych wyzwań anti-bot (Cloudflare Turnstile, Datadome challenge), sam got-scraping nie wystarczy. Wtedy przechodzisz do Playwright lub Puppeteer, najlepiej przez Crawlee's PlaywrightCrawler.

Heurystyka: jeśli curl z nagłówkami przeglądarki zwraca HTML z treścią — got-scraping wystarczy. Jeśli zwraca pusty <div id="root"> lub stronę challenge — potrzebujesz browsera. W obu przypadkach proxy rezydencjalne pozostaje kluczowe: browser bez dobrego IP też zostanie zablokowany.

Etyka i legalność: publiczne dane, robots.txt i oficjalne API

Scraping danych publicznych jest legalny w wielu jurysdykcjach, ale granice zależą od kontekstu. W USA CFAA była interpretowana szeroko, choć wyrok Van Buren v. United States (2021) zawęził jej zastosowanie do danych, do których nie miałbyś dostępu w ogóle. W UE GDPR ogranicza przetwarzanie danych osobowych — scraping danych osobowych bez podstawy prawnej może być nielegalny.

Praktyczne zasady:

  • Scraping tylko danych publicznie dostępnych — jeśli potrzebujesz logowania, prawdopodobnie łamiesz ToS.
  • Honoruj robots.txt — to nie tylko etykieta, w niektórych jurysdykcjach ma wagę prawną.
  • Przestrzegaj limitów — jeśli strona mówi 1 req/sec, dostosuj się. Proxy nie daje licencji na spam.
  • Preferuj oficjalne API — jeśli strona oferuje API, użyj go. Jest tańsze, stabilniejsze i legalne.
  • Nie scrapuj danych osobowych bez podstawy — GDPR wymaga podstawy prawnej (zgoda, uzasadniony interes).

Więcej wzorców scrapingowych znajdziesz w przypadkach użycia ProxyHat oraz w dokumentacji ProxyHat. Do monitorowania wyników wyszukiwania sprawdź śledzenie SERP.

Kluczowe wnioski

got-scraping w Node.js rozwiązuje trzy najważniejsze przyczyny blokad: niespójne nagłówki (przez header-generator), brak HTTP/2 (przez natywną obsługę) i adresy IP z datacenter (przez proxy rezydencjalne). Używaj got.extend z useHeaderGenerator: true, ustawiaj proxyUrl na gate.proxyhat.com:8080 i rotuj sesje przez hook beforeRequest. Dla skalowania używaj p-limit lub przejdź na Crawlee's CheerioCrawler.

  • Nagłówki muszą być spójne: headerGeneratorOptions z fizycznymi kombinacjami (Chrome + Windows, Safari + macOS).
  • HTTP/2 jest domyślnie obsługiwany — nie spada do HTTP/1.1 przy proxy.
  • Proxy rezydencjalne są konieczne, gdy nagłówki i TLS już wyglądają prawdziwie — to ostatni filtr.
  • Rotuj sesje per-żądanie przez user-session-{uuid} w nazwie użytkownika ProxyHat.
  • Ogranicz współbieżność do 10–20 żądań na gateway, aby uniknąć rate limitów.
  • Honoruj robots.txt, GDPR i ToS — proxy nie zwalnia z odpowiedzialności prawnej.

Gotowy, aby zacząć?

Dostęp do ponad 50 mln rezydencjalnych IP w ponad 148 krajach z filtrowaniem AI.

Zobacz cenyProxy rezydencjalne
← Powrót do Bloga