การตั้งค่า proxy ใน Node.js เชื่อมต่อ HTTP และ SOCKS5 proxy กับ fetch และ https
คู่มือนี้จะอธิบายวิธีการเชื่อมต่อ HTTP Proxy ของ Socks5Proxy ด้วย ProxyAgent ของ undici และ SOCKS5 Proxy ด้วย socks-proxy-agent ใน Node.js 18 ขึ้นไป รวมถึงโค้ดสำหรับรับรายการจาก API และหมุนเวียน
ต้องใช้อะไรบ้างในการใช้ proxy ใน Node.js?
fetch ที่มีอยู่ใน Node.js ทำงานด้วยไลบรารีชื่อ undici และโดยค่าเริ่มต้นจะไม่อ่านการตั้งค่า proxy ดังนั้น HTTP proxy ใช้ ProxyAgent ของ undici เป็น dispatcher ส่วน SOCKS5 proxy ใช้ socks-proxy-agent เป็น agent ในโมดูล https ของ Node
ตัวอย่างใช้ IP 198.51.100.23 พอร์ต HTTP 8000 พอร์ต SOCKS5 1080 ชื่อผู้ใช้ spx_user และรหัสผ่าน spx_pass รหัสทั้งหมดเป็นรูปแบบ ES module (.mjs)
npm install undici socks-proxy-agentคุณจะเชื่อมต่อ HTTP Proxy กับ fetch ได้อย่างไร?
ใส่ที่อยู่ proxy ใน ProxyAgent และใส่ชื่อผู้ใช้กับรหัสผ่านในตัวเลือก token เป็นค่า Basic authentication เมื่อส่ง dispatcher ไปยัง fetch จาก undici คำขอนั้นจะผ่าน proxy ที่อยู่ HTTPS จะถูกส่งผ่านในรูปแบบ CONNECT ที่เข้ารหัส
การใช้ ProxyAgent เดียวกันซ้ำในหลายคำขอจะทำให้การเชื่อมต่อถูกนำกลับมาใช้ใหม่ ทำให้เร็วขึ้น สร้างเอเจนต์หนึ่งอันต่อพร็อกซีหนึ่งอันและใช้สลับกัน
import { fetch, ProxyAgent } from "undici";
const agent = new ProxyAgent({
uri: "http://198.51.100.23:8000",
token: "Basic " + Buffer.from("spx_user:spx_pass").toString("base64"),
});
const res = await fetch("https://api.ipify.org?format=json", { dispatcher: agent });
console.log(res.status, await res.json()); // "ip" should be the proxy IPจะเชื่อมต่อ SOCKS5 Proxy อย่างไร
socks-proxy-agent เป็นเอเจนต์ที่ใช้ร่วมกับโมดูล http และ https ของ Node การใช้ที่อยู่ socks5h จะให้พร็อกซี่จัดการการแก้ไขชื่อโดเมน ตัวอย่างด้านล่างส่งเอเจนต์ไปยัง https.get และรวบรวมเนื้อหาการตอบกลับเพื่อแสดงผล
สำหรับไลบรารีที่ทำงานบนโมดูล http เช่น axios การใส่เอเจนต์เดียวกันใน httpAgent และ httpsAgent จะทำให้คำขอออกผ่าน SOCKS5
import https from "node:https";
import { SocksProxyAgent } from "socks-proxy-agent";
const agent = new SocksProxyAgent("socks5h://spx_user:spx_pass@198.51.100.23:1080");
https.get("https://api.ipify.org?format=json", { agent }, (res) => {
let body = "";
res.on("data", (chunk) => (body += chunk));
res.on("end", () => console.log(res.statusCode, body));
}).on("error", (err) => console.error("request failed:", err.message));รับรายการด้วย API แล้ววนอย่างไร
เมื่อเริ่มต้น คุณสามารถรับรายการ proxy ผ่าน API และสร้าง agent สำหรับแต่ละ proxy โดยในแต่ละคำขอจะใช้ agent ถัดไป รหัสด้านล่างนี้จะรับรายการ HTTP ในรูปแบบ ip:port:user:pass เพื่อสร้างอาร์เรย์ ProxyAgent และหากล้มเหลวจะลองใช้ proxy ถัดไป
อย่าลืมตั้งเวลาจำกัดในการร้องขอด้วย AbortSignal.timeout การร้องขอที่ไม่มีคำตอบจะทำให้งานทั้งหมดช้าลง
import { fetch, ProxyAgent } from "undici";
const API = "https://socks5proxy.io/api/v1";
const KEY = "YOUR_API_KEY";
const text = await (await fetch(API + "/proxies/download?format=ip:port:user:pass&protocol=http", {
headers: { Authorization: "Bearer " + KEY },
})).text();
const agents = text.trim().split("\n").map((line) => {
const [ip, port, user, pass] = line.trim().split(":");
return new ProxyAgent({
uri: "http://" + ip + ":" + port,
token: "Basic " + Buffer.from(user + ":" + pass).toString("base64"),
});
});
let i = 0;
async function get(url, tries = 3) {
for (let t = 0; t < tries; t++) {
const dispatcher = agents[i++ % agents.length];
try {
const res = await fetch(url, { dispatcher, signal: AbortSignal.timeout(20000) });
if (res.ok) return await res.text();
} catch {
// try the next proxy
}
}
return null;
}
console.log((await get("https://example.com/"))?.length);โค้ดจะเปลี่ยนไปอย่างไรเมื่อใช้การรับรอง IP?
เมื่อลงทะเบียน IPv4 ของเซิร์ฟเวอร์ที่รัน Node.js ในแดชบอร์ด คุณสามารถใช้ ProxyAgent โดยไม่ต้องใช้ token เพียงแค่ใส่ที่อยู่เท่านั้น สำหรับ SOCKS5 สามารถใช้แบบไม่มีชื่อผู้ใช้และรหัสผ่านเช่น socks5h://198.51.100.23:1080 ทำให้ง่ายต่อการปรับใช้บนเซิร์ฟเวอร์หลายเครื่องโดยไม่ต้องเก็บรหัสผ่านในตัวแปรสภาพแวดล้อมหรือโค้ด
หากคุณใช้ axios คุณจะเชื่อมต่ออย่างไร?
axios ทำงานบนโมดูล http และ https ของ Node ดังนั้นคุณสามารถส่งเอเจนต์เพื่อใช้พร็อกซีได้ สำหรับ SOCKS5 ให้ใส่ socks-proxy-agent ใน httpAgent และ httpsAgent และตั้งค่าตัวเลือก proxy ของ axios เป็น false เพื่อไม่ให้การตั้งค่าทับซ้อนกัน
HTTP proxies can also be configured in axios's proxy option with host, port, and auth, but using an agent is more stable for HTTPS addresses. The https-proxy-agent package allows HTTP proxies to connect with the same structure.
import axios from "axios";
import { SocksProxyAgent } from "socks-proxy-agent";
const agent = new SocksProxyAgent("socks5h://spx_user:spx_pass@198.51.100.23:1080");
const client = axios.create({ httpAgent: agent, httpsAgent: agent, proxy: false, timeout: 20000 });
const { data } = await client.get("https://api.ipify.org?format=json");
console.log(data);สิ่งที่ควรระวังเมื่อส่งคำขอหลายรายการพร้อมกันคืออะไร?
การเริ่มคำขอหลายพันครั้งพร้อมกันด้วย Promise.all จะเปิดการเชื่อมต่อมากเกินไปและสร้างภาระให้ทั้ง proxy และเว็บไซต์เป้าหมาย กำหนดจำนวนคำขอที่ประมวลผลพร้อมกันและใช้คิวงานที่จำกัดจำนวน สามารถสร้างฟังก์ชันเล็กๆ เองหรือใช้แพ็กเกจเช่น p-limit
การกระจายจำนวนการเชื่อมต่อพร้อมกันอย่างเท่าเทียมกันในแต่ละพร็อกซี่จะช่วยป้องกันไม่ให้คำขอรวมอยู่ที่ IP เดียว แผน共享รวมการเชื่อมต่อพร้อมกัน 500 ครั้ง ดังนั้นควรปรับจำนวนคำขอพร้อมกันทั้งหมดให้อยู่ในขอบเขตนี้
สามารถใช้ตัวแปรสภาพแวดล้อมเพื่อใช้พร็อกซีกับคำขอทั้งหมดได้หรือไม่
EnvHttpProxyAgent ของ undici อ่านตัวแปรสภาพแวดล้อม HTTP_PROXY, HTTPS_PROXY และ NO_PROXY เพื่อกำหนดพร็อกซี่ การลงทะเบียนด้วย setGlobalDispatcher จะทำให้ fetch ทั้งหมดในกระบวนการนั้นผ่านพร็อกซี่ สะดวกเมื่อไลบรารีหลายตัวใช้ fetch ภายในและต้องการการตั้งค่าเดียวกัน
การตั้งค่าพร็อกซี่แบบ global จะใช้ IP เดียวกันสำหรับทุกคำขอ จึงเหมาะสำหรับเครื่องมือหรือการทดสอบที่ต้องเข้าถึงจากประเทศเดียวมากกว่างานเก็บข้อมูลที่ต้องสลับ IP หากต้องการการหมุนเวียน ให้ส่ง dispatcher ในแต่ละคำขอตามที่กล่าวไว้ข้างต้น
// HTTPS_PROXY=http://spx_user:spx_pass@198.51.100.23:8000 node app.mjs
import { EnvHttpProxyAgent, setGlobalDispatcher, fetch } from "undici";
setGlobalDispatcher(new EnvHttpProxyAgent());
const res = await fetch("https://api.ipify.org");
console.log(await res.text());วิธีแก้ไขปัญหาที่พบบ่อย?
หาก dispatcher ที่ส่งไปยัง fetch ในตัวของ Node.js ดูเหมือนถูกละเลย ให้ตรวจสอบว่าได้นำเข้า fetch จากแพ็กเกจ undici ด้วยหรือไม่ หากเวอร์ชันของ fetch ในตัวและ undici ที่ติดตั้งต่างกัน เอเจนต์อาจไม่ตรงกัน วิธีที่แน่นอนที่สุดคือนำเข้า fetch และ ProxyAgent จากแพ็กเกจเดียวกัน
การตอบสนอง 407 หมายถึงข้อมูลการรับรองไม่ถูกต้อง กรุณาตรวจสอบว่าในค่า token หลังคำว่า Basic มีชื่อผู้ใช้:รหัสผ่านที่แปลงเป็น base64 แล้วหรือไม่ ECONNREFUSED มักเกิดจากพอร์ตไม่ถูกต้อง กรุณาใช้พอร์ต HTTP สำหรับตัวแทน HTTP และพอร์ต SOCKS5 สำหรับตัวแทน SOCKS
- fetch และ ProxyAgent อยู่ในแพ็คเกจ undici เดียวกัน
- 407: ตรวจสอบค่า token และ IP ที่ลงทะเบียน
- ECONNREFUSED: Distinguishing HTTP Port and SOCKS5 Port
- จำกัดเวลาในทุกคำขอ