cURL结合simple_html_dom按id定位tester元素提取纯文本报非对象错误
报错根因
这个报错Trying to get property 'plaintext' of non-object的触发逻辑非常直接:你调用$html->find("#tester")拿到的返回值根本不是可以访问属性的DOM节点对象,这时候强行取plaintext属性自然会报错。结合你用的工具和抓取场景,大概率是三个原因叠加导致的:
- simple_html_dom的find方法规则踩坑:这个是刚用这个库的开发者十有八九都会踩的坑——find方法默认行为是,如果不传第二个索引参数,不管匹配到多少个节点,都会把所有符合条件的节点打包成数组返回。哪怕页面里真的有id为
tester的元素,拿到的数组也没法直接用->访问属性,自然会抛非对象的错。必须给第二个参数传0,才会直接返回第一个匹配到的节点对象,匹配失败就返回null。 - 实际抓取到的内容里压根没有目标节点:别以为cURL返回了200状态码就拿到了你浏览器里看到的正式页面。这类公开SaaS站点很容易通过请求特征识别爬虫,返回的内容可能是验证页、空白跳转页;再加上这类页面基本都是前端JS动态渲染内容,cURL拿到的静态HTML里本身就没有id为
tester的节点。 - 选择器匹配失效:如果实际页面里的id有大小写差异、前后带隐藏空格、或者被JS动态修改过,哪怕选择器写的没错,也匹配不到对应节点。
排查和修复步骤
- 修正find方法的调用逻辑
查找单个元素的时候,必须给find方法传第二个参数0,表示返回第一个匹配的节点对象,匹配失败就返回null,而不是返回数组。
错误写法:
正确写法:echo $html->find("#tester")->plaintext;$tester_node = $html->find("#tester", 0); - 增加空值判断,从根源避免报错
拿到节点后先判断是不是有效对象,再提取纯文本,哪怕没找到元素也不会抛出非对象的警告:if ($tester_node) { echo trim($tester_node->plaintext); } else { echo "未找到id为tester的元素"; } - 增加抓取内容调试逻辑
cURL请求完成后先把拿到的内容存成本地html文件,打开搜索有没有id="tester"的节点:
如果打开的debug文件里根本搜不到$initpage = curl_exec($ch); // 调试用:把抓取内容存到本地文件 file_put_contents('debug_crawl.html', $initpage); curl_close($ch);tester元素,说明问题根本不在解析阶段,是抓取的内容不对:- 可以先补全cURL的请求头,模拟真实浏览器的请求特征,减少被拦截的概率
- 如果debug文件是目标站点的单页应用初始空白页,说明目标元素是JS执行后才渲染生成的,纯cURL+静态DOM解析的方案拿不到这类动态内容,需要更换支持JS渲染的抓取方案。
- 优化cURL请求配置
补全常用的浏览器请求头,提升请求的真实度:curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language: en-US,en;q=0.5', 'Accept-Encoding: gzip, deflate, br', 'Connection: keep-alive', 'Upgrade-Insecure-Requests: 1' ]);
修正后的完整可运行代码
<?php include('simple_html_dom.php'); error_reporting(E_ALL); ini_set('display_errors', true); $target = 'https://creator.zohopublic.com/james.taylor5/order-management-application/view-perma/CounterGP/FhXFwMj4Tah9gXk52AMS7GZFDK45qaV6eF5M4dF50x1WuApG4BeYs2jVh9nCwggm5WfA1MH8bAUy8NxvZ9Onn9Ds6u80vSBEXTTv'; $user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36'; $ch = curl_init($target); curl_setopt($ch, CURLOPT_USERAGENT, $user_agent); curl_setopt($ch, CURLOPT_URL, $target); curl_setopt($ch, CURLOPT_FAILONERROR, TRUE); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_MAXREDIRS, 4); curl_setopt($ch, CURLOPT_COOKIESESSION, TRUE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_ENCODING, ""); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // 补全真实浏览器请求头 curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language: en-US,en;q=0.5' ]); $initpage = curl_exec($ch); // 调试用,确认抓取内容是否正确 file_put_contents('debug_crawl.html', $initpage); curl_close($ch); $html = new simple_html_dom(); $html->load($initpage); // 正确的单元素查找方式 $tester_node = $html->find("#tester", 0); if ($tester_node) { echo trim($tester_node->plaintext); } else { echo "当前抓取的页面内容中没有找到id为tester的元素"; }
内容的提问来源于stack exchange,提问作者electronduck
相关产品推荐
相关产品推荐

