cURL脚本偶发400错误:排查方法与参数优化咨询
问题与解决方案
我的PHP cURL脚本请求固定URL(symbol参数固定为RWOD)时,时而返回HTTP 200成功,时而返回HTTP 400错误。目标站点访问量高,推测有多台服务器做负载均衡。现需解决两个问题:
- 如何获取400错误的详细信息?
- 调整哪些cURL参数可让请求稳定成功?
相关信息:
- 前端页面:https://www.otcmarkets.com/stock/RWOD/profile
- 实际请求API:https://backend.otcmarkets.com/otcapi/company/profile/full/RWOD?symbol=RWOD
- 当前cURL代码:
function NewCurl() { global $OSFLSymb, $UseProxy, $ProxArr, $ProxyAuth, $HTTPStatus ; $NewCurlHandle = curl_init(); $NewUrl = 'https://backend.otcmarkets.com/otcapi/company/profile/full/' . $OSFLSymb . '?symbol=' . $OSFLSymb; curl_setopt ($NewCurlHandle, CURLOPT_URL, $NewUrl); curl_setopt($NewCurlHandle, CURLOPT_SSL_VERIFYPEER,false); curl_setopt_array( $NewCurlHandle, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_HTTPHEADER => [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0', 'Accept: application/json, text/plain, */*', 'Accept-Language: en-US,en;q=0.5', 'Origin: https://www.otcmarkets.com', 'Connection: keep-alive', 'Referer: https://www.otcmarkets.com/', 'Sec-Fetch-Dest: empty', 'Sec-Fetch-Mode: cors', 'Sec-Fetch-Site: same-site', 'Cache-Control: max-age=0' ], ] ); If ($UseProxy) { $CurrProxy = $ProxArr[array_rand($ProxArr)]; curl_setopt($NewCurlHandle, CURLOPT_PROXY, $CurrProxy); curl_setopt($NewCurlHandle, CURLOPT_PROXYUSERPWD, $ProxyAuth); } $CurlPageNew = curl_exec( $NewCurlHandle ); $HTTPStatus = curl_getinfo($NewCurlHandle, CURLINFO_HTTP_CODE); echo "<P>HTTP Code: $HTTPStatus"; echo "<BR>Proxy: $CurrProxy"; $cURLError = curl_error($NewCurlHandle); echo "<BR>cURL Error: $cURLError"; curl_close($NewCurlHandle); return $CurlPageNew; }
一、获取400错误的详细信息
HTTP 400错误的响应体通常包含具体错误原因(比如参数不合法、请求头格式错误等),你可以通过以下方式捕获:
- 输出400状态对应的响应内容
在获取$HTTPStatus后增加判断逻辑,直接打印响应体:
$HTTPStatus = curl_getinfo($NewCurlHandle, CURLINFO_HTTP_CODE); echo "<P>HTTP Code: $HTTPStatus"; if ($HTTPStatus == 400 && !empty($CurlPageNew)) { echo "<BR>400 Error Details: " . htmlspecialchars($CurlPageNew); }
- 查看响应头信息
部分服务器会在响应头中附加错误提示,可通过curl_getinfo获取:
$responseHeaders = curl_getinfo($NewCurlHandle, CURLINFO_HEADER_OUT); echo "<BR>Response Headers: " . htmlspecialchars($responseHeaders);
二、调整cURL参数让请求稳定成功
结合负载均衡场景和目标站点的校验逻辑,重点调整以下内容:
1. 修复请求头格式错误
当前代码中User-Agent没有按键: 值的HTTP头格式编写,这是典型的非法请求头,会直接触发400错误。修正后的请求头数组应把User-Agent改为:
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0'
2. 增加重试机制
负载均衡下部分服务器可能临时过载或触发频率限制,添加重试逻辑可提高成功率:
- 设置
CURLOPT_TIMEOUT为具体数值(比如30秒),避免无限等待 - 对400/5xx状态码进行1-2次重试,每次间隔1-2秒
3. 对齐真实浏览器请求特征
- 把
Referer改为对应前端页面的完整路径:https://www.otcmarkets.com/stock/' . $OSFLSymb . '/profile,更符合真实访问场景 - 移除
CURLOPT_CUSTOMREQUEST,GET请求使用默认设置即可,手动指定可能引发兼容问题 - 指定
CURLOPT_ENCODING为'gzip, deflate',匹配浏览器的编码协商行为
4. 代理优化
- 避免每次请求随机切换代理,固定一个代理会话(或同一IP段的代理),降低被识别为爬虫的概率
- 添加
CURLOPT_PROXYTYPE指定代理类型(比如CURLPROXY_HTTP),避免默认值引发的兼容性问题
调整后的完整代码示例
function NewCurl() { global $OSFLSymb, $UseProxy, $ProxArr, $ProxyAuth, $HTTPStatus ; $retryCount = 2; // 最大重试次数 $retryDelay = 1; // 重试间隔(秒) $successResponse = null; for ($i = 0; $i <= $retryCount; $i++) { $NewCurlHandle = curl_init(); $NewUrl = 'https://backend.otcmarkets.com/otcapi/company/profile/full/' . $OSFLSymb . '?symbol=' . $OSFLSymb; curl_setopt ($NewCurlHandle, CURLOPT_URL, $NewUrl); curl_setopt($NewCurlHandle, CURLOPT_SSL_VERIFYPEER, false); curl_setopt_array( $NewCurlHandle, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => 'gzip, deflate', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 30, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_HTTPHEADER => [ 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0', 'Accept: application/json, text/plain, */*', 'Accept-Language: en-US,en;q=0.5', 'Origin: https://www.otcmarkets.com', 'Connection: keep-alive', 'Referer: https://www.otcmarkets.com/stock/' . $OSFLSymb . '/profile', 'Sec-Fetch-Dest: empty', 'Sec-Fetch-Mode: cors', 'Sec-Fetch-Site: same-site', 'Cache-Control: max-age=0' ], ] ); if ($UseProxy) { // 可选:如需稳定请求,可固定代理而非随机切换 $CurrProxy = $ProxArr[array_rand($ProxArr)]; curl_setopt($NewCurlHandle, CURLOPT_PROXY, $CurrProxy); curl_setopt($NewCurlHandle, CURLOPT_PROXYUSERPWD, $ProxyAuth); curl_setopt($NewCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); } $CurlPageNew = curl_exec( $NewCurlHandle ); $HTTPStatus = curl_getinfo($NewCurlHandle, CURLINFO_HTTP_CODE); echo "<P>Attempt " . ($i+1) . " - HTTP Code: $HTTPStatus"; if ($HTTPStatus == 400 && !empty($CurlPageNew)) { echo "<BR>400 Error Details: " . htmlspecialchars($CurlPageNew); } echo "<BR>Proxy: " . ($UseProxy ? $CurrProxy : 'None'); $cURLError = curl_error($NewCurlHandle); echo "<BR>cURL Error: $cURLError"; if ($HTTPStatus == 200) { $successResponse = $CurlPageNew; curl_close($NewCurlHandle); break; } curl_close($NewCurlHandle); if ($i < $retryCount) { sleep($retryDelay); echo "<BR>Retrying after $retryDelay seconds...<BR>"; } } return $successResponse; }
内容的提问来源于stack exchange,提问作者Zardiw
相关产品推荐
相关产品推荐

