PHP爬取网页出现Failed to open stream HTTP 400错误如何解决
问题描述
对目标站点做数据爬取时,程序抛出报错 Failed to open stream: HTTP request failed! HTTP/1.0 400 Bad request,爬取流程无法正常执行,原实现代码如下:
$numOfPage = 2; for ($i = 1; $i < $numOfPage; $i++) { $url = file_get_html('https://www.jugantor.com/all-news?page=' . '' . $i); foreach ($url->find('.col-8') as $a) { foreach ($a->find('a') as $b) { //echo $b->href . '<br>'; $newsUrl[] = $b->href; } } $html = file_get_html($newsUrl[0]); foreach($html->find('.col-md-8') as $a){ echo 1; } }
程序运行实际报错截图:
错误触发原因
- 核心原因是请求未携带合法浏览器标识:
file_get_html默认发起请求时使用PHP自带的User-Agent头,目标站点的反爬规则会直接拦截这类非浏览器发起的请求,返回400状态码。 - 链接格式非法:列表页提取的
href属性多为相对路径(如/news/xxx.html),代码未做域名拼接直接传入请求,生成的无效URL会触发400报错。 - 缺少异常校验:代码直接读取
$newsUrl[0]发起请求,若当前页未提取到有效链接,传入空值同样会触发请求错误。 - 存在内存泄漏风险:simple_html_dom解析完成后未主动清理内存,长时间运行会导致内存占用过高,间接引发请求异常。
修复方案
按以下步骤调整代码即可解决问题:
- 构造请求上下文,配置正常浏览器的User-Agent头,模拟真实用户访问绕过基础反爬。
- 对提取到的链接做格式判断,相对路径自动拼接站点根域名,生成合法的绝对请求地址。
- 增加请求前校验,列表页/详情页请求失败、未提取到有效链接时直接跳过当前循环,避免程序整体中断。
- 每次DOM解析完成后主动清理对象内存,增加合理请求间隔,降低被站点封禁的概率。
修复后的可运行代码:
// 提前引入simple_html_dom依赖,确保文件路径正确 require_once 'simple_html_dom.php'; $siteBase = 'https://www.jugantor.com'; $totalPage = 2; // 配置请求上下文,模拟Chrome浏览器请求头 $requestContext = stream_context_create([ 'http' => [ 'header' => "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36\r\nAccept: text/html,application/xhtml+xml\r\n", 'timeout' => 15 ] ]); for ($page = 1; $page < $totalPage; $page++) { $listPageUrl = $siteBase . '/all-news?page=' . $page; $listDom = file_get_html($listPageUrl, false, $requestContext); if (!$listDom) continue; $newsLinks = []; foreach ($listDom->find('.col-8') as $colBlock) { foreach ($colBlock->find('a') as $link) { $href = trim($link->href); if (!$href) continue; // 相对路径转绝对路径,兼容所有PHP版本 if (strpos($href, 'http') !== 0) { $href = rtrim($siteBase, '/') . '/' . ltrim($href, '/'); } $newsLinks[] = $href; } } if (empty($newsLinks[0])) { $listDom->clear(); unset($listDom); continue; } $detailDom = file_get_html($newsLinks[0], false, $requestContext); if ($detailDom) { foreach ($detailDom->find('.col-md-8') as $contentBlock) { echo 1; } $detailDom->clear(); unset($detailDom); } // 清理DOM内存避免泄漏 $listDom->clear(); unset($listDom); // 增加1秒请求间隔,降低被反爬拦截概率 sleep(1); }
内容的提问来源于stack exchange,提问作者user8272461
相关产品推荐
相关产品推荐

