如何在单次CURL请求中捕获随机Token并提交POST数据?
解决单次流程中捕获页面Token并提交POST数据的问题
这个问题很常见——服务器生成的Token通常和用户会话(Session)绑定,两次独立的curl请求会触发新的会话,导致之前拿到的Token失效。要解决这个问题,你需要把「获取Token」和「提交POST数据」放在同一个curl会话里,保持会话连贯性,这样就能用刚拿到的有效Token提交请求了。
核心思路
- 初始化一个curl会话,启用Cookie持久化,确保GET和POST请求共享同一个会话。
- 发起GET请求,获取包含Token的页面HTML。
- 从HTML中提取目标隐藏输入框的Token值(推荐用DOM解析,比正则更可靠)。
- 构造POST请求数据,包含提取到的Token和其他表单字段。
- 在同一个curl会话中切换为POST模式,提交数据并获取响应。
完整PHP代码实现
<?php // 替换为你的目标页面URL(包含Token的页面) $targetPageUrl = 'https://your-target-site.com/page-with-token'; // 替换为POST提交的处理URL(通常和GET页面一致,或单独的提交接口) $submitUrl = 'https://your-target-site.com/submit-form'; // 初始化curl会话 $ch = curl_init(); // 基础curl配置:保持Cookie、返回响应内容、跟随重定向 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 启用Cookie存储,确保会话一致(cookie.txt会自动创建,需确保脚本有写入权限) curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookie.txt'); curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookie.txt'); // 模拟浏览器请求头,避免被服务器拦截 curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36', 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' ]); // 第一步:GET请求获取页面内容 curl_setopt($ch, CURLOPT_URL, $targetPageUrl); $htmlContent = curl_exec($ch); if (curl_errno($ch)) { die("GET请求失败:" . curl_error($ch)); } // 第二步:提取Token - 推荐用DOM解析(更稳定,不受HTML属性顺序影响) $token = ''; $dom = new DOMDocument(); // 抑制HTML解析警告(兼容不规范的页面代码) libxml_use_internal_errors(true); $dom->loadHTML($htmlContent); libxml_clear_errors(); // 通过ID查找隐藏输入框 $clippingInput = $dom->getElementById('clipping'); if ($clippingInput) { $token = $clippingInput->getAttribute('value'); } if (empty($token)) { die("无法提取Token,请检查页面结构是否变化"); } // 第三步:构造POST提交的数据(根据实际表单字段调整) $postFields = [ 'clipping' => $token, // 注意:这里的键要和表单中该输入框的name属性一致(不是id) 'username' => 'your-username', 'password' => 'your-password', // 添加其他需要提交的字段 ]; // 转换为URL编码的POST字符串 $postData = http_build_query($postFields); // 第四步:切换为POST模式,提交数据 curl_setopt($ch, CURLOPT_URL, $submitUrl); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); $postResponse = curl_exec($ch); if (curl_errno($ch)) { die("POST请求失败:" . curl_error($ch)); } // 关闭curl会话 curl_close($ch); // 处理并输出响应 echo "POST请求响应结果:<br>" . htmlspecialchars($postResponse); ?>
关键细节说明
- 会话一致性:
CURLOPT_COOKIEJAR和CURLOPT_COOKIEFILE是核心,它们让curl在两次请求中共享Cookie(比如Session ID),服务器会认为是同一个用户会话,Token自然有效。 - DOM解析vs正则:用
DOMDocument通过ID查找元素比正则表达式更可靠——如果页面HTML的属性顺序变化(比如type="hidden"放在id前面),正则可能失效,但DOM解析不受影响。如果一定要用正则,也可以用这个兼容写法:preg_match('/<input[^>]+id="clipping"[^>]+value="([^"]+)"/i', $htmlContent, $matches); $token = isset($matches[1]) ? $matches[1] : ''; - 请求头模拟:添加浏览器样式的User-Agent和Accept头,避免服务器把请求识别为爬虫而拒绝。
- 错误处理:加入
curl_errno和curl_error可以快速定位请求中的问题(比如网络错误、URL无效等)。
对你原函数的改进
如果你想继续使用自己的getStr2函数,也可以调整为更稳定的版本:
function getStr2($content, $startStr, $endStr) { $startPos = strpos($content, $startStr); if ($startPos === false) return ''; $startPos += strlen($startStr); $endPos = strpos($content, $endStr, $startPos); if ($endPos === false) return ''; return substr($content, $startPos, $endPos - $startPos); } // 使用方式 $token = getStr2($htmlContent, '<input id="clipping" type="hidden" value="', '"');
内容的提问来源于stack exchange,提问作者Geraldo Magela
相关产品推荐
相关产品推荐

