셀레니움 파이썬 웹 크롤러 프록시 적용 방법 및 트래픽 줄이기

김희규
6 min readAug 7, 2022

최근 파이썬과 셀레니움을 이용해서 웹 크롤러를 개발하고 프록시를 도입하면서 비용 문제 때문에 최적화를 진행했는데 그 과정에서 사용한 방법들을 정리해봅니다.

Photo by Jack Blueberry on Unsplash

웹사이트로부터의 차단 피하기

지나치게 많은 요청을 보내거나 오랫동안 크롤링을 하면 서버에서 알아차리고 접근을 막아버리게 됩니다. 이 때 응답으로 429 Too Many Request를 보내거나 웹 페이지 내에서 캡차등을 띄우기도 합니다. 크롤러인것을 숨기려고 User-Agent를 지속적으로 바꾸거나 요청 보내는 빈도를 줄여도 결국 오랜 기간 크롤러를 운영하면 차단을 당합니다. 그래서 대안으로 프록시를 사용합니다.

프록시 IP를 구매해도 하나의 IP에서 계속 접근하면 서버에서 눈치채기 때문에 IP를 바꿔주어야합니다. 때문에 여러 프록시 제공업체에서 Rotating Proxy라는 기능을 제공합니다. 업체에서 제공한 도메인을 거쳐서 요청을 날리면, 업체가 매 요청마다 IP를 바꿔서 전송해주기 때문에 서버에서 차단하기가 힘들게 됩니다.

셀레니움에서 유로 프록시 사용하기

유로 프록시 서비스의 경우 ID와 password 방식의 인증을 해야하는데, 셀레니움에서 크롬을 이용하는 경우에는 ID Password 방식을 사용하기 위해선 extension 기능을 이용해야 합니다. 자세한 방법은 아래 스택오버플로우 질문에 답이 있는데요.

정리하면 크롬에서는 프록시 인증이 javascript 코드를 이용해야만 가능합니다. 프록시 URL, ID, password 에 맞는 스크립트를 생성해서 extension으로 추가해주어야 합니다.

또, extension 기능을 사용할 경우 헤드리스 기능을 사용할 수 없게 되는데, 아래처럼 헤드리스를 지정해주면 extension과 헤드리스를 동시에 사용할 수 있게 됩니다.

from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("--headless=chrome")

셀레니움 트래픽 줄이기

셀레니움을 써야만 하는 경우에는 필연적으로 불필요한 리소스까지 가져오게 되므로, 더 많은 트래픽이 발생하게 됩니다. 특히 프록시 서비스의 경우 요청이나 트래픽 양을 기준으로 요금이 책정되다 보니, 비용을 줄이기 위해서라도 필요합니다.

크롬의 경우 개발자 도구의 Network 탭에서 전송된 요청 수(72 request)와 트래픽 양(2.5 MB resources)를 확인할 수 있습니다. 여기서 transferred가 트래픽 양이라고 생각했는데 프록시 업체에서 특정한 양이 훨씬 많길래 왜 그렇지?? 하고 물어봤는데 resources 란이 트래픽 양이라고 합니다… 😢

selenium-wire

셀레니움은 기본적으로 특정 URL로 요청을 막는 등의 기능을 지원해주지 않습니다. selenium-wire를 사용하면 가능합니다.

from seleniumwire import webdriverdef intercept_youtube_request(request):
url = request.url.split("?")[0]
# font.static.com 으로 가는 요청 모두 중단
iㄹ "fonts.gstatic.com" in url:
request.abort()
driver = wd.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)
# 트래픽을 줄이기 위해 일부 요청을 가로챈다.
driver.request_interceptor = func

이를 이용해서 불필요한 이미지, 폰트, 동영상, 광고, 각종 분석툴(Google Analytics 같은) 요청을 막고 요청 수와 트래픽 모두 줄일 수 있습니다.

(+내용추가) chrome extension으로 프록시를 사용하면 selenium-wire의 interceptor가 동작하지 않는 문제

이 경우 proxy chrome extension을 추가할 때 onBeforeRequest 리스너를 추가해서 특정 URL을 걸러낼 수 있습니다.

...
chrome.webRequest.onAuthRequired.addListener(
callbackFn,
{urls: ["<all_urls>"]},
['blocking']
);
// 여기 위는 프록시 관련 코드들인데 아래에 추가하기
var blockingPatterns = ['.png', '.jpeg', '.jpg'];
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
const {
pathname
} = new URL(details.url);
for(var i in blockingPatterns) {
if(pathname.includes(blockingPatterns[i]))
return { cancel: true };
}
return { cancel: false };
},
{urls: ["<all_urls>"]},
["blocking"]
);

캐싱

셀레니움은 기본적으로 매 인스턴스마다 새로운 사용자 프로필을 생성하고 그곳에 캐시 데이터를 저장합니다. 매 URL마다 셀레니움 인스턴스를 생성하게 된다면 캐싱 효과를 받지 못하게 됩니다. 한 인스턴스에서 계속 URL을 바꿔가며 크롤링을 하면 좋겠지만 그렇지 않은 경우에는 프로필이 저장되는 디렉토리를 지정해서 이전 인스턴스의 프로필, 캐시를 다시 사용할 수 있습니다.

options.add_argument(“user-data-dir=” + DIR_NAME))

브라우저를 종료하지 않고 계속 사용해서 크롤링을 하면 캐싱을 통해 트래픽을 절약할 수 있지만 IP가 달라도 쿠키나 세션을 통해 동일 사용자인 것을 알아차릴 수 있으니 주기적으로 사용자 프로필을 삭제해서 초기화해주어야합니다.

--

--

김희규

나는 최고의 선수다. 나를 최고라고 믿지 않는 사람은 최고가 될 수 없다.