在 Deno 和 Bun 中使用代理:开发者完整指南

Deno 和 Bun 的原生 fetch 默认不读取代理配置。本文讲解如何用 Deno.createHttpClient 和 Bun fetch proxy 选项接入住宅代理,涵盖地理定位、粘性会话、SOCKS5、重试与生产实践。

Using Proxies in Deno and Bun: A Code-First Guide
本文目录

如果你在 Deno 或 Bun 里直接写 fetch('https://example.com'),请求会从本机 IP 发出,完全忽略 HTTP_PROXY 等环境变量——这与浏览器和 Node.js + undici 的行为并不一致。对于需要 在 Deno 和 Bun 中使用代理 的爬虫、SERP 追踪和自动化场景,你必须显式配置 HTTP 客户端。本文给出可运行的代码、生产级重试逻辑,以及 ProxyHat 住宅代理的接入方式。

Deno 代理基础:createHttpClient 与 fetch client 选项

Deno 从 1.25 起支持通过 Deno.createHttpClient 创建自定义 HTTP 客户端,并在 fetch 的第二个参数中传入 { client }。这是 Deno 代理的标准做法,因为 fetch 本身不接受 proxy 字段。

// deno run --allow-net --unstable proxy_deno.ts
const client = Deno.createHttpClient({
  proxy: {
    url: "http://gate.proxyhat.com:8080",
    basicAuth: {
      username: "user-country-US-session-abc123",
      password: "YOUR_PASSWORD",
    },
  },
});

const res = await fetch("https://api.ipify.org?format=json", { client });
const data = await res.json();
console.log(data); // { ip: "住宅 IP" }

用户名中的 country-US 指定出口国家,session-abc123 创建粘性会话——同一会话 ID 在 TTL 内保持同一出口 IP,适合登录态或多页爬取。要切换 IP,只需更换会话 ID。

Bun fetch proxy:一行搞定

Bun 的 fetch 原生支持 proxy 选项,这是 Bun 相对 Deno 的一个便利点。根据 Bun 官方文档proxy 接受标准 URL 字符串,Bun 会自动处理 CONNECT 隧道。

// bun run proxy_bun.ts
const proxyUrl = "http://user-country-DE-session-s1:YOUR_PASSWORD@gate.proxyhat.com:8080";

const res = await fetch("https://api.ipify.org?format=json", {
  proxy: proxyUrl,
});
console.log(await res.json());

Bun 同样支持 SOCKS5,只需把端口改为 1080 并使用 socks5:// 前缀。SOCKS5 适合需要更细粒度 TCP 隧道的场景,比如通过代理连接非 HTTP 协议。

环境变量路径:何时使用 HTTP_PROXY

两个运行时都支持读取 HTTP_PROXY / HTTPS_PROXY 环境变量,但行为有差异。Deno 需要在创建 HttpClient 时显式引用,而 Bun 在较新版本中会自动读取。对于生产环境,建议使用 per-client 配置而非全局环境变量,原因有三:

  • 多租户场景下不同任务需要不同地理出口。
  • 环境变量容易被其他依赖库意外读取,导致代理泄漏。
  • 粘性会话 ID 必须按请求动态生成,环境变量无法满足。
// Deno 环境变量方式(不推荐用于生产)
// HTTPS_PROXY=http://user:pass@gate.proxyhat.com:8080 deno run script.ts

// 推荐:动态构建 proxy URL
function buildProxyUrl(country: string, session: string) {
  const u = `user-country-${country}-session-${session}`;
  return `http://${encodeURIComponent(u)}:YOUR_PASSWORD@gate.proxyhat.com:8080`;
}

住宅代理与高拦截目标:并发粘性会话池

数据中心 IP 在面对 Cloudflare、Akamai、PerimeterX 等反爬系统时拦截率可达 60%–90%。住宅代理使用真实 ISP 分配的 IP,请求看起来像普通家庭用户,拦截率显著降低。下面是一个在 Deno 中用 Promise.all 并发轮换粘性会话、并用 AbortController 设置超时的完整示例:

// deno run --allow-net --unstable pool.ts
const TARGETS = [
  "https://httpbin.org/ip",
  "https://api.ipify.org?format=json",
  "https://ifconfig.me/all.json",
];

async function fetchViaProxy(target: string, sessionId: string) {
  const client = Deno.createHttpClient({
    proxy: {
      url: "http://gate.proxyhat.com:8080",
      basicAuth: {
        username: `user-country-US-session-${sessionId}`,
        password: "YOUR_PASSWORD",
      },
    },
  });

  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 15000);

  try {
    const res = await fetch(target, { client, signal: controller.signal });
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } finally {
    clearTimeout(timeout);
  }
}

const tasks = TARGETS.map((url, i) =>
  fetchViaProxy(url, `pool-${i}-${Date.now()}`).catch((e) => ({ error: e.message, url }))
);
const results = await Promise.all(tasks);
console.log(results);

每个请求使用独立的会话 ID(pool-0-时间戳),因此每个请求获得不同的住宅出口 IP。如果需要同一站点多页保持同一 IP,则在同一逻辑任务内复用会话 ID。

生产实践:重试退避、自定义 CA 与连接复用

指数退避重试

async function fetchWithRetry(
  url: string,
  client: Deno.HttpClient,
  maxRetries = 3,
  baseDelay = 500
): Promise<Response> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const res = await fetch(url, { client });
      if (res.ok) return res;
      if (res.status === 429 || res.status >= 500) throw new Error(`Retryable: ${res.status}`);
      return res; // 4xx 直接返回
    } catch (e) {
      if (attempt === maxRetries) throw e;
      const delay = baseDelay * 2 ** attempt + Math.random() * 100;
      await new Promise((r) => setTimeout(r, delay));
    }
  }
  throw new Error("unreachable");
}

自定义 CA 证书

在需要 MITM 代理或企业自签证书的环境中,Deno 允许通过 caCerts 传入 PEM 格式的根证书:

const caCert = await Deno.readTextFile("./custom-ca.pem");
const client = Deno.createHttpClient({
  caCerts: [caCert],
  proxy: {
    url: "http://gate.proxyhat.com:8080",
    basicAuth: { username: "user-country-US", password: "YOUR_PASSWORD" },
  },
});

Bun 目前不支持在 fetch 级别自定义 CA,但会信任系统证书库。如果遇到 TLS 错误,可设置 NODE_TLS_REJECT_UNAUTHORIZED 相关环境变量或在系统层面安装证书。

Deno vs Bun 代理能力对比

特性DenoBun
代理配置方式createHttpClient + fetch clientfetch proxy 选项
SOCKS5 支持是(socks5:// URL)是(socks5:// URL,端口 1080)
自定义 CAcaCerts 字段系统证书库
连接复用HttpClient 实例复用自动连接池
环境变量自动读取否(需显式引用)较新版本支持
粘性会话用户名编码用户名编码

使用 ProxyHat Node SDK(两个运行时通用)

ProxyHat 提供的 Node SDK 基于 undici,在 Deno 和 Bun 的 Node 兼容层下均可运行。这适合不想手写 proxy URL 的团队:

// bun run sdk_demo.ts 或 deno run --allow-net sdk_demo.ts
import { ProxyClient } from "proxyhat-sdk"; // 假定包名,参考官方文档

const client = new ProxyClient({
  gateway: "gate.proxyhat.com",
  port: 8080,
  username: "user-country-US",
  password: "YOUR_PASSWORD",
});

const res = await client.fetch("https://api.ipify.org?format=json");
console.log(await res.json());

SDK 封装了轮换逻辑和错误处理,适合快速上手。查看 ProxyHat 官方文档获取最新 API。完整定价参见 ProxyHat 定价页,可用地理位置见 代理位置列表

伦理与合规:只抓公开数据

爬虫不是法外之地。在美国,CFAA(计算机欺诈和滥用法)可能适用于绕过认证壁垒的访问;在欧盟,GDPR 对个人数据的处理有严格要求。实践原则:

  • 优先使用目标平台提供的官方 API。
  • 遵守 robots.txt 和服务条款。
  • 只采集公开可访问的数据,不绕过付费墙或登录。
  • 控制请求频率,避免对目标服务器造成负担。

更多爬虫场景参见 Web 抓取用例SERP 追踪用例

关键要点

Deno 用 createHttpClient({ proxy }) + fetch(url, { client });Bun 用 fetch(url, { proxy })。两者都通过用户名编码地理定位和粘性会话。生产环境务必加超时、重试和并发控制。住宅代理在高拦截目标上成功率远超数据中心 IP。

  • Deno fetch 不自动读取代理——必须传 client
  • Bun fetch 原生支持 proxy 选项,更简洁。
  • 用户名格式 user-country-US-session-abc123 同时控制地理和会话。
  • SOCKS5 使用端口 1080,HTTP 使用 8080
  • 住宅代理在高拦截目标上成功率可提升 3–5 倍

常见问题

在 Deno 和 Bun 中使用代理是什么意思?

指在 Deno 和 Bun 这两个现代 JavaScript 运行时中,通过配置 HTTP 客户端使 fetch 请求经由代理服务器发出。Deno 需要用 Deno.createHttpClient 创建带代理配置的客户端并传入 fetch,而 Bun 的 fetch 原生支持 proxy 选项。两者都支持在用户名中编码地理定位和粘性会话参数。

为什么 Deno 的 fetch 默认不使用代理?

Deno 的 fetch 实现基于 Rust 的 hyper 库,默认不读取 HTTP_PROXY 等环境变量,也不在 fetch 选项中接受 proxy 字段。这是出于安全隔离的考虑——避免无意中通过代理泄漏请求。开发者必须显式调用 Deno.createHttpClient 并在 fetch 中传入 client 参数才能使用代理。

哪种代理类型最适合 Deno 和 Bun 的爬虫场景?

对于高拦截目标(如受 Cloudflare 保护的站点),住宅代理是最佳选择,因为其 IP 来自真实 ISP,请求看起来像普通用户。数据中心代理速度快但容易被识别和封禁。移动代理适合需要移动端 IP 指纹的场景。ProxyHat 同时提供三种类型,HTTP 端口 8080,SOCKS5 端口 1080。

如何在 Deno 和 Bun 中避免被目标网站封禁?

使用住宅代理配合粘性会话轮换,为每个请求或每组相关请求分配不同会话 ID。设置合理的请求间隔和并发数(如 5–10 并发),加入指数退避重试,使用 AbortController 设置 10–15 秒超时。遵守 robots.txt 和目标平台服务条款,优先使用官方 API,只采集公开数据。

ProxyHat 代理在 Deno 和 Bun 中如何配置 SOCKS5?

SOCKS5 使用端口 1080 而非 8080。在 Bun 中将 proxy 选项设为 socks5://user:pass@gate.proxyhat.com:1080。在 Deno 中将 createHttpClient 的 proxy.url 设为 socks5://gate.proxyhat.com:1080,basicAuth 填入用户名和密码。用户名中同样支持 country 和 session 参数。

准备好开始了吗?

覆盖 148+ 国家的住宅、ISP 和移动代理。创建免费账户。

创建免费账户
← 返回博客