使用PHP爬取议会网站遭遇‘Invalid Checksum format’错误求助
解决Oracle APEX表单提交的Checksum格式错误问题
我在做一个爬取当地议会网站垃圾收集日期的业余项目,用PHP+Curl模拟表单提交,但遇到了头疼的问题:把代码里打印出的变量手动复制到Postman里提交能成功,但直接跑PHP代码就返回Checksum format error。我怀疑是$protected变量的问题,因为把它换成手动复制的静态值代码就能正常运行。
原代码
<?php function curlResponseHeaderCallback($ch, $headerLine) { global $cookies; if (preg_match('/^Set-Cookie:\s*([^;]*)/mi', $headerLine, $cookie) == 1) $cookies[] = $cookie; return strlen($headerLine); // Needed by curl } function get_string_between($string, $start, $end){ $string = ' ' . $string; $ini = strpos($string, $start); if ($ini == 0) return ''; $ini += strlen($start); $len = strpos($string, $end, $ini) - $ini; return substr($string, $ini, $len); } $cookies = Array(); $ch = curl_init('https://iweb.itouchvision.com/portal/f?p=customer:BIN_DAYS:::NO:RP:UID:625C791B4D9301137723E9095361401AE8C03934'); // Ask for the callback. curl_setopt($ch, CURLOPT_HEADERFUNCTION, "curlResponseHeaderCallback"); curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); $result = curl_exec($ch); curl_close($ch); $protected = get_string_between($result, 'id="pPageItemsProtected" value="', '"'); $arr = explode('set-cookie: ORA_WWV_APP_1030=', $cookies[0][0]); $cookie = $arr[1]; $p_flow_id = get_string_between($result, 'name="p_flow_id" value="', '" id="pFlowId"'); $p_flow_step_id = get_string_between($result, 'name="p_flow_step_id" value="', '" id="pFlowStepId" '); $p_instance = get_string_between($result, 'name="p_instance" value="', '" id="pInstance"'); $p_page_submission_id = get_string_between($result, '<input type="hidden" name="p_page_submission_id" value="', '" id="pPage'); $P0_PWD_POLICY_LONG = get_string_between($result, 'data-for="P0_PWD_POLICY_LONG" value="', '">'); $P0_PWD_POLICY_NO_SPACE = get_string_between($result, 'data-for="P0_PWD_POLICY_NO_SPACE" value="', '">'); $P0_PWD_POLICY_VALID_PUNCT = get_string_between($result, 'data-for="P0_PWD_POLICY_VALID_PUNCT" value="', '">'); $P0_PWD_POLICY_REPEAT_CHAR = get_string_between($result, 'data-for="P0_PWD_POLICY_REPEAT_CHAR" value="', '">'); $P0_PWD_POLICY_USERNAME = get_string_between($result, 'data-for="P0_PWD_POLICY_USERNAME" value="', '">'); $P0_PWD_POLICY_ALPHANUM = get_string_between($result, 'data-for="P0_PWD_POLICY_ALPHANUM" value="', '">'); $P153_CAL_MSG = get_string_between($result, 'data-for="P153_CAL_MSG" value="', '">'); $P153_ZABY = get_string_between($result, 'data-for="P153_ZABY" value="', '">'); $P153_USER_AGENT = get_string_between($result, 'data-for="P153_USER_AGENT" value="', '">'); $P153_SYSDATE = get_string_between($result, 'name="P153_SYSDATE" maxlength="" size="30" value="', '" autocomplete="off">'); echo 'p_flow_id' . $p_flow_id . ' </br> '; echo 'p_flow_step_id' . $p_flow_step_id . ' </br> '; echo 'p_instance' . $p_instance . ' </br> '; echo 'p_page_submission_id' . $p_page_submission_id . ' </br>'; echo 'cookie' . $cookie . '</br>'; echo '$protected' . $protected . '</br>'; $p_json = '{"salt":"'.$p_page_submission_id.'","pageItems":{"itemsToSubmit":[{"n":"P0_LANGUAGE","v":"EN"},{"n":"P0_PWD_POLICY_LONG","v":"","ck":"'.$P0_PWD_POLICY_LONG.'"},{"n":"P0_PWD_POLICY_NO_SPACE","v":"","ck":"'.$P0_PWD_POLICY_NO_SPACE.'"},{"n":"P0_PWD_POLICY_VALID_PUNCT","v":"","ck":"'.$P0_PWD_POLICY_VALID_PUNCT.'"},{"n":"P0_PWD_POLICY_REPEAT_CHAR","v":"","ck":"'.$P0_PWD_POLICY_REPEAT_CHAR.'"},{"n":"P0_PWD_POLICY_USERNAME","v":"","ck":"'.$P0_PWD_POLICY_USERNAME.'"},{"n":"P0_PWD_POLICY_ALPHANUM","v":"","ck":"'.$P0_PWD_POLICY_ALPHANUM.'"},{"n":"P153_POST_CODE","v":"ta1 1aa"},{"n":"P153_CAL_MSG","v":"","ck":"'.$P153_CAL_MSG.'"},{"n":"P153_ZABY","v":"","ck":"'.$P153_ZABY.'"},{"n":"P153_TEMP","v":""},{"n":"P153_USER_AGENT","v":"","ck":"'.$P153_USER_AGENT.'"},{"n":"P153_SYSDATE","v":"'.$P153_SYSDATE.'"}],"protected":"'.$protected.'","rowVersion":"","formRegionChecksums":[]}}'; echo $p_json; $url = 'https://iweb.itouchvision.com/portal/wwv_flow.accept'; $data = array('p_json' => $p_json, 'p_flow_id' => $p_flow_id, 'p_flow_step_id' => $p_flow_step_id, 'p_instance' => $p_instance, 'p_page_submission_id' => $p_page_submission_id, 'p_request' => 'SEARCH', 'p_reload_on_submit' => 'A'); $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded')); curl_setopt($ch, CURLOPT_HTTPHEADER, array("Cookie: ORA_WWV_APP_1030=" . $cookie)); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $response = curl_exec($ch); echo $response; curl_close($ch);
问题根源
你的get_string_between函数在提取变量时,可能带回了不可见的空白字符(比如换行、制表符、首尾空格),或者变量在手动拼接JSON时出现了转义错误。尤其是$protected这类校验值,哪怕多一个不可见字符,都会导致校验失败。
修复步骤
- 清理提取的变量:对所有从HTML中提取的变量做
trim()处理,去除首尾空白字符:
$protected = trim(get_string_between($result, 'id="pPageItemsProtected" value="', '"')); $p_flow_id = trim(get_string_between($result, 'name="p_flow_id" value="', '" id="pFlowId"')); $p_flow_step_id = trim(get_string_between($result, 'name="p_flow_step_id" value="', '" id="pFlowStepId" ')); $p_instance = trim(get_string_between($result, 'name="p_instance" value="', '" id="pInstance"')); $p_page_submission_id = trim(get_string_between($result, '<input type="hidden" name="p_page_submission_id" value="', '" id="pPage')); // 其他所有提取的变量都加上trim()
- 修复Curl的HTTP头设置:连续两次调用
CURLOPT_HTTPHEADER会覆盖前一次的设置,导致Content-Type头丢失,合并HTTP头:
curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/x-www-form-urlencoded', "Cookie: ORA_WWV_APP_1030=" . $cookie ));
- 避免手动拼接JSON:手动拼接容易出现转义错误,改用
json_encode生成标准JSON:
$pageItems = [ ["n"=>"P0_LANGUAGE","v"=>"EN"], ["n"=>"P0_PWD_POLICY_LONG","v"=>"","ck"=>$P0_PWD_POLICY_LONG], ["n"=>"P0_PWD_POLICY_NO_SPACE","v"=>"","ck"=>$P0_PWD_POLICY_NO_SPACE], ["n"=>"P0_PWD_POLICY_VALID_PUNCT","v"=>"","ck"=>$P0_PWD_POLICY_VALID_PUNCT], ["n"=>"P0_PWD_POLICY_REPEAT_CHAR","v"=>"","ck"=>$P0_PWD_POLICY_REPEAT_CHAR], ["n"=>"P0_PWD_POLICY_USERNAME","v"=>"","ck"=>$P0_PWD_POLICY_USERNAME], ["n"=>"P0_PWD_POLICY_ALPHANUM","v"=>"","ck"=>$P0_PWD_POLICY_ALPHANUM], ["n"=>"P153_POST_CODE","v"=>"ta1 1aa"], ["n"=>"P153_CAL_MSG","v"=>"","ck"=>$P153_CAL_MSG], ["n"=>"P153_ZABY","v"=>"","ck"=>$P153_ZABY], ["n"=>"P153_TEMP","v"=>""], ["n"=>"P153_USER_AGENT","v"=>"","ck"=>$P153_USER_AGENT], ["n"=>"P153_SYSDATE","v"=>$P153_SYSDATE] ]; $p_json_data = [ "salt" => $p_page_submission_id, "pageItems" => [ "itemsToSubmit" => $pageItems, "protected" => $protected, "rowVersion" => "", "formRegionChecksums" => [] ] ]; $p_json = json_encode($p_json_data);
- 优化Cookie管理:手动解析Cookie容易出错,改用Curl的
CURLOPT_COOKIEJAR和CURLOPT_COOKIEFILE自动管理:
// 第一次请求保存Cookie到临时文件 $cookie_file = tempnam(sys_get_temp_dir(), 'cookie'); $ch = curl_init('https://iweb.itouchvision.com/portal/f?p=customer:BIN_DAYS:::NO:RP:UID:625C791B4D9301137723E9095361401AE8C03934'); curl_setopt($ch, CURLOPT_HEADERFUNCTION, "curlResponseHeaderCallback"); curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file); $result = curl_exec($ch); curl_close($ch); // 第二次请求加载Cookie $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded')); curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $response = curl_exec($ch); curl_close($ch); unlink($cookie_file); // 用完删除临时文件
验证方法
每次提取变量后,用var_dump($protected)查看变量的实际内容和长度,对比手动复制的静态值,确保完全一致(包括字符数、不可见字符)。
内容的提问来源于stack exchange,提问作者Spike
相关产品推荐
相关产品推荐

