Selenium은 현존하는 브라우저 자동화 도구 중 가장 널리 사용되는 도구이며, JavaScript 비중이 높은 대상을 스크레이핑할 때도 제 역할을 한다. 하지만 레지덴셜 프록시를 처음 추가할 때 거의 모두가 걸려 넘어지는 오래된 공백이 하나 있다. 프록시 호스트를 설정하는 것은 간단하지만, 사용자 이름과 비밀번호를 제공하는 것은 그렇지 않은데, Selenium에는 이를 위한 내장 방법이 없기 때문이다. Chrome을 인증이 필요한 프록시로 향하게 하면 Selenium이 채울 수 없는 네이티브 407 로그인 대화 상자가 나타나고, 스크립트가 멈춰버린다. 이를 우회하는 세 가지 방법을 소개한다.
이 글은 Playwright에서 레지덴셜 프록시 사용하기와 Puppeteer에서 사용하기 같은 다른 브라우저 가이드와 함께 볼 만한데, 두 도구 모두 프록시 인증을 네이티브로 처리한다. 완전한 브라우저가 필요하지 않다면, 일반 HTTP 클라이언트를 사용하는 Python에서 프록시 사용하기가 훨씬 간단하다.
아래 내용은 모두 Shifter의 레지덴셜 게이트웨이를 사용한다. 단일 엔드포인트 p.shifter.io:443이며, 모든 타겟팅 정보는 사용자 이름에 인코딩된다. 다른 제공업체를 사용한다면 호스트와 자격 증명만 바꾸면 되며, 전체적인 형태는 동일하다.
게이트웨이 모델을 한 문단으로 정리하면
프록시 사용자 이름에는 인증 정보와 타겟팅 정보가 함께 담긴다. 국가나 세션을 바꾸기 위해 엔드포인트를 전환하는 것이 아니라, 사용자 이름 문자열을 바꾼다.
customer-USERNAME-country-us-sid-abc123-ttl-600country-us는 미국을 타겟팅하고, sid는 고정 세션을 지정하며, ttl은 해당 IP를 N초 동안 유지한다. sid/ttl을 생략하면 매 연결마다 로테이션된다. 비밀번호는 고정값이다. Selenium에서의 전체 문제는 이 사용자 이름과 비밀번호를 프록시에 전달하는 방법에 있다.
핵심 문제
호스트 설정은 쉬운 절반에 불과하다. --proxy-server를 Chrome 옵션에 다른 곳과 똑같이 전달하면 된다.
from selenium import webdriver
options = webdriver.ChromeOptions()options.add_argument('--proxy-server=http://p.shifter.io:443') # host onlydriver = webdriver.Chrome(options=options)이는 자격 증명이 없는 IP 화이트리스트 방식 프록시에는 통한다. 하지만 게이트웨이는 사용자 이름과 비밀번호로 인증하는 방식이고, Chromium은 이 플래그로부터 자격 증명을 읽지 않으므로, 첫 페이지 이동에서 Selenium이 닫을 수 없는 407 인증 프롬프트에 걸려 멈춘다. 이 문제를 해결하려면 다음 세 가지 방법 중 하나가 필요하다.
방법 1: Selenium Wire (가장 쉬운 방법)
Selenium Wire는 Selenium을 확장하여 프록시 자격 증명을 직접 받아들이고, 인증을 대신 처리해준다. 마찰이 가장 적은 옵션이며 대부분의 Python 스크레이퍼가 선택하는 방법이다.
from seleniumwire import webdriver # pip install selenium-wireimport os
user = os.environ['SHIFTER_USER'] + '-country-us' # targeting in the usernamepw = os.environ['SHIFTER_PASS']
seleniumwire_options = { 'proxy': { 'http': f'http://{user}:{pw}@p.shifter.io:443', 'https': f'http://{user}:{pw}@p.shifter.io:443', 'no_proxy': 'localhost,127.0.0.1', }}
driver = webdriver.Chrome(seleniumwire_options=seleniumwire_options)driver.get('https://api.ipify.org')print(driver.page_source) # a US residential IPdriver.quit()사용자 이름에 담긴 타겟팅 플래그를 포함한 자격 증명은 proxy 설정에 들어가며, Selenium Wire가 407을 투명하게 처리한다. 또한 런타임에 driver.proxy를 재할당하여 프록시를 변경할 수 있어, Chrome을 다시 실행하지 않고도 로테이션을 할 때 유용하다.
방법 2: 자격 증명 확장 프로그램 (추가 라이브러리 없는 순수 Selenium)
순수 Selenium을 그대로 사용하고 싶다면, 고전적인 기법은 자격 증명으로 인증 챌린지에 응답하는 작은 Chrome 확장 프로그램을 로드하는 것이다. 즉석에서 이를 만들어 add_extension으로 전달한다.
# manifest.json declares proxy + auth permissions; background.js supplies creds.background_js = """chrome.webRequest.onAuthRequired.addListener( () => ({ authCredentials: { username: USER, password: PASS } }), { urls: ['<all_urls>'] }, ['blocking']);""".replace('USER', repr(user)).replace('PASS', repr(pw))# zip manifest.json + background.js, then:options.add_extension('proxy_auth.zip')이 방법은 의존성 없이 유지할 수 있고, 확장 프로그램이 작업을 처리하므로 어떤 Selenium 언어 바인딩에서도 동작한다. 단점은, 위의 blocking onAuthRequired 패턴이 Manifest V2 기법이고, Chrome이 MV3를 위해 MV2를 단계적으로 폐지하고 있어서, 현재 Chrome에서는 이 방식이 예전보다 취약해졌다는 점이다. 새로 시작한다면 Selenium Wire나 아래의 CDP 방식을 우선 고려하라.
방법 3: Selenium 4의 CDP
Selenium 4는 Chrome DevTools Protocol을 노출하며, Fetch 도메인을 통해 Fetch.authRequired를 처리하고 자격 증명과 함께 요청을 계속 진행함으로써 프록시 인증에 응답할 수 있다. 최신 Selenium에 네이티브로 포함되어 있어 추가 패키지가 필요 없지만, 직접 연결하기는 까다롭고, 사실상 Selenium Wire가 이미 감싸놓은 것을 다시 구현하는 셈이다. 서드파티 의존성을 전혀 두고 싶지 않고 CDP에 익숙하다면 이 방법을 택하라. 그렇지 않다면 Selenium Wire가 수고를 덜어준다.
지역과 세션 로테이션
타겟팅 정보가 사용자 이름에 담겨 있기 때문에, 다른 아이덴티티는 곧 다른 사용자 이름을 의미하며, Selenium에서는 프록시가 브라우저 수준에서 설정된다. 즉 로테이션은 탭 단위가 아니라 드라이버 단위로 일어난다. 실용적인 패턴 두 가지가 있다. Selenium Wire를 쓴다면 런타임에 driver.proxy를 재할당해 작업 단위별로 사용자 이름을 바꾸면 된다. 확장 프로그램이나 CDP 방식을 쓴다면 아이덴티티별로 드라이버를 하나씩 실행해 풀링한다. 어느 쪽이든 각 논리적 작업 단위마다 고유한 sid를 부여하고, 세션 도중이 아니라 작업 단위 사이에서 로테이션하라(고정 프록시 대 로테이팅 프록시에서 이 구분을 다룬다). 작업을 아이덴티티에 매핑하는 방법은 로드 밸런싱 글에서 설명한다.
드라이버를 재사용하고, 동시성을 제한하라
Chrome을 실행하는 데는 비용이 든다. 요청마다 새 드라이버를 띄우면 매번 실제 시작 시간과 메모리를 소모하는데, 지연 시간 가이드가 존재하는 이유가 바로 이 오버헤드를 없애기 위해서다. 드라이버(또는 소규모 드라이버 풀)를 하나 시작해 여러 요청에 걸쳐 재사용하라. 그리고 각 드라이버가 실제 메모리를 차지하는 완전한 브라우저이기 때문에 수천 개를 동시에 실행할 수는 없다. 제한된 풀을 유지하고 대상 호스트별 동시 진행 작업 수를 제한해서, 취약한 사이트 하나가 두들겨 맞는 동안 허용적인 다른 사이트가 굶주리는 일이 없게 하라. 대상이 감당할 수 있는 수준을 넘어선 병렬성은 처리량이 아니라 차단과 메모리 부족 크래시를 불러온다(차단을 피하는 방법).
브라우저는 차단을 피하는 절반에 불과하다
레지덴셜 IP는 사람처럼 보이는 것의 네트워크 측면을 처리해주지만, Selenium은 여전히 자동화된 브라우저를 구동하고 있으며, 사이트는 이 역시 지문 인식한다. navigator.webdriver, 자동화 플래그, 헤드리스 특유의 흔적들이 그렇다. 평판이 좋은 깨끗한 IP는 많은 챌린지를 피하게 해주지만(평판), 명백히 자동화된 브라우저를 숨겨주지는 않는다. user-agent와 뷰포트를 현실적으로 유지하고, 페이지를 사람과 같은 속도로 조작하며, 탐지를 유발하는 실수들이 IP 계층만큼이나 브라우저 계층에도 적용된다는 점을 기억하라. 둘은 서로 맞아떨어져야 한다.
실제로 프록시를 타고 있는지 확인하라
벤치마크나 다른 디버깅을 하기 전에, 브라우저 내부에서 종료 IP를 확인하라.
driver.get('http://ip-api.com/json')print(driver.find_element('tag name', 'body').text) # expect the targeted country자신의 IP가 나온다면 프록시가 적용되지 않은 것이다. 로그인 대화 상자에서 멈춘다면 인증 단계(Selenium Wire, 확장 프로그램, CDP)가 빠졌거나 잘못 설정된 것이다. 전반적으로 멈춘다면 로컬 아웃바운드 연결이 차단된 것이다. 이 세 가지 모두 타임아웃 진단 가이드에서 다룬다.
FAQ
왜 Selenium이 프록시 로그인 팝업에서 멈추나요?
Chromium은 인증이 필요한 프록시에 대해 네이티브 407 인증 대화 상자를 띄우는데, Selenium은 네이티브 브라우저 대화 상자와 상호작용할 수 없다. 다른 방법으로 챌린지에 응답해야 한다. Selenium Wire, 자격 증명 확장 프로그램, 또는 CDP의 Fetch.authRequired가 그것이다. --proxy-server만 설정하는 것으로는 호스트만 제공될 뿐 자격 증명은 제공되지 않는다.
--proxy-server에 user:pass@host를 넣을 수 있나요?
안 된다. Chromium은 --proxy-server 플래그로부터 자격 증명을 읽지 않는다. 호스트는 그곳에 제공하고, 사용자 이름과 비밀번호는 위의 세 가지 방법 중 하나를 통해 제공해야 한다. 게이트웨이가 타겟팅 정보를 사용자 이름에 인코딩하므로, -country-...가 포함된 전체 사용자 이름을 프록시 사용자 이름으로 전달해야 한다.
꼭 Selenium Wire를 써야 하나요? 그렇지 않지만, Python에서는 가장 간단한 방법이다. 의존성 없는 대안으로는 자격 증명 확장 프로그램(어떤 언어 바인딩에서도 동작하지만, 고전적인 MV2 패턴은 단계적으로 폐지되고 있음)이나 Selenium 4의 CDP(네이티브지만 직접 연결하기는 더 번거로움)가 있다.
Selenium에서 IP를 어떻게 로테이션하나요?
프록시 사용자 이름을 바꾸면 같은 게이트웨이를 통해 아이덴티티가 바뀐다. Selenium Wire에서는 런타임에 driver.proxy를 재할당할 수 있다. 그렇지 않다면 아이덴티티당 드라이버 하나를 실행해 풀링하라. 매 연결마다 로테이션하려면 사용자 이름에서 sid를 생략하면 된다.
Selenium, Playwright, Puppeteer 중 무엇을 써야 하나요? Playwright와 Puppeteer는 둘 다 프록시 자격 증명을 네이티브로 받아들이므로 이런 우회 과정 전체를 피할 수 있다. Selenium은 여기서 다룬 우회 방법 중 하나가 필요하다. 이미 스택에서 사용 중이라면 Selenium도 여전히 좋은 선택이다. 새로 시작하면서 프록시 인증을 수월하게 처리하고 싶다면 다른 두 도구가 더 매끄럽다.
결론
Selenium과 레지덴셜 프록시의 조합은 하나의 공백만 해결하면 잘 작동한다. --proxy-server로 프록시 호스트를 받아들이긴 하지만, 인증이 필요한 프록시에 자격 증명을 제공하려면 도움이 필요하다는 점이다. 마찰이 가장 적은 방법으로는 Selenium Wire를, 의존성 없이 유지하고 싶다면 자격 증명 확장 프로그램을, 네이티브 방식을 원한다면 Selenium 4의 CDP를 사용하라. 그런 다음 사용자 이름을 바꿔가며 지역과 세션을 로테이션하고, 오래 유지되는 드라이버를 재사용하며, 각 드라이버가 실제 브라우저라는 점을 감안해 동시성을 제한하고, IP만큼이나 브라우저 지문도 사람처럼 유지하라.
이를 제대로 해내면 Selenium은 일반 HTTP 클라이언트가 처리할 수 없는 상호작용이 많고 JavaScript 비중이 높은 대상을 다룰 수 있다. 레지덴셜 게이트웨이를 향하게 하고, 풀의 품질이 애초에 챌린지를 얼마나 자주 받는지를 좌우한다는 점을 기억하라(IP 평판). 가격 페이지에는 자신의 대상에 맞춰 테스트해볼 수 있는 GB당 요금제가 나와 있다.