PHP HTTP + DOM

PHP cURL代理爬虫

PHP cURL负责代理连接和错误处理,DOMDocument与DOMXPath解析Quotes to Scrape。案例连续采集3页并输出UTF-8 JSON。

语言PHP 8.1+框架cURL + DOMXPath测试目标Quotes to Scrape输出quotes-php.json
打开对应产品提取 查看测试目标
01
END-TO-END RESULT

cURL代理请求、XPath解析与JSON输出

测试目标Quotes to Scrapehttps://quotes.toscrape.com/ 运行时PHP 8.1+cURL + DOMXPath 输出文件结构化数据quotes-php.json

代码直接连接隧道代理固定网关 proxy.123proxy.cn:36923。主机与端口无需替换,出口路由由当前产品配置决定;你只需设置控制台生成的完整代理用户名和密码。

02
CREDENTIALS

只把代理账密放进环境变量

PROXY_USER必须是控制台中的代理用户名,不是网站登录手机号。地区和SESSION场景应复制控制台生成的完整路由用户名。

export PROXY_USER="替换为控制台代理用户名"
export PROXY_PASS="替换为控制台代理密码"
# 隧道代理固定网关:proxy.123proxy.cn:36923

不要把真实代理密码提交到Git仓库、镜像、日志或前端代码中。

03
COMPLETE PROGRAM

scrape.php

以下代码包含完整入口和输出逻辑。安装注释、运行命令与依赖声明保留在文件顶部,复制为对应文件后即可执行。

<?php
// requirements: PHP 8+ with curl and dom extensions
$proxyHost = 'proxy.123proxy.cn';
$proxyPort = 36923;
$proxyUser = getenv('PROXY_USER');
$proxyPass = getenv('PROXY_PASS');

if (!$proxyUser || !$proxyPass) {
    throw new RuntimeException('set PROXY_USER and PROXY_PASS first');
}

function fetchHtml(
    string $url,
    string $proxyHost,
    int $proxyPort,
    string $proxyUser,
    string $proxyPass
): string {
    $curl = curl_init($url);
    curl_setopt_array($curl, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_PROXY => $proxyHost,
        CURLOPT_PROXYPORT => $proxyPort,
        CURLOPT_PROXYTYPE => CURLPROXY_HTTP,
        CURLOPT_PROXYUSERPWD => $proxyUser . ':' . $proxyPass,
        CURLOPT_CONNECTTIMEOUT => 15,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_FAILONERROR => true,
        CURLOPT_USERAGENT => '123Proxy-Developer-Example/1.0',
    ]);
    $body = curl_exec($curl);
    if ($body === false) {
        $error = curl_error($curl);
        curl_close($curl);
        throw new RuntimeException($error);
    }
    curl_close($curl);
    return $body;
}

$records = [];
$url = 'https://quotes.toscrape.com/';
for ($pageNumber = 1; $pageNumber <= 3; $pageNumber++) {
    $html = fetchHtml(
        $url,
        $proxyHost,
        $proxyPort,
        $proxyUser,
        $proxyPass
    );
    $document = new DOMDocument();
    @$document->loadHTML($html, LIBXML_NOERROR | LIBXML_NOWARNING);
    $xpath = new DOMXPath($document);

    foreach ($xpath->query('//div[contains(@class, "quote")]') as $quote) {
        $text = $xpath->query(
            './/span[contains(@class, "text")]',
            $quote
        )->item(0)?->textContent;
        $author = $xpath->query(
            './/small[contains(@class, "author")]',
            $quote
        )->item(0)?->textContent;
        $tags = [];
        foreach ($xpath->query(
            './/a[contains(@class, "tag")]',
            $quote
        ) as $tag) {
            $tags[] = trim($tag->textContent);
        }
        $records[] = [
            'text' => trim((string) $text),
            'author' => trim((string) $author),
            'tags' => $tags,
            'source_url' => $url,
        ];
    }

    $next = $xpath->query('//li[contains(@class, "next")]/a')
        ->item(0);
    if (!$next) {
        break;
    }
    $url = 'https://quotes.toscrape.com' .
        $next->getAttribute('href');
    usleep(500000);
}

file_put_contents(
    'quotes-php.json',
    json_encode($records, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
);
echo 'saved ' . count($records) . " records to quotes-php.json\n";
04
ENGINEERING CHECKS

案例没有省略的关键逻辑

01代理主机、端口与CURLOPT_PROXYUSERPWD分别设置
02连接超时和总超时分开配置
03HTTP 4xx/5xx通过CURLOPT_FAILONERROR进入错误处理
04XPath相对当前quote节点提取字段
05
SWITCH PROXY PRODUCT

替换网关,不改采集逻辑

代理产品网关域名端口适合任务
隧道代理proxy.123proxy.cn36923混合池 / 纯住宅池
隧道住宅代理residential.123proxy.cn33000国家地区与SESSION
不限量动态住宅unlimit.residential.123proxy.cn10253不限流量住宅任务
两类静态代理控制台分配的固定代理IP返回端口直连IP,无网关域名
隧道住宅代理的国家与SESSION写在完整代理用户名中

网关固定为 residential.123proxy.cn:33000。例如使用美国SESSION时,直接把控制台生成的完整用户名作为 PROXY_USER,代码无需解析或重组。

06
PRIMARY REFERENCES

框架文档与测试目标

07
TROUBLESHOOTING

常见问题

为什么不把账密直接拼到代理URL?

CURLOPT_PROXYUSERPWD专门处理代理凭证,避免特殊字符破坏URL结构。

PHP缺少DOMDocument怎么办?

需要启用dom扩展;缺少cURL时也必须先启用curl扩展。

如何改为SOCKS5?

将CURLOPT_PROXYTYPE改为CURLPROXY_SOCKS5_HOSTNAME,并使用控制台生成的SOCKS接入信息。