거의 모든 스크래핑 프레임워크는 프록시를 설정할 수 있게 해줍니다. 하지만 프록시를 직접 관리해주는 것, 즉 로테이션하고 차단된 프록시를 은퇴시키고 세션을 하나의 IP에 유지하는 것까지 해주는 경우는 훨씬 적습니다. 레지덴셜 프록시를 사용한다면, 프레임워크가 처리하는 부분과 게이트웨이가 처리하는 부분을 구분해서 아는 것만으로도 많은 글루 코드를 절약할 수 있습니다.
이 가이드는 흔히 쓰이는 여섯 가지 도구를 세 가지 질문을 기준으로 비교합니다: 프록시를 어떻게 설정하는지, 도구가 스스로 프록시를 로테이션하는지, 그리고 세션을 하나의 프록시에 묶어주는지입니다. 그런 다음 로테이팅 게이트웨이 뒤에서 각 도구에 필요한 약간의 코드를 보여줍니다.
짧은 답
| 도구 | 언어 | 프록시 설정 방법 | 스스로 로테이션하는가 | 세션을 프록시에 묶는가 |
|---|---|---|---|---|
| Crawlee | Node.js | ProxyConfiguration | 예: 라운드 로빈, 커스텀 함수, 티어 | 예, 세션 풀을 통해 |
| Scrapy | Python | request.meta["proxy"] 또는 환경 변수 | 아니요, 자체 미들웨어 필요 | 아니요 |
| Colly | Go | SetProxy 또는 SetProxyFunc | 예: 라운드 로빈 스위처 | 아니요 |
| Playwright | Node.js, Python, Java, .NET | 실행 시 또는 브라우저 컨텍스트별 | 아니요 | 컨텍스트별 |
| Puppeteer | Node.js | 실행 플래그 또는 브라우저 컨텍스트별 | 아니요 | 컨텍스트별 |
| Selenium | 다수 언어 | 브라우저 전체 옵션 | 아니요 | 아니요 |
Crawlee는 프록시를 단순한 설정값이 아니라 관리해야 할 대상으로 취급하는 유일한 도구입니다. 브라우저 도구들은 컨텍스트별 격리를 제공하는데, 이는 세션의 기본 구성 요소이지만 로테이션 로직은 직접 구현해야 합니다.
Crawlee: 관리되는 리소스로서의 프록시
Crawlee의 ProxyConfiguration은 프록시 URL 목록을 받아 “라운드 로빈 방식으로” 순환합니다. 목록 대신 각 요청에 사용할 프록시를 선택하는 함수를 전달할 수도 있습니다. 세 번째 모드인 티어드 프록시는 가장 저렴한 티어(흔히 프록시를 아예 사용하지 않는 티어)부터 시작해서, 사이트가 크롤러를 차단하면 한 단계 위 티어로 올라가고, 낮은 티어가 다시 통하면 다시 내려갑니다.
가장 중요한 부분은 세션입니다. 크롤러가 세션 풀을 사용해 실행되면, 모든 세션은 동일한 프록시를 유지합니다. 문서의 표현을 빌리면, 이후 같은 sessionId로 호출하면 “항상 같은 proxyUrl을 반환”합니다. Crawlee가 세션이 차단되었다고 판단하면 해당 세션을 은퇴시키고, 다음 세션은 다른 프록시를 받습니다. 쿠키는 세션을 따라가므로 IP와 쿠키가 함께 유지됩니다.
Scrapy: 요청당 하나의 프록시, 로테이션은 직접 구현
Scrapy의 HttpProxyMiddleware는 request.meta["proxy"]에서 프록시를 읽으며, 없으면 http_proxy와 https_proxy 환경 변수로 대체합니다. 자격 증명은 http://username:password@host:port처럼 URL 안에 넣을 수 있습니다. 이것이 전부입니다. 요청마다 다른 프록시를 고르고, 차단을 감지하고, 죽은 프록시를 쉬게 하는 일은 직접 하거나 scrapy-rotating-proxies 같은 확장 기능에 맡겨야 합니다. 이 확장은 프록시 목록을 읽어들이고 기본적으로 200이 아닌 응답이나 빈 응답을 차단으로 간주합니다.
Scrapy 통합 가이드에서 기본 설정을 다루고 있으며, Scrapy에서 레지덴셜 프록시 로테이션하기 글에서는 미들웨어를 더 깊이 다룹니다.
Colly: 단순한 라운드 로빈 스위처
Go 프레임워크인 Colly에는 하나의 프록시를 위한 SetProxy와, 요청마다 하나를 선택하는 함수를 위한 SetProxyFunc가 있습니다. Colly의 proxy 패키지에는 RoundRobinProxySwitcher가 포함되어 있는데, 이는 “요청마다 ProxyURL을 로테이션”하며 http, https, socks5 URL을 모두 받아들입니다. 세션 개념은 없으므로, 두 요청이 같은 IP를 공유해야 한다면 자신의 함수 안에서 결정해야 합니다.
Playwright와 Puppeteer: 컨텍스트별 격리
브라우저 자동화 도구들은 아무것도 로테이션하지 않지만, 각 브라우저 컨텍스트가 자체 프록시를 사용할 수 있게 해주며, 이것이 하나의 브라우저 안에서 여러 개의 분리된 세션을 운용하는 방법입니다.
- Playwright는
server,username,password를 받는proxy옵션을 제공하며, 브라우저 전체를 위해 실행 시 설정하거나newContext()로 컨텍스트 하나를 위해 설정할 수 있습니다. 전체 설정은 Playwright 통합 가이드를 참고하세요. - Puppeteer는
--proxy-server실행 플래그로 프록시를 설정하거나,createBrowserContext({ proxyServer })로 컨텍스트별로 설정합니다. 둘 다 자격 증명을 받지 않으므로, 프록시의 로그인 챌린지에는page.authenticate()로 응답해야 합니다.
각 컨텍스트에 자체 세션을 부여하면 컨텍스트마다 하나의 IP와 하나의 쿠키 저장소를 갖게 되며, 이는 로그인 플로우에 필요한 것입니다.
Selenium: 브라우저 전체 설정, 비밀번호 처리가 번거로움
Selenium은 브라우저 옵션을 통해 프록시를 설정하므로 브라우저 전체에 적용됩니다. Chrome에서는 보통 --proxy-server 스위치를 사용하는데, 여기에는 사용자 이름과 비밀번호를 넣을 자리가 없습니다. 흔한 해결책으로는 로그인 챌린지에 응답하는 작은 브라우저 확장 프로그램을 쓰거나, 비밀번호 없는 로컬 포워딩 프록시를 두어 트래픽을 자격 증명과 함께 제공업체로 전달하는 방법이 있습니다. 한때 인기 있던 해결책이었던 Selenium Wire는 2024년 1월에 아카이브 처리되어 더 이상 유지보수되지 않습니다.
로테이팅 게이트웨이가 바꾸는 것
프레임워크의 로테이션 기능은 프록시 목록, 즉 순환시키고 테스트하고 은퇴시키는 수백 개의 IP:port 쌍을 위해 설계되었습니다. 로테이팅 레지덴셜 게이트웨이는 다르게 동작합니다. 하나의 호스트와 포트에 접속하면 게이트웨이가 출구 IP를 선택합니다. 이로 인해 대부분의 작업이 코드 밖으로 빠져나갑니다:
- 로테이션은 게이트웨이에서 일어나므로 프록시 URL 하나면 충분합니다. 항목이 하나인 라운드 로빈 목록은 단순한 설정일 뿐입니다.
- 세션은 사용자 이름에 요청됩니다. Shifter에서는
sid-<id>를 추가해 하나의 출구 IP를 유지하고,ttl-<seconds>로 얼마나 오래 유지할지 지정합니다. 자세한 내용은 고정 프록시와 로테이팅 프록시 비교에서 설명합니다. - 위치 또한 사용자 이름의 일부이므로, 크롤러는 두 번째 프록시 목록 없이도 요청마다 국가나 도시를 지정할 수 있습니다.
따라서 프레임워크가 할 일은 하나로 줄어듭니다. 바로 자신만의 세션 개념을 프록시 사용자 이름 속 세션 ID에 매핑하는 것입니다.
연결하기
Crawlee. Crawlee의 세션 ID로 사용자 이름을 만드는 함수를 전달합니다. 세션 ID는 언더스코어 같은 문자를 포함할 수 있으므로 문자와 숫자만 남깁니다:
import { CheerioCrawler, ProxyConfiguration } from 'crawlee';
const proxyConfiguration = new ProxyConfiguration({
newUrlFunction: (sessionId) => {
const sid = sessionId
? `-sid-${String(sessionId).replace(/[^a-zA-Z0-9]/g, '')}-ttl-600`
: '';
return `http://customer-USERNAME${sid}:PASSWORD@p.shifter.io:443`;
},
});
const crawler = new CheerioCrawler({
proxyConfiguration,
useSessionPool: true,
persistCookiesPerSession: true,
async requestHandler({ request, $ }) {
console.log(request.url, $('title').text());
},
});
await crawler.run(['https://example.com/']);
Crawlee가 차단된 세션을 은퇴시키면, 다음 세션 ID는 자동으로 새로운 고정 IP를 받습니다.
Scrapy. 작은 다운로더 미들웨어가 Scrapy 자체 프록시 미들웨어가 실행되기 전에 프록시를 설정합니다. meta에 session 값을 가진 요청들은 하나의 IP를 공유하고, 나머지는 로테이션됩니다:
# middlewares.py
class GatewayProxyMiddleware:
def process_request(self, request, spider):
sid = request.meta.get("session")
user = "customer-USERNAME"
if sid:
user += f"-sid-{sid}-ttl-600"
request.meta["proxy"] = f"http://{user}:PASSWORD@p.shifter.io:443"
# settings.py
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.GatewayProxyMiddleware": 350,
}
Playwright. 세션당 컨텍스트 하나:
const context = await browser.newContext({
proxy: {
server: 'http://p.shifter.io:443',
username: 'customer-USERNAME-sid-cart1-ttl-600',
password: 'PASSWORD',
},
});
무엇을 선택할 것인가
프록시 기능이 아니라 사이트와 팀에 맞춰 프레임워크를 선택하세요. Node.js에서 차단 처리, 세션, 프록시 티어를 대신 처리해주길 원한다면 Crawlee가 가장 강력한 선택입니다. Scrapy는 Python에서 대량의 단순 HTML 페이지를 크롤링하는 가장 빠른 방법으로 남아 있으며, 프록시 관련 부족한 부분은 10줄짜리 미들웨어로 해결됩니다. Playwright나 Puppeteer는 페이지에 실제 브라우저가 필요할 때 적합한 도구이며, 컨텍스트는 깨끗한 세션을 제공합니다. Selenium도 작동하지만, 자격 증명 처리에 더 많은 노력이 필요합니다.
로테이팅 게이트웨이 뒤에서는 여섯 가지 도구 모두 같은 패턴으로 귀결됩니다. 프록시 URL 하나, 그리고 요청들이 IP를 공유해야 하는 곳에는 사용자 이름 속 세션 ID입니다. 이를 프로덕션 크롤러에 적용하는 방법에 대해서는 웹 스크래핑 사용 사례를 참고하세요.
출처 및 참고 자료
- Crawlee, 프록시 관리 가이드: crawlee.dev
- Scrapy, 다운로더 미들웨어 레퍼런스 (HttpProxyMiddleware): docs.scrapy.org
- scrapy-rotating-proxies: github.com
- Colly, proxy 패키지: pkg.go.dev
- Playwright, network and HTTP proxy: playwright.dev
- Puppeteer, BrowserContextOptions: pptr.dev
- Selenium Wire 저장소 (2024년 1월 3일 아카이브됨): github.com