就像网上有些快递查询系统一样,在本地显示EMS验证码的图片,在表单中输入单号和验证码 向EMS服务器进行模拟浏览器的查询,并取得查询结果。这个功能我用CURL已经实现了。 但是我现在用的空间服务器不支持CURL,只能用其他的办法实现了。就选择了snoopy类,这个在我的空间是能用的。EMS的查询验证机制是这样的:
一 在表单页 生成随机码,验证码的表单输入框的名称也是随机变化
二 验证码和SESSIONID
表单页:http://www.ems.com.cn/qcgzOutQueryAction.do?reqCode=gotoSearch
处理页:http://www.ems.com.cn/qcgzOutQueryAction.do
验证码:http://www.ems.com.cn/servlet/ImageCaptchaServlet模拟查询流程
1 连接表单页 取得随机码 验证码表单名称 和 JSESSIONID
2 带着这个JSESSIONID连接验证码页,显示炎症码
3 带着JSESSIONID和提交数据模拟POST处理页,取得返回结果。共三个文件 ems.php emscode.php emspost.php<?PHP
//ems.php
session_start(); 
include("Snoopy.class.php"); 
$url = "http://www.ems.com.cn/qcgzOutQueryAction.do?reqCode=gotoSearch"; 
$url2 = "http://www.ems.com.cn/qcgzOutQueryAction.do"; 
  
$snoopy = new Snoopy;   
$snoopy->fetch($url); //获取所有内容   
//print_r($snoopy->headers);
$c =$snoopy->headers[5];
preg_match_all('|Set-Cookie: (.*);|U', $c, $mycookie); 
$_SESSION['sid'] = $mycookie[1][0];$content = $snoopy->results;
preg_match('/name="myEmsbarCode" value="(.*)"/i',$content,$myEmsbarCode);
preg_match('/<input name="(.*)" type="text" maxlength="5"/i',$content,$imgname);
$myEmsbarCode =$myEmsbarCode[1];
$imgname = $imgname[1];
?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>中国邮政集团公司-全程跟踪查询</title>
<script>
<!--
function checkdata(v){ 
        if(v){
                var dzzmailnum = this.document.form1.DzzmailNum.value;
                if(''==dzzmailnum){
                        alert("请输入查询单证照号!");
                        form1.DzzmailNum.focus();
                        return false;
                }else{
                        trackagainform.style.display = "none";
                        progress.style.display = "";
                        return true;
                }
        }
        
    var bool;
    var str;
    var len;
    var i;
    var charsets;
    
    bool="true";
    charnum="0123456789";
    charsets="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
    str=this.document.form1.mailNum.value;
    
    len=str.length;
        
    if (str=="") {
        bool="false1";
    }
    else{
        if (len!=13) {
            bool="false2";
        }
        else {  
            if ((charsets.indexOf(str.substring(0,1))==-1)||(charsets.indexOf(str.substring(1,2))==-1)) {bool="false3";}
            for(i=2;i<11;i++) {
                //alert(charnum.indexOf(ch,0));
                if (charnum.indexOf(str.substring(i,i+1),0)<0) {bool="false4";}
            }
            if ((charsets.indexOf(str.substring(11,12))==-1)||(charsets.indexOf(str.substring(12,13))==-1)) {bool="false5";}
                //        if(4!=document.form1.checknumone.value.length) {bool="false6";}                                         
        }
    }
    
    if (bool!="true") {
        if (bool=="false1") {alert("用户查询的输入值不能为空!");bool="true";return false;}
        if (bool=="false2") {alert("输入值必须为13位!");bool="true";return false;}
        if (bool=="false3") {alert("输入值前两位必须为字符!");bool="true";return false;}
        if (bool=="false4") {alert("输入值第3位至第11位必须为数字123456789!");bool="true";return false;}
        if (bool=="false5") {alert("输入值后两位必须为字符!");bool="true";return false;}
        //        if (bool=="false6") {alert("请输入4位查询验证码!");return false;}   
    }
    else {
                form1.submit();
    }           
}
-->
</script>
</head>
<body>
<form name="form1" method="post" action="emspost.php" onSubmit="return checkdata(false);">
<table border="1" cellpadding="0" cellspacing="0" width="300" align="center">
  <tr>      
      <td height=25 colspan="2" align="center"><b>EMS运单查询系统</b></td>
  </tr>
  <tr>
     <td width="90" height=25  align="right">运单号:</td>
     <td colspan="2">
        <input type="text" name="mailNum" value="EE082209212CS"/>
        <input type="hidden" name="reqCode" value="browseBASE"/>        
        <input type="hidden" name="myEmsbarCode" value="<?php echo $myEmsbarCode ?>"/>        
     </td>
   </tr>
   <tr>
      <td width="90" height=25  align="right">验证码:</td>
      <td align="left">
        <img align="absmiddle" src="emscode.php" style="cursor:pointer;" onClick="this.src='emscode.php';" />
        <input type="text" name="verifycode" size="5" />
        <INPUT type="hidden" name="imgname" value="<?php echo $imgname; ?>" />
      </td>
   </tr>
   <tr>
      <td align="center" colspan="2"><input type="submit" name="Submit2" value=" 查 询 ">
      </td>
   </tr>
</table>
</form>
</body>
</html>
第二个取验证码的emscode.php<?PHP
session_start();
$url = "http://www.ems.com.cn/servlet/ImageCaptchaServlet";   include("Snoopy.class.php");   
$snoopy = new Snoopy;   
$snoopy->agent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)"; //伪装浏览器   
$snoopy->referer = "http://www.ems.com.cn"; //伪装来源页地址 http_referer
$snoopy->rawheaders["COOKIE"]=$_SESSION['sid']; //伪装sessionid 
$snoopy->rawheaders["Pragma"] = "no-cache"; //cache 的http头信息   
$snoopy->rawheaders["X_FORWARDED_FOR"] = "211.156.193.130"; //伪装ip   
$snoopy->fetch($url); //模拟浏览器连接并取得返回结果  
echo($snoopy->results);?>第三部分emspost.php<?php
  //提交数据
  session_start();
  $sid = $_SESSION['sid'];
  $url = "http://www.ems.com.cn/qcgzOutQueryAction.do?reqCode=gotoSearch";  //填写信息页
  $url2 = "http://www.ems.com.cn/qcgzOutQueryAction.do";  //提交判断页
$posts = array();
$posts['reqCode'] = 'browseBASE';       
$posts['myEmsbarCode'] = $_POST["myEmsbarCode"]; 
$posts['mailNum'] = $_POST["mailNum"]; 
$posts[$_POST["imgname"]] = $_POST["verifycode"]; 
include("Snoopy.class.php");   
$snoopy = new Snoopy;   
$snoopy->agent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)"; //伪装浏览器   
$snoopy->referer = $url; //伪装来源页地址 http_referer
$snoopy->rawheaders["COOKIE"]=$_SESSION['sid']; //伪装sessionid 
$snoopy->rawheaders["Pragma"] = "no-cache"; //cache 的http头信息   
$snoopy->rawheaders["X_FORWARDED_FOR"] = "211.156.193.130"; //伪装ip   
$snoopy->submit($url2,$posts); //模拟浏览器提交并取得返回结果  
print_r($snoopy->results);
?>
为什么每次查询的结果都是“停留页面时间过长”、“校验查询标准失败,请重新输入”等等。 请高说作答。

解决方案 »

  1.   

    你要把emscode.php这一步,获取的cookie在验证的时候,发送回去你curl实现的时候,有没有这样?
      

  2.   

    这个工作比较复杂,您要用录包工具耐心地分析,看看是不是少了什么 COOKIE 什么参数之类的,建议操作步骤:
    1,先找个录包工具,在 EMS.COM.CN 上操作几次,然后分析其发包和收到的HTTP包。
    2,仔细耐心地分析。
    3,动手写代码,一般用 SOCKETOPEN 之类的可以控制超时的函数来操作,否则很容易引起雪崩,导致你自己的网站都很慢。
      

  3.   

    回:amani11流程是这样的:
    1 连接表单页,取得相应的随机码同时也取的cookie
    2 连接验证码页,并且把刚才取得的cookie也发送过去(使得服务器不在产生新的sessionid,这一点也确实做到了,在分析验证码的header时,确实已经不再返回新的sessionid了)
    3 把表单所需要的值和sessionid一起发送给验证页,并取得返回值。(这一部分返回的header也分析了,也没有JSESSIONID,可见服务器是认可发给他的sessionid的)我在用curl实现的时候很轻松的获取正确的返回结果,可是用snoopy却总是返回“停留页面时间过长”、“校验查询标准失败,请重新输入”等等curl的流程和snoopy的是一样的,只是在存储cookie上略有区别,curl是标准的网景的cookie格式;snoopy是取得header中的JSESSIONID并存入session['sid']中,在页面中传递。我观察两个方法虽不同,但取得的JSESSIONID是一样的。
      

  4.   

    回 xingworld
    我用抓包工具仔细分析过。
    JSESSIONID和表单页的随机码是一一对应的,所以在取的验证码页的时候不能让他产生新的JSESSIONID,否则先前取得的随机码和JSESSIONID就对不上号了,验证就会失败。
      

  5.   

    我是说“第二个取验证码的emscode.php”这一个文件,你要重新获取cookie,,snoopy我用不多,但我看了下,感觉你没改变cookie,这一步是关键吧?
      

  6.   

    CURL可以轻松设置cookie保存文件,读文件即可,现在这个你保存在session里,,,,可能获取验证码后,cookie值变化了
      

  7.   

    to  amani11
    首先感谢你的回复。在取验证码的时候 不能让他产生新的SESSIONID,否则先前的表单页获取的随机码就失效了。
    我在用curl的时候也是这么做的。原理都一样。因为我对snoopy也不是多熟悉,所以不理解我的代码为何最后取不到正确的答案呢
      

  8.   

    我看了一下,的确,cookie是不变的。我的意思是,如果有变化,是需要重新取值的
    我以前做过类似东西,有过操作,不论变不变,我都重新取值,完全模拟当然这里不需要
    另外,把你后面两文件的伪造ip全部去掉,我测试成功了
      

  9.   

     感谢 amani11确实是那个伪造的IP出了问题。
    结贴给分
      

  10.   

    还有那个 no-catch 也是罪魁
    在emscode和emspost页,必须要带着catch里的sessionid来post值的,怎么能no-catch呢呵呵,当初就没看到这里。
      

  11.   

    伪造IP其实没关系,关键是no-catch是不能有的。
      

  12.   

    我也出现这个问题 sessionid和验证码还有myEmsbarCode这3个要怎么联系在一起啊???
      

  13.   

    用你的代码 去掉cookie后还是提示:
    信息提示:页面停留时间过长导致邮件查询标志丢失,请刷新查询页面。
      

  14.   

    那个emscode.php和emspost.php后面两个cookie不能去掉  去掉后验证码和post都和之前的页面分开了no-cache要去掉才可以
      

  15.   

    还是有朋友不了解哈
    好吧 ,我把我的源码公开下载。演示地址:http://life169.web-141.com/ems/ems.php下载地址:http://download.csdn.net/source/2570680
      

  16.   

    可是我在测试的过程中却遇到一个问题,JsessionID,经常会取不到,session_start();  
    include("Snoopy.class.php");  
    $url = "http://www.ems.com.cn/qcgzOutQueryNewAction.do?reqCode=gotoSearch";  
    $url2 = "http://www.ems.com.cn/qcgzOutQueryNewAction.do";  $snoopy = new Snoopy;
    $snoopy->fetch($url); //获取所有内容  
    print_r($snoopy->headers);打印头信息,返回的是:
    Array ( [0] => HTTP/1.1 200 OK [1] => Content-Type: text/html; charset=GB2312 [2] => X-Powered-By: Servlet/2.4 JSP/2.0 [3] => Accept-Ranges: bytes [4] => Connection: close [5] => Date: Tue, 15 Mar 2011 11:24:53 GMT [6] => Age: 1876 [7] => Content-Length: 22804 )  正确的返回信息应当是:
    Array ( [0] => HTTP/1.1 200 OK [1] => Cache-Control: no-cache="set-cookie" [2] => Connection: close [3] => Date: Fri, 11 Mar 2011 07:42:04 GMT [4] => Content-Type: text/html; charset=GB2312 [5] => Set-Cookie: JSESSIONID=N5SMs1dMW1Wy8LJ48Z1ZhP1p2kS1GSgY5twTNxcYpz1MvgYZk0LQ!195316506; path=/ [6] => X-Powered-By: Servlet/2.4 JSP/2.0 )  前面返回的里面没有JSESSIONID, 导致在后面的查询中返回不了查询结果, 我已经整了2天了,在网上查了大量资料,还是没有头绪,现在有一个奇怪现象,如果在浏览器打开ems.php页面,过一段时间再刷新页面后,又会返回正确的headers信息,但是过一阵又不能正确返回,为什么会出现这种情况, 希望有大虾能帮我看看是怎么回事? 急
      

  17.   


    我这个也是,有时候能取得到cookie信息,有什么时候取不到。高手帮忙解决啊!!!!
      

  18.   

    curl 和 snoopy 哪个更好用啊?