在ThinkPHP 8.1中用QueryList实现网页抓取需四步闭环:安装依赖、注册服务提供者、封装命令行爬虫、处理分页与反爬;须配置超时、请求头、异常捕获及日志落盘。

要在ThinkPHP 8.1项目中快速实现网页内容抓取,必须绕过手动解析HTML、正则硬编码和HTTP客户端裸写这些低效方式——QueryList提供jQuery风格选择器+链式调用,3行代码就能提取标题列表,且天然兼容TP8.1的PSR-4自动加载与依赖注入机制。
安装QueryList并注册服务
在ThinkPHP 8.1根目录执行:composer require jaeger/querylist。这一步不能跳过,否则后续所有采集逻辑都会报Class not found错误。
进入app/provider.php,在return数组末尾添加一行:QL\QueryList::class,。该操作让QueryList类在容器中可被自动解析,避免每次都要use QL\QueryList再new实例。
【注意:不注册provider会导致命令行模式下QueryList无法通过容器获取,Web请求反而可能因自动加载侥幸成功,但定时任务必崩】
立即学习“PHP免费学习笔记(深入)”;
封装一个可复用的爬虫命令
执行php think make:command CrawlNews生成命令类。
打开app/command/CrawlNews.php,在execute()方法中写入:
第一步:初始化QueryList并设置基础参数
第二步:用get()请求目标URL,传入含User-Agent和timeout的配置数组
第三步:用find()配合CSS选择器定位标题元素,再用texts()批量提取纯文本
第四步:遍历结果,用Db::name('news')->insert()写入数据库
这四步构成最小可行闭环,缺一不可。其中第二步的timeout必须设为15以上,否则遇到慢响应网站会直接中断,连错误日志都来不及写。
处理动态分页与反爬头
方法一:手动拼接URL分页参数
适用于目标站URL结构清晰,如https://xxx.com/list?page=1。只需用for循环递增$page变量,每次构造新URL调用QueryList::get()即可。
方法二:从上一页DOM中提取下一页链接
先find('.pagination .next a')->attr('href'),判断返回值是否为空;非空则递归调用自身,空则终止。此法适配前端JS渲染分页的站点,但要注意避免无限重定向陷阱——需加max_depth=5硬限制。
方法三:伪造完整浏览器请求头
必须包含User-Agent、Accept、Accept-Language三项,缺任意一项都可能触发Cloudflare拦截。示例值:"User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"。
异常捕获与日志落盘
在采集循环内包裹try...catch,捕获Exception和Throwable两类错误。
对QueryList::get()失败的情况,记录url、http_code、error_message到runtime/log/crawl/下的日期文件,格式为Y-m-d。不写入数据库,防止日志写入失败导致整个采集流程卡死。
遇到429 Too Many Requests时,立即sleep(3)再重试一次,不要无脑循环重试——多数反爬系统3秒后即解除IP限流。



















