Puppeteer에서 프록시는 어떻게 동작하나요?
Puppeteer는 Chrome을 실행할 때 넘기는 인자로 프록시를 정해요. --proxy-server에 주소를 넣으면 그 브라우저의 모든 페이지가 같은 프록시를 써요. HTTP 프록시의 아이디와 비밀번호는 실행 인자로 넣을 수 없어서, 페이지를 연 뒤 page.authenticate로 넘겨요. 그러면 프록시가 인증을 요청할 때 Puppeteer가 자동으로 답해요.
예시는 IP 198.51.100.23, HTTP 포트 8000, SOCKS5 포트 1080, 아이디 spx_user, 비밀번호 spx_pass를 써요.
npm install puppeteerHTTP 프록시와 인증은 어떻게 연결하나요?
launch의 args에 --proxy-server=198.51.100.23:8000을 넣고, newPage로 페이지를 만든 직후 page.authenticate에 아이디와 비밀번호를 넘겨요. 인증은 페이지마다 해야 해서 새 페이지를 만들 때마다 같은 호출을 넣어 주세요.
HTTPS 사이트도 그대로 열려요. 브라우저가 CONNECT 터널을 만들고 그 안에서 암호화된 채 통신해요.
import puppeteer from "puppeteer";
const browser = await puppeteer.launch({
headless: true,
args: ["--proxy-server=http://198.51.100.23:8000"],
});
const page = await browser.newPage();
await page.authenticate({ username: "spx_user", password: "spx_pass" });
await page.goto("https://api.ipify.org", { waitUntil: "domcontentloaded" });
console.log(await page.evaluate(() => document.body.innerText)); // proxy IP
await browser.close();SOCKS5 프록시는 어떻게 쓰나요?
--proxy-server에 socks5:// 주소를 넣으면 돼요. 다만 Chrome은 SOCKS 프록시의 아이디·비밀번호 인증을 지원하지 않아서 page.authenticate가 SOCKS5에는 적용되지 않아요. SOCKS5로 연결하려면 대시보드에 이 코드가 실행되는 서버의 IPv4를 등록하는 IP 인증을 쓰세요.
아이디와 비밀번호로 연결해야 하는 환경이라면 같은 IP의 HTTP 포트를 쓰는 것이 가장 간단해요. 같은 IP라서 대상 사이트에서 보이는 위치는 같아요.
import puppeteer from "puppeteer";
const browser = await puppeteer.launch({
args: ["--proxy-server=socks5://198.51.100.23:1080"], // server IP is authorized in the dashboard
});
const page = await browser.newPage();
await page.goto("https://api.ipify.org");
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();여러 IP로 브라우저를 나눠 띄우려면 어떻게 하나요?
Chrome은 실행된 브라우저 하나에 프록시 하나라서, 서로 다른 IP가 필요하면 브라우저를 따로 실행해요. 아래 코드는 API로 ip:port:user:pass 목록을 받아 앞의 3개로 브라우저를 하나씩 띄우고, 각 페이지에서 보이는 IP를 출력해요.
브라우저를 많이 띄우면 메모리를 많이 써요. 작업 큐를 두고 동시에 실행할 브라우저 수를 정해 두는 것을 추천해요.
import puppeteer from "puppeteer";
const KEY = "YOUR_API_KEY";
const res = await fetch("https://socks5proxy.io/api/v1/proxies/download?format=ip:port:user:pass&protocol=http", {
headers: { Authorization: "Bearer " + KEY },
});
const lines = (await res.text()).trim().split("\n").slice(0, 3);
await Promise.all(lines.map(async (line) => {
const [ip, port, user, pass] = line.trim().split(":");
const browser = await puppeteer.launch({ args: ["--proxy-server=http://" + ip + ":" + port] });
try {
const page = await browser.newPage();
await page.authenticate({ username: user, password: pass });
await page.goto("https://api.ipify.org", { timeout: 30000 });
console.log(ip, await page.evaluate(() => document.body.innerText));
} finally {
await browser.close();
}
}));페이지를 더 빠르게 열려면 어떻게 하나요?
프록시를 거치면 그림, 글꼴, 동영상까지 모두 프록시로 받아서 대역폭과 시간이 더 들어요. 필요한 것이 글과 데이터뿐이라면 page.setRequestInterception으로 그림과 글꼴 요청을 막으면 속도가 빨라지고 공유 플랜의 월 대역폭도 아낄 수 있어요.
waitUntil 값도 중요해요. networkidle0은 모든 요청이 끝날 때까지 기다려서 느릴 수 있으니, 필요한 요소가 보이면 바로 다음 단계로 넘어가도록 page.waitForSelector를 쓰는 편이 좋아요.
await page.setRequestInterception(true);
page.on("request", (req) => {
const type = req.resourceType();
if (type === "image" || type === "font" || type === "media") req.abort();
else req.continue();
});브라우저 하나에서 페이지마다 다른 IP를 쓸 수 있나요?
Chrome의 --proxy-server는 브라우저 전체에 적용되어 페이지마다 다른 IP를 주기 어려워요. Puppeteer 22 이상은 browser.createBrowserContext에 proxyServer 옵션을 넣어 컨텍스트마다 다른 프록시를 쓸 수 있어요. 컨텍스트는 쿠키와 저장소가 분리되어 있어서 계정별 작업에도 잘 맞아요.
컨텍스트에서 만든 페이지도 page.authenticate로 아이디와 비밀번호를 넘기면 돼요. 브라우저를 여러 개 띄우는 것보다 메모리를 적게 써요.
import puppeteer from "puppeteer";
const browser = await puppeteer.launch();
const context = await browser.createBrowserContext({ proxyServer: "http://198.51.100.23:8000" });
const page = await context.newPage();
await page.authenticate({ username: "spx_user", password: "spx_pass" });
await page.goto("https://api.ipify.org");
console.log(await page.evaluate(() => document.body.innerText));
await context.close();
await browser.close();그 나라 이용자처럼 보이게 하려면 무엇을 맞추나요?
IP 나라에 맞춰 page.setExtraHTTPHeaders로 Accept-Language를, page.emulateTimezone으로 시간대를 정하세요. 언어와 시간대가 IP 나라와 다르면 페이지가 엉뚱한 언어로 나오거나 날짜 표시가 달라질 수 있어요.
화면 크기는 page.setViewport로 정하고, 휴대폰 화면이 필요하면 puppeteer의 KnownDevices에 있는 기기 설정을 page.emulate에 넘기면 돼요.
await page.setExtraHTTPHeaders({ "Accept-Language": "ja-JP,ja;q=0.9" });
await page.emulateTimezone("Asia/Tokyo");
await page.setViewport({ width: 1366, height: 900 });오래 도는 작업에서 브라우저를 안정적으로 유지하려면 어떻게 하나요?
하루 종일 도는 작업은 브라우저를 한 번 띄워 계속 쓰기보다 일정 수의 페이지를 처리한 뒤 닫고 새로 띄우는 편이 안정적이에요. 오래 켜 둔 브라우저는 메모리를 점점 많이 쓰고, 한 번 오류가 나면 이후 작업이 모두 멈출 수 있어요.
페이지마다 try와 finally로 page.close를 꼭 부르고, goto에는 timeout을 넣어 응답 없는 페이지에서 멈추지 않게 하세요. 실패한 작업은 다른 프록시로 새 브라우저를 띄워 다시 시도하면 돼요.
프록시 응답 시간과 오류를 프록시별로 기록해 두면 느린 줄을 찾아 대시보드 고객센터에 문의하거나 작업 배치를 바꾸는 데 도움이 돼요.
자주 나는 오류는 어떻게 해결하나요?
net::ERR_INVALID_AUTH_CREDENTIALS 또는 407은 page.authenticate 값이 틀렸거나 newPage 뒤에 호출하지 않은 경우예요. 페이지마다 goto 전에 호출했는지 확인하세요.
net::ERR_PROXY_CONNECTION_FAILED는 주소나 포트가 틀린 경우예요. HTTP 주소에 HTTP 포트, SOCKS5 주소에 SOCKS5 포트를 썼는지 확인하세요. SOCKS5에서 연결이 거부되면 IP 인증에 서버의 나가는 IP가 등록되어 있는지 확인해 주세요.