开始之前

  • 启用了 curl 扩展的 PHP 8。Debian 和 Ubuntu 把它单独打包,包名带 PHP 版本号,例如 php8.3-curl。
  • Guzzle 示例需要 Composer:composer require guzzlehttp/guzzle。
  • 控制台服务页面上的 HOST、PORT、USERNAME 和 PASSWORD。

你的连接信息

登录控制台,打开你购买的服务。服务页面上会显示该服务的主机、端口、用户名和密码。ProxyPanda 没有一个统一的固定地址,所以每次都请从那里复制。指南中的代码使用下面这些占位符,请逐一替换成你自己的值。

HOST
该服务的代理地址,按服务页面上显示的原样填写。
PORT
要连接的端口。请和主机一起复制,因为不同服务的端口可能不同。
USERNAME
你的代理登录名。它与你登录控制台所用的邮箱是分开的。
PASSWORD
请完整复制。住宅代理在控制台中选择的选项会附加在密码末尾,凭记忆手动输入的密码会丢失这些选项。

把你用来连接的 IP 地址加入服务的白名单,就可以不用用户名和密码。这时代码里只需要 HOST 和 PORT。

示例都使用 HTTP,它可以通过加密隧道访问 https 网站。SOCKS5 同样可用:住宅代理的端口两种都接受,ISP 和数据中心代理在控制台里切换协议。

步骤

  1. 确认 curl 扩展已经加载

    php -m 会列出所有已加载的扩展。列表里没有 curl 时,为你的 PHP 安装对应的软件包,或在 php.ini 里取消 extension=curl 的注释,然后再看一次。

    terminal
    php -m | grep -i curl
  2. 让 cURL 句柄指向代理

    CURLOPT_PROXY 填 http://HOST:PORT,CURLOPT_PROXYUSERPWD 填 USERNAME:PASSWORD。两个超时分别限制连上代理的等待时间和整次传输的最长时间。脚本会输出 api.ipify.org 看到的地址。

    check_ip.php
    <?php
    $ch = curl_init('https://api.ipify.org?format=json');
    curl_setopt_array($ch, [
        CURLOPT_PROXY => 'http://HOST:PORT',
        CURLOPT_PROXYUSERPWD => 'USERNAME:PASSWORD',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 30,
    ]);
    
    $body = curl_exec($ch);
    if ($body === false) {
        echo 'cURL error ', curl_errno($ch), ': ', curl_error($ch), PHP_EOL;
    } else {
        echo $body, PHP_EOL;
    }
  3. 弄清 CURLOPT_HTTPPROXYTUNNEL 何时有用

    访问 https 网址时,cURL 会自己经代理建立 CONNECT 隧道,网站的 TLS 端到端保持加密,这个选项不起作用。设为 true 后,普通 http 网址也会强制走隧道。不设置时,这类请求以普通 HTTP 交给代理,大多数任务这样就够了。

  4. 用 Guzzle 发同样的请求

    Guzzle 读取一个带登录信息的代理 URL,connect_timeout 和 timeout 对应 cURL 的两个超时。密码里含有 @、: 或 / 时,先用 rawurlencode 处理,再拼进 URL。

    guzzle.php
    <?php
    require __DIR__ . '/vendor/autoload.php';
    
    use GuzzleHttp\Client;
    
    $client = new Client([
        'proxy' => 'http://USERNAME:PASSWORD@HOST:PORT',
        'connect_timeout' => 10,
        'timeout' => 30,
    ]);
    
    $response = $client->get('https://api.ipify.org?format=json');
    echo $response->getBody(), PHP_EOL;
  5. 在多个静态 IP 之间轮流使用

    ISP 和数据中心服务租给你的是一组固定地址。把它们存进 proxies.txt,每行一个,格式为 HOST:PORT:USERNAME:PASSWORD,循环会把每个请求交给下一行。把示例里重复的 ipify 网址换成你要抓取的页面。

    rotate.php
    <?php
    $proxies = file('proxies.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    $urls = array_fill(0, 6, 'https://api.ipify.org');
    
    foreach ($urls as $i => $url) {
        [$host, $port, $user, $pass] = explode(':', trim($proxies[$i % count($proxies)]), 4);
    
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_PROXY => "$host:$port",
            CURLOPT_PROXYUSERPWD => "$user:$pass",
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_TIMEOUT => 30,
        ]);
    
        $body = curl_exec($ch);
        echo $host, ' -> ', $body === false ? curl_error($ch) : $body, PHP_EOL;
    }

轮换 IP 与粘性 IP

每次 curl_init 都会创建新的句柄和新的连接。住宅代理选“随机 IP”时,第一个脚本运行两次会打印两个不同的地址。

复用同一个句柄或同一个 Guzzle 客户端,连接会在多次调用之间保持,共用连接的请求从同一个地址发出。这适合“粘性 IP”和静态 IP;在“随机 IP”模式下想换出口,就新建一个句柄。

轮换循环是为静态产品线准备的,proxies.txt 的每一行都是不同的固定 IP。住宅代理在代理端轮换,只写一行并选“随机 IP”就能达到同样效果。

常见错误与解决办法

Call to undefined function curl_init()

运行脚本的 PHP 没有加载 curl 扩展。命令行 PHP 和 Web 服务器里的 PHP 可能读取不同的 php.ini;php --ini 会显示命令行用的是哪一个。

cURL error 56: CONNECT tunnel failed, response 407

代理拒绝了登录。重新复制 USERNAME 和 PASSWORD。在 Guzzle 中用 rawurlencode 编码特殊字符,或者改用原生 cURL,CURLOPT_PROXYUSERPWD 里的密码不需要编码。

cURL error 7: Failed to connect

那个主机和端口上没有代理应答。把两者和服务页面对照一遍,再到控制台确认服务没有到期。

cURL error 28: Operation timed out

传输超过了 CURLOPT_TIMEOUT。住宅出口是家庭网络,有时较慢,可以多给一些时间,并只重试这一个网址,不必整批重来。

cURL error 60: SSL certificate problem: unable to get local issuer certificate

PHP 找不到 CA 证书包,Windows 版 PHP 上最常见。在 php.ini 中把 curl.cainfo 指向一个较新的 cacert.pem。关闭证书校验只是把问题藏起来,并不是解决办法。

选择哪条产品线

用 PHP 抓取或监控价格、目标网站接受服务器流量时,数据中心 IP 是低成本的起点,轮换循环会把工作分摊到你租的每个 IP 上。来自服务器 IP 段的请求开始被封时换成住宅代理;某个地址需要连续几周保持登录时,选 ISP。

卡在某一步了?

把命令和报错贴到 Discord,记得先删掉密码。那里的人大多遇到过这些错误。

加入 Discorddiscord.gg/proxypanda
$5 起步去 Discord 问