Node.js에서 got-scraping과 주거 프록시로 웹 스크래핑하기

got-scraping의 헤더 생성기와 HTTP/2 지원을 활용해 차단을 우회하는 방법, ProxyHat 주거 프록시 라우팅, 그리고 Crawlee 통합까지 다루는 실전 가이드입니다.

got-scraping in Node.js: A Developer's Guide with Residential Proxies
이 글의 목차

got-scraping nodejs로 차단을 우회하는 완벽한 가이드

Node.js 개발자라면 got-scraping이라는 이름을 들어본 적이 있을 것입니다. 이 라이브러리는 Apify가 got을 기반으로 만든 HTTP 클라이언트로, 브라우저와 거의 구별할 수 없는 요청을 생성하도록 설계되었습니다. 일반적인 got이나 axios 호출은 단 몇 번의 요청 만에 봇 감지 시스템에 노출되지만, got-scraping은 일관된 헤더 세트, HTTP/2 지원, 그리고 프록시 라우팅을 통해 그 간극을 메워줍니다.

이 글에서는 got-scraping을 프록시 미들웨어로 다루는 방법, ProxyHat 주거 프록시를 gate.proxyhat.com:8080에 연결하는 방법, 그리고 Crawlee로 확장하는 패턴까지 실전 코드와 함께 살펴봅니다.

법적 주의사항: 이 글은 공개 데이터 수집을 전제로 합니다. 미국의 CFAA(컴퓨터 사기 및 남용법)과 EU의 GDPR은 비공개 데이터나 ToS 위반 수집에 강력한 제재를 가할 수 있습니다. robots.txt를 준수하고, 속도 제한을 존중하며, 공식 API가 있다면 그것을 먼저 사용하세요.

왜 일반 got/axios 요청이 감지되는가

대부분의 스크래핑 차단은 TLS 지문, 헤더 순서, 그리고 IP 평판이라는 세 가지 축에서 발생합니다. 순수 got은 기본적으로 User-Agent 하나만 설정하면 끝이라고 착각하기 쉽지만, 현대 봇 감지 시스템은 훨씬 더 깊이 들여다봅니다.

  • 헤더 순서: Chrome, Firefox, Safari는 각자 고유한 헤더 순서를 사용합니다. acceptaccept-encoding보다 먼저 오는지, sec-ch-uauser-agent보다 먼저 오는지가 중요합니다.
  • sec-ch-ua 계열: Chromium 기반 브라우저는 sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform 헤더를 보냅니다. 이 값들이 user-agent와 일치하지 않으면 즉시 의심을 받습니다.
  • accept-language 일관성: en-US,en;q=0.9 같은 값이 IP의 지역과 어울리지 않으면 신호가 됩니다.
  • HTTP/2 사용 여부: 실제 Chrome은 HTTP/2를 사용하지만, 많은 스크래퍼가 여전히 HTTP/1.1로 요청을 보냅니다.

got-scraping의 header-generator는 이 문제를 근본적으로 해결합니다. 브라우저, 운영체제, 기기 조합을 입력하면 그에 맞는 일관된 헤더 세트 전체를 생성합니다. 단순히 user-agent만 바꾸는 것이 아니라, accept, accept-encoding, accept-language, sec-ch-ua 계열, upgrade-insecure-requests까지 브라우저 프로필에 맞춰 정확한 순서로 배치합니다.

이것이 got scraping header generator가 다른 헤더 위장 기법보다 신뢰할 수 있는 이유입니다. 직접 헤더를 하드코딩하면 한 글자만 틀려도 지문이 어긋나지만, header-generator는 수천 개의 실제 브라우저 프로필을 기반으로 일관성을 보장합니다.

got-scraping의 관용적 사용법

got-scraping은 got의 확장이므로, got의 모든 옵션을 그대로 사용할 수 있습니다. 핵심은 got.extend()로 커스텀 인스턴스를 만들고 useHeaderGenerator 옵션을 켜는 것입니다.

기본 인스턴스 구성

import { gotScraping } from 'got-scraping';
import { HeaderGenerator } from 'got-scraping';

const headerGenerator = new HeaderGenerator({
  browsers: ['chrome'],
  devices: ['desktop'],
  operatingSystems: ['windows', 'macos'],
});

const client = gotScraping.extend({
  useHeaderGenerator: true,
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['windows', 'macos'],
  },
  http2: true,
  timeout: { request: 30000 },
  retry: { limit: 3 },
});

headerGeneratorOptionsHeaderGenerator 생성자와 동일한 스키마를 받습니다. browsers, devices, operatingSystems 배열을 조합해 원하는 브라우저 프로필을 만듭니다. http2: true는 가능한 경우 HTTP/2를 사용하도록 강제합니다.

proxyUrl 옵션

got-scraping은 proxyUrl 옵션을 통해 HTTP/1.1과 HTTP/2 프록시를 모두 지원합니다. 이 옵션은 got의 agent를 직접 만지는 것보다 훨씬 안전합니다. HTTP/2 프록시를 사용할 때는 proxyUrlhttps:// 스킴을 사용해야 하며, got-scraping이 자동으로 CONNECT 터널을 설정합니다.

const res = await client('https://example.com', {
  proxyUrl: 'http://user-country-US:pass@gate.proxyhat.com:8080',
});
console.log(res.statusCode, res.body.slice(0, 200));

이것이 got-scraping proxy의 기본 형태입니다. proxyUrl에 ProxyHat 게이트웨이를 넣기만 하면 됩니다.

ProxyHat 주거 프록시로 라우팅하기

헤더와 TLS가 완벽해도 IP 평판이 나쁘면 차단은 피할 수 없습니다. 데이터센터 IP는 Cloudflare, Akamai, PerimeterX 같은 시스템에서 거의 즉시 플래그됩니다. 주거 프록시는 실제 ISP가 할당한 주거 IP를 사용하므로, 헤더와 IP가 일관된 지역을 가리킬 때 가장 강력한 조합이 됩니다.

ProxyHat 주거 프록시는 gate.proxyhat.com 게이트웨이를 통해 접근합니다. HTTP는 포트 8080, SOCKS5는 포트 1080을 사용합니다. 사용자 이름에 국가, 도시, 세션 플래그를 넣어 지역 타겟팅과 세션 고정을 제어합니다.

연결 유형URL 형식사용 사례
HTTP (기본)http://user-country-US:pass@gate.proxyhat.com:8080대부분의 HTTP/HTTPS 스크래핑
HTTP + 세션http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080로그인 상태 유지, 다단계 폼
HTTP + 도시http://user-country-DE-city-berlin:pass@gate.proxyhat.com:8080지역별 가격/콘텐츠 차이 분석
SOCKS5socks5://user-country-US:pass@gate.proxyhat.com:1080TCP 레벨 터널링, 특수 프로토콜

SOCKS5는 HTTP 프록시가 차단되는 일부 환경에서 유용하지만, 대부분의 웹 스크래핑에서는 HTTP 게이트웨이가 충분합니다. SOCKS5는 socks5:// 스키마를 사용하며 got-scraping의 proxyUrl에 그대로 넣을 수 있습니다.

const res = await client('https://example.com', {
  proxyUrl: 'socks5://user-country-US:pass@gate.proxyhat.com:1080',
});

요청별 세션 회전 예제

이제 실제로 동작하는 Node.js 예제를 봅시다. 이 예제는 ProxyHat 사용자 이름을 요청마다 생성하고, got의 hooks로 재시도 로직을 추가합니다.

import { gotScraping } from 'got-scraping';
import { randomUUID } from 'node:crypto';

const PROXYHAT_USER = 'user';
const PROXYHAT_PASS = 'pass';
const GATEWAY = 'gate.proxyhat.com:8080';

function buildProxyUrl({ country = 'US', session } = {}) {
  const sid = session ?? randomUUID().slice(0, 12);
  const username = `${PROXYHAT_USER}-country-${country}-session-${sid}`;
  const encoded = encodeURIComponent(username);
  return `http://${encoded}:${PROXYHAT_PASS}@${GATEWAY}`;
}

const client = gotScraping.extend({
  useHeaderGenerator: true,
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['windows'],
  },
  http2: true,
  timeout: { request: 30000 },
  retry: { limit: 3, statusCodes: [403, 429, 500, 502, 503] },
  hooks: {
    beforeRequest: [
      (options) => {
        // 매 요청마다 새 세션 ID로 새 IP 할당
        options.proxyUrl = buildProxyUrl({ country: 'US' });
      },
    ],
    afterResponse: [
      async (response, retryWithMergedOptions) => {
        if (response.statusCode === 403 || response.statusCode === 429) {
          // IP가 차단된 것으로 보이면 새 세션으로 재시도
          return retryWithMergedOptions({
            proxyUrl: buildProxyUrl({ country: 'US' }),
          });
        }
        return response;
      },
    ],
  },
});

// 사용 예
const targets = [
  'https://example.com/page-1',
  'https://example.com/page-2',
  'https://example.com/page-3',
];

for (const url of targets) {
  const res = await client(url);
  console.log(res.statusCode, url, res.body.slice(0, 80));
}

beforeRequest 훅은 매 요청마다 proxyUrl을 새로 설정하므로, 각 요청이 다른 주거 IP에서 나갑니다. afterResponse 훅은 403/429를 받으면 새 세션으로 재시도합니다. 이 패턴은 nodejs scraping proxy 운영에서 가장 자주 쓰이는 형태입니다.

더 많은 엔드포인트 옵션은 ProxyHat 공식 문서에서 확인할 수 있습니다.

프로덕션 패턴

제한된 동시성

수백 개의 요청을 동시에 보내면 ProxyHat 게이트웨이는 잘 버텨도 타겟 서버가 막히거나 차단합니다. p-limit로 동시성을 묶으세요.

import pLimit from 'p-limit';

const limit = pLimit(20); // 동시 20개 요청

const results = await Promise.all(
  urls.map((url) => limit(() => client(url)))
);

20~50 동시 요청이 주거 프록시 스크래핑의 무난한 시작점입니다. 응답 시간이 200ms 이하로 유지된다면 점차 올리되, 429 비율이 5%를 넘으면 줄이세요.

쿠키 jar

세션 고정이 필요한 경우 (예: 로그인 후 페이지네이션), cookieJar 옵션과 ProxyHat 세션 플래그를 함께 사용하세요. got은 tough-cookie 기반 jar를 지원합니다.

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

const jar = new CookieJar();
const session = 'login-flow-001';

const proxyUrl = `http://${encodeURIComponent(
  `user-country-US-session-${session}`
)}:pass@gate.proxyhat.com:8080`;

const client = gotScraping.extend({
  cookieJar: jar,
  useHeaderGenerator: true,
  http2: true,
});

// 같은 세션 IP + 쿠키로 다단계 폼 진행
await client.post('https://example.com/login', {
  proxyUrl,
  json: { user: 'foo', pass: 'bar' },
});
await client.get('https://example.com/dashboard', { proxyUrl });

세션 ID가 같으면 ProxyHat은 같은 출구 IP를 유지하므로, 쿠키와 IP가 일관되게 유지됩니다.

Crawlee CheerioCrawler로 확장

수천 페이지를 크롤링해야 한다면 CrawleeCheerioCrawler가 자연스러운 다음 단계입니다. Crawlee는 got-scraping을 내부적으로 사용하므로, 프록시 설정이 그대로 적용됩니다.

import { CheerioCrawler } from 'crawlee';

const crawler = new CheerioCrawler({
  useHeaderGenerator: true,
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['windows'],
  },
  proxyConfiguration: {
    newUrlFunction: () =>
      `http://${encodeURIComponent(
        `user-country-US-session-${randomUUID().slice(0, 12)}`
      )}:pass@gate.proxyhat.com:8080`,
  },
  maxConcurrency: 20,
  requestHandler: async ({ $, request }) => {
    const title = $('title').text();
    console.log(request.url, title);
  },
});

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

proxyConfiguration.newUrlFunction은 각 요청마다 새 프록시 URL을 반환합니다. 이것이 Crawlee에서 got-scraping proxy를 회전시키는 관용적 방법입니다.

컨테이너화

스케일아웃이 필요하면 각 컨테이너가 독립적인 세션 풀을 가지도록 설계하세요. 예를 들어 10개 컨테이너가 각각 50 동시 요청을 처리하면 총 500 동시 세션이 됩니다. 컨테이너 간 세션 ID 충돌을 피하려면 호스트명이나 컨테이너 ID를 세션 접두사로 사용하세요.

const containerId = process.env.HOSTNAME ?? 'local';
const session = `${containerId}-${randomUUID().slice(0, 8)}`;

헤드리스 브라우저가 불가피한 순간

got-scraping은 강력하지만, 다음 상황에서는 한계가 있습니다.

  • JavaScript 렌더링 필수: 콘텐츠가 클라이언트 사이드에서 로드되는 SPA.
  • 복잡한 CAPTCHA: hCaptcha, reCAPTCHA v3가 챌린지를 강제하는 경우.
  • 행동 지문: 마우스 이동, 스크롤 패턴을 검사하는 고급 시스템.

이때는 Playwright나 Puppeteer로 헤드리스 Chrome을 띄우고, ProxyHat 주거 프록시를 브라우저 수준에서 설정하세요. Crawlee의 PlaywrightCrawler는 got-scraping과 같은 proxyConfiguration 인터페이스를 사용하므로 전환 비용이 낮습니다.

다만 헤드리스 브라우저는 메모리와 CPU 비용이 got-scraping의 수십 배입니다. 가능하면 got-scraping으로 먼저 시도하고, 진짜 렌더링이 필요한 페이지만 헤드리스로 라우팅하는 하이브리드 전략이 비용 효율적입니다.

윤리와 컴플라이언스

기술적으로 가능하다고 해서 합법적인 것은 아닙니다. 스크래핑 전 다음을 확인하세요.

  • 공개 데이터만: 로그인 없이 접근할 수 없는 페이지는 CFAA 위반 소지가 있습니다.
  • GDPR: EU 거주자 개인 데이터를 수집하면 동의 기반 또는 정당한 이익 근거가 필요합니다.
  • robots.txt: Disallow된 경로는 수집하지 마세요. 법적 강제력은 약하지만, ToS 위반 주장의 근거가 됩니다.
  • 속도 제한: 타겟 서버에 부하를 주지 않는 속도를 유지하세요. 분당 60~600 요청이 일반적인 안전 범위입니다.
  • 공식 API 우선: 공식 API가 있다면 스크래핑보다 안정적이고 합법적입니다.

핵심 요약

  • got-scraping은 header-generator로 일관된 브라우저 헤더 세트를 생성하고 HTTP/2를 지원해 차단을 줄입니다.
  • got.extend() + useHeaderGenerator + headerGeneratorOptions가 관용적 구성 패턴입니다.
  • ProxyHat 주거 프록시는 gate.proxyhat.com:8080(HTTP) 또는 :1080(SOCKS5)로 연결하며, 사용자 이름에 국가/세션 플래그를 넣습니다.
  • beforeRequest 훅에서 요청마다 새 세션 ID를 생성해 IP를 회전시키세요.
  • 동시성은 p-limit로 묶고, 대규모 크롤링은 Crawlee CheerioCrawler로 넘기세요.
  • 헤드리스 브라우저는 JavaScript 렌더링이 진짜 필요할 때만 사용하세요.
  • 공개 데이터, robots.txt, 속도 제한을 존중하고 공식 API를 먼저 확인하세요.

ProxyHat 주거 프록시로 바로 시작하려면 프라이싱 페이지를 확인하고, 웹 스크래핑 사용 사례SERP 추적 사용 사례에서 더 많은 패턴을 살펴보세요. 사용 가능한 지역은 위치 페이지에서 확인할 수 있습니다.

자주 묻는 질문

got-scraping in Node.js란 무엇인가요?

got-scraping은 Apify가 got를 기반으로 만든 HTTP 클라이언트로, 브라우저와 구별하기 어려운 요청을 생성하도록 설계되었습니다. 핵심은 header-generator가 일관된 헤더 세트(순서, sec-ch-ua, accept-language)를 생성하고, HTTP/2를 지원하며, proxyUrl 옵션으로 프록시 라우팅을 단순화한다는 점입니다. 일반 got나 axios와 달리 봇 감지 시스템을 통과하기 위한 기본 기능이 내장되어 있습니다.

프록시 사용자에게 got-scraping이 왜 중요한가요?

헤더와 TLS 지문이 브라우저와 일치하지 않으면, 아무리 좋은 주거 프록시를 써도 차단됩니다. got-scraping은 헤더 일관성을 자동으로 보장하므로, 프록시 IP 평판이 헤더 프로필과 어긋나서 발생하는 불필요한 차단을 줄여줍니다. 결과적으로 주거 프록시 투자 대비 성공률이 크게 올라갑니다.

got-scraping에 어떤 프록시 유형이 가장 적합한가요?

주거 프록시가 가장 적합합니다. 데이터센터 IP는 Cloudflare, Akamai 같은 시스템에서 즉시 플래그되는 반면, 주거 IP는 실제 ISP 할당이므로 헤더와 IP 지역이 일치할 때 가장 자연스럽습니다. ProxyHat 주거 프록시는 gate.proxyhat.com:8080(HTTP) 또는 :1080(SOCKS5)로 연결하며, 사용자 이름에 국가와 세션 플래그를 넣어 지역 타겟팅과 세션 고정을 제어합니다.

got-scraping으로 차단을 피하려면 어떻게 하나요?

세 가지를 함께 사용하세요. 첫째, header-generator로 브라우저 프로필을 일관되게 생성합니다. 둘째, 주거 프록시로 IP 평판을 확보하고, 요청마다 세션 ID를 바꿔 IP를 회전시킵니다. 셋째, p-limit로 동시성을 20~50으로 묶고, got의 afterResponse 훅에서 403/429 응답을 새 세션으로 재시도합니다. 이 조합이 대부분의 중간 수준 봇 감지를 통과합니다.

got-scraping과 Crawlee는 어떤 관계인가요?

Crawlee는 Apify가 만든 크롤링 프레임워크로, CheerioCrawler와 PlaywrightCrawler 내부에서 got-scraping을 사용합니다. 따라서 got-scraping의 header-generator, proxyUrl, HTTP/2 지원이 Crawlee에 그대로 적용됩니다. 단일 페이지 요청은 got-scraping을 직접 쓰고, 수천 페이지 크롤링은 Crawlee로 넘어가는 것이 자연스러운 확장 경로입니다.

자주 묻는 질문

got-scraping in Node.js란 무엇인가요?

got-scraping은 Apify가 got를 기반으로 만든 HTTP 클라이언트로, 브라우저와 구별하기 어려운 요청을 생성하도록 설계되었습니다. 핵심은 header-generator가 일관된 헤더 세트(순서, sec-ch-ua, accept-language)를 생성하고, HTTP/2를 지원하며, proxyUrl 옵션으로 프록시 라우팅을 단순화한다는 점입니다. 일반 got나 axios와 달리 봇 감지 시스템을 통과하기 위한 기본 기능이 내장되어 있습니다.

프록시 사용자에게 got-scraping이 왜 중요한가요?

헤더와 TLS 지문이 브라우저와 일치하지 않으면, 아무리 좋은 주거 프록시를 써도 차단됩니다. got-scraping은 헤더 일관성을 자동으로 보장하므로, 프록시 IP 평판이 헤더 프로필과 어긋나서 발생하는 불필요한 차단을 줄여줍니다. 결과적으로 주거 프록시 투자 대비 성공률이 크게 올라갑니다.

got-scraping에 어떤 프록시 유형이 가장 적합한가요?

주거 프록시가 가장 적합합니다. 데이터센터 IP는 Cloudflare, Akamai 같은 시스템에서 즉시 플래그되는 반면, 주거 IP는 실제 ISP 할당이므로 헤더와 IP 지역이 일치할 때 가장 자연스럽습니다. ProxyHat 주거 프록시는 gate.proxyhat.com:8080(HTTP) 또는 :1080(SOCKS5)로 연결하며, 사용자 이름에 국가와 세션 플래그를 넣어 지역 타겟팅과 세션 고정을 제어합니다.

got-scraping으로 차단을 피하려면 어떻게 하나요?

세 가지를 함께 사용하세요. 첫째, header-generator로 브라우저 프로필을 일관되게 생성합니다. 둘째, 주거 프록시로 IP 평판을 확보하고, 요청마다 세션 ID를 바꿔 IP를 회전시킵니다. 셋째, p-limit로 동시성을 20~50으로 묶고, got의 afterResponse 훅에서 403/429 응답을 새 세션으로 재시도합니다. 이 조합이 대부분의 중간 수준 봇 감지를 통과합니다.

got-scraping과 Crawlee는 어떤 관계인가요?

Crawlee는 Apify가 만든 크롤링 프레임워크로, CheerioCrawler와 PlaywrightCrawler 내부에서 got-scraping을 사용합니다. 따라서 got-scraping의 header-generator, proxyUrl, HTTP/2 지원이 Crawlee에 그대로 적용됩니다. 단일 페이지 요청은 got-scraping을 직접 쓰고, 수천 페이지 크롤링은 Crawlee로 넘어가는 것이 자연스러운 확장 경로입니다.

시작할 준비가 되셨나요?

148개국 이상의 주거용, ISP, 모바일 프록시. 무료 계정을 만드세요.

무료 계정 만들기
← 블로그로 돌아가기