应首选Guzzle而非file_get_contents,因其支持User-Agent、超时、重试等反爬配置;DOMDocument解析需配合mb_convert_encoding()和LIBXML_HTML_NOIMPLIED等选项规避HTML结构敏感问题;Webman中爬虫须分层隔离并复用Guzzle客户端。

直接用 Webman + Guzzle 写爬虫没问题,但得避开几个关键陷阱:不是所有请求都能靠 GuzzleHttp\Client 一发就通;DOMDocument::loadHTML() 遇到 malformed HTML 容易静默失败;Webman 默认不处理并发限流,高频请求容易被封 IP。
为什么不用 file_get_contents 而选 Guzzle
file_get_contents 看似简单,但它依赖 allow_url_fopen 开启,且无法设置 User-Agent、Referer、超时、重试、Cookie 持久化等基础反爬字段。Guzzle 支持这些,还自带连接池和异步能力(配合 Promise),适合 Webman 这类常驻进程框架。
常见错误现象:Warning: file_get_contents(): failed to open stream: Connection refused 或返回空字符串 —— 很可能被目标站 HTTP 层拦截了。
- 必须显式设置
'user_agent'头,否则多数站点返回 403 - 超时建议设为
'timeout' => 10,避免单个请求卡死整个 worker - 启用
'verify' => false仅限测试环境,生产中应配好 CA 证书路径
DOMDocument 解析 HTML 的坑怎么绕
DOMDocument->loadHTML() 对 HTML 结构极其敏感:缺少 <html> 或 <body> 标签、UTF-8 BOM、未闭合标签都会导致解析中断或节点丢失。用 @ 抑制警告只是掩盖问题,不是解决。
立即学习“PHP免费学习笔记(深入)”;
推荐做法是先用 mb_convert_encoding() 统一编码,再用 libxml_use_internal_errors(true) 捕获解析错误:
$html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'); libxml_use_internal_errors(true); $dom = new \DOMDocument(); $dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); libxml_clear_errors();
- 务必加
LIBXML_HTML_NOIMPLIED和LIBXML_HTML_NODEFDTD,否则会自动补全<html><body>,干扰 XPath 定位 - 若目标页含大量 JS 渲染内容,
DOMDocument无能为力,得切到Symfony\Component\DomCrawler\Crawler或启用无头浏览器 - 用
DOMXPath查询前,先确认$dom->documentElement不为空,否则query()返回 false
Webman 中如何组织爬虫逻辑不污染主流程
Webman 是 Web 框架,不是爬虫框架。把爬虫逻辑硬塞进 app/controller 或直接写在 onWorkerStart 里,会导致:路由响应变慢、worker 进程被阻塞、无法单独启停爬虫任务、日志混杂。
正确做法是分层隔离:
- 爬虫代码统一放
app/spider/目录下,按职责拆成Producer(生成 URL)、Downloader(发请求)、Parser(解析数据)三类类文件 - 用
Workerman\Timer::add()启动独立定时任务,不要依赖 HTTP 请求触发 - HTTP 请求必须包装在
try/catch中,捕获GuzzleHttp\Exception\RequestException和DOMException,并记录完整错误上下文(URL、状态码、响应头) - 避免在爬虫中直接操作数据库,改用消息队列(如 Redis List)或写入临时 JSON 文件,由另一个 worker 异步消费入库
最易被忽略的是 DNS 缓存和连接复用:Guzzle 默认开启连接池,但 Webman 的多进程模型下,每个 worker 进程持有独立的 Client 实例,DNS 解析会重复进行。如果爬取域名集中,建议在 Client 构造时传入 'curl' => [CURLOPT_DNS_CACHE_TIMEOUT => 300] 并复用同一实例,而不是每次请求都 new 一个。



















