微信公众平台图书馆查询开发记录

摘要:这篇文章记录图书馆查询微信公众平台开发全过程,主要内容包括:(1)Fidder2解析图书馆登入系统过程(2)模拟图书馆登入系统过程(3)机器识别验证码(4)Unicode转UTF-8编码过程(5)微信公众账号的绑定(6)Memcache缓存技术(7)正则表达式

这是一篇记录我的第一个微信应用——福建农林大学图书馆服务的开发记录,应用架设在Sina App Engine,开发目标是解决手机登入web查看图书不便的问题,并提供响应的服务功能,比如(1)图书5天内图书到期的提醒(2)图书的查询服务(3)图书借阅的查询服务(4)暂时图书馆没有藏书的到书提醒(5)免验证码登陆,算是对自己多天折腾的总结并提供有相关需求的朋友一些思路。

Fidder2解析图书馆登入系统过程

Fidder2介绍

Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的“进出”Fiddler的数据(指cookie,html,js,css等文件,这些都可以让你胡乱修改的意思)。 Fiddler 要比其他的网络调试器要更加简单,因为它不仅仅暴露http通讯还提供了一个用户友好的格式。

利用Fidder可以方便的从数据连接中扒取链接的信息,即使是表单是通过_POST,我们也能够很方便的从数据流中截取用户发送的信息,从而将机器伪装成人肉对服务器进行请求,实现模拟登入。

通过Fidder查看图书馆登入过程

首先,打开Fidder,开始对通信进行监听,登入我们的目标站点——福建农林大学图书馆的登入页面http://210.34.85.114:8080/reader/login.php,输入学号,密码和验证码(正不正确不重要,这里主要查看数据的发送格式),选择证件号,点击登入,打开Fidder,选择验证服务器的urlhttp://210.34.85.114:8080/reader/redr_verify.php,在Inspectors下的Raw标签下可以看到发送的信息,如下

POST http://210.34.85.114:8080/reader/redr_verify.php HTTP/1.1
Host: 210.34.85.114:8080
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://210.34.85.114:8080/reader/login.php
Cookie: PHPSESSID=ordh88pv46g9jco23d32pvqer3
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 70

number=3125002076&passwd=147qj9&captcha=1173&select=cert_no&returnUrl=

发送的目标地址

POST http://210.34.85.114:8080/reader/redr_verify.php HTTP/1.1

用户进行浏览器的头文件为:

User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate

用户发送的信息格式为:

number=3125002076&passwd=147qj9&captcha=1173&select=cert_no&returnUrl=

后头我们将对这些信息进行封装,通过curl POST至目标地址http://210.34.85.114:8080/reader/redr_verify.php HTTP/1.1
由于本文涉及到验证码的机器识别,所以我们还需要获取生成验证码的服务器地址,Fidder中我们可以找到生成验证码的urlhttp://210.34.85.114:8080/reader/captcha.php,返回数据格式为captcha.gif

模拟图书馆登入系统过程

//*-----------------------------------WXLibSearch.php--------------------------------------*//
<?php
include ('Valite.php'); //验证码识别类
include ('u2utf82gb.php');  //NCR转GB2312类
//获取用户键入的信息
function getborrowedbookinfo ($keyword){

    /*
    *正则表达式检测用户输入信息是否为"学号(10位)+密码"
    *如果是则进行模拟登入获取借阅信息
    *如果输入错误则返回"请按照格式输入学号和密码"
    */

    if (preg_match_all("/^(\d{10})\+\S+/",$keyword,$info")){
        
        //切割字符串,学号为前10位(0-9),密码为第11后(10-)
        $num=substr($info,0,9); //学号
        $pass=substr($info,11); //密码
        
        
        //获取验证码url
        $url = "http://210.34.85.114:8080/reader/captcha.php";
        //保存cookie在当前路径下的"valid.tmp"文件之中,跨页面传递用户信息
        $cookie = dirname(__FILE__)."/valid.tmp";
         
        //curl访问验证码图片网址,把返回的cookie保存为valid.tmp文件
        $curl = curl_init($url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);  //设定返回 的数据是否自动显示
        curl_setopt($curl, CURLOPT_COOKIEFILE, $cookie);
        curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie); // 把返回来的cookie信息保存在$cookie文件中
        $data = curl_exec($curl);
        curl_close($curl);
         
        //保存验证码图片
        $fp = fopen("valid.gif","wb");
        fwrite($fp, $data);
        fclose($fp); 
         
        //识别验证码图片
        $valid = new Valite();
        $valid->setImage("valid.gif");
        $valid->getHec();
        $validCode = $valid->run();
        
        
        //组装数据
        $data = "number=".$num."&passwd=".$pass."&captcha=".$validCode."&select=cert_no&returnUrl=";
        //头文件
        $headers = array(
            "User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0","Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3",
            "Accept-Encoding: gzip, deflate",
            );

        //curl发送数据进行登陆
        $url = "http://210.34.85.114:8080/reader/redr_verify.php";
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);   /设定是否显示头信息
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_COOKIEFILE, $cookie);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl,CURLOPT_POSTFIELDS,$data);
        $src = curl_exec($curl);
        curl_close($curl);
        
        //利用cookie进行借阅图书查询
        $url = "http://210.34.85.114:8080/reader/book_lst.php";
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_COOKIEFILE, $cookie);
        $src = curl_exec($curl);
        curl_close($curl);
        
        //截取出html中的书名
        
        $booknum = preg_match_all('/<a[^>]+>\S+<\/a>/',$src,$bookinfo);
        $hadborrowed = $booknum-23; //当前借阅量
        $bookborrowed = "当前借阅量:$hadborrowed";
        $bookborrowed = iconv('UTF-8', 'GB2312', $bookborrowed);
        $books = "";    //建立容器储存书名
        $n = 0; //用于为标题名前加上标号
        for ($i=23;$i<$booknum;$i++){
        $n = $n+1;  
        $str1 = ""; //建立容器用于储存转化为GB2312的字符,每循环一次数据清空,储存到$books容器之中
            //unicode转化为utf-8
            //通过explode,标记为";"将字符串切割成单个Uincode编码字符
            $bookinf = explode(';',$bookinfo[0][$i]);
            foreach($bookinf as $bookinfx){ //遍历字符串字符进行转码
                $bookinfx = preg_replace('/<[^>]+>/','',$bookinfx); //除去头尾字符的html标签
                $code = substr($bookinfx,3);    //除去16位NCR编码的表示部分"&#x"
                $unicode = hexdec($code);   //16进制转化我10进制
                $string = u2utf82gb($unicode);  //Unicode转GB2312
                $str1 = $str1.$string;  //将转码后的单个字符拼接起来
            }
            $books = $books.$n.".".$str1;   //将每个书名拼接起来并加上标号"$n."
        }
        
        echo $bookborrowed."\n".$books; //输出借阅的图书量加上书名输出测试
    }
}
        
?>

机器识别验证码

验证码识别一般分为以下几个步骤:
一、取出字模
识别验证码,毕竟不是专业的OCR识别,并且,由于各个网站的验证码各不相同,所以,最常见的方法就是就是建立这个验证码的特征码库。去字模时,我们需要多下载几张图片,使这些图片中,包括所有的字符,我们这里的图片里只有数字,所以,只要收集到包括0-9的数字图片即可。

1、多刷新几次验证码,将验证码图片保存起来,要搜集齐0-9的图片,这里我收集了4张验证码才凑齐0~9 10位数字。

验证码
验证码

2、用图片处理软件打开图片,我用的是Photoshop,按住ctrl+可以将图片的视图放大,这样就能很清楚地观察到图片的每个像素。

放大后的验证码
放大后的验证码

可以发现,每个数字的宽是8px,高是10px,数字的间隔是4px,第一个数字左边偏移了6px,顶部偏移了16px。这些数字后面都是要用到的。
3、将每个数字截出来保存为图片,大小为8*10(虽然数字1的宽只占了7个像素,但是还是要截取8个像素的宽度)。

二、图片二值化
二值化就是把图片上的验证数字上每个象素用数字1表示,其它部分用0表示。把要识别的图片,进行二值化,将数据保存到二维数组里,得到图片特征数组。

1、首先要将数字和背景色和干扰色区分开来,用屏幕取色器观察颜色的规律。
可以得出一个结论:背景颜色的R、G、B值都是大于200的,而数字的颜色的R、G、B值的某一项有小于200,因此可以很容易区分。

2、下面的php代码只是为了演示二维数组,为了直观看出数字,所以把1和0改为了0和-:

echo '<br><img src="captcha.gif"><br><br>';
 
getHec("captcha.gif");  //"captcha.gif"中的值为8690
 
function getHec($imagePath) {
    $res = imagecreatefromjpeg($imagePath);
    $size = getimagesize($imagePath);
    
    for ($i = 0; $i < $size[1]; ++$i) {
        for ($j = 0; $j < $size[0]; ++$j) {
            $rgb = imagecolorat($res, $j, $i);
            $rgbarray = imagecolorsforindex($res, $rgb);
            if ($rgbarray['red'] < 200 || $rgbarray['green']<200 || $rgbarray['blue'] < 200) {
                echo "0";
            }else{
                echo "-";
            }
        }
        echo "<br>";
    }
} 

二值化的结果如下图所示:

------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
--------0000--------0000--------0000---------00-------------
-------00--00------00--00------00--00-------0000------------
------00----00----00----0-----00----00-----00--00-----------
-------00--00-----00----------00----00----00----00----------
--------0000------00-000-------00--000----00----00----------
-------00--00-----000--00-------000-00----00----00----------
------00----00----00----00----------00----00----00----------
------00----00----00----00-----0----00-----00--00-----------
-------00--00------00--00------00--00-------0000------------
--------0000--------0000--------0000---------00-------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------

如果图片的背景颜色比较复杂,处理方法也是一样的,总能找到临界值来区分,具体要靠自己观察了。

三、数字字模二值化
计算出每个数字字模的二值化的数据,记录下这些数据,当作key即可。

1、将0-9的数字字模图片进行二值化,逐个取出图片的像个像素的颜色,然后获取每个像素的R、G、B值,再进行判断,代码如下:

for ($i = 0; $i < 10; $i++) {
    echo "'$i'=>'";
    echo getHec("$i.jpg")."',<br>";
}
 
function getHec($imagePath) {
    $res = imagecreatefromjpeg($imagePath);
    $size = getimagesize($imagePath);
    
    for ($i = 0; $i < $size[1]; ++$i) {
        for ($j = 0; $j < $size[0]; ++$j) {
            $rgb = imagecolorat($res, $j, $i);
            $rgbarray = imagecolorsforindex($res, $rgb);
            if ($rgbarray['red'] < 200 || $rgbarray['green']<200 || $rgbarray['blue'] < 200) {
                echo "1";
            }else{
                echo "0";
            }
        }
    }
}
输出结果:
'0' => '011110100001100001100001100001100001100001100001100001011110',
'1' => '001000111000001000001000001000001000001000001000001000111110',
'2' => '011110100001100001000001000010000100001000010000110011111111',
'3' => '011110100001100001000010001100000010000001100001100001011110',
'4' => '000100000100001100010100100100100100111111000100001100001111',
'5' => '111111100000100000101110110001000001000001100001100001011110',
'6' => '001110010001100000100000101110110001100001100001100001011110',
'7' => '111111100010100010000100000100001000001000001000001000001000',
'8' => '011110100001100001100001011110010010100001100001100001011110',
'9' => '011100100010100001100001100011011101000001000001100010011100',

四、对照样本
把步骤二中的图片特征码和步骤三中的验证码的字模进行对比,得到验证图片上的数字。
算法过程:

1、将图片二值化后的值保存到二维数组里。

2、通过循环,求出每一个数字的位置,要用到前面得到的数字的宽、高、间隔、左边偏移、顶部偏移。
例如:第i个数字左边偏移 =(数字宽 + 间隔)* i + 左边偏移。

3、知道了数字的偏移位置,就可以计算出数字在二维数组里的位置,通过循环将数字的6*10=60个数据取出来拼接在一起,就形成了与数字字模类似的字符串。

4、将字符串与每一个字模的字符串比较,求其相似度,取最高的相似度对应的数字,或者相似度达到95%以上就可以断定是某个数字。

5、识别结果如下:

识别结果
识别结果

使用目前这种方法,对验证码的识别基本上可以做到100%。
通过以上步骤,您可能说了,并没有发现如何取出干扰素啊!其实取出干扰素的方法很简单,干扰素的一个重要特征是,不能影响验证码的显示效果,所以制作干扰素时它的RGB可能低于或者高于某个特定值,比如我给的例子中的图片,干扰素的RGB各项值是不会小于200的,所以,这样我们就很容易去掉干扰素了。

//*-----------------------------------Valite.php--------------------------------------*//
<?php

define('WORD_WIDTH',8); //定义每个字符的高度
define('WORD_HIGHT',10);    //定义每个字的宽度
define('OFFSET_X',6);   //左偏移的像素数
define('OFFSET_Y',16);  //高偏移的像素数
define('WORD_SPACING',4);   //字符间的像素数

class valite
{
    public function setImage($Image)    //打开二维码图片
    {
        $this->ImagePath = $Image;
    }
    
    public function getData()
    {
        return $data;
    }
    
    public function getResult()
    {
        return $DataArray;
    }
    
    public function getHec()    //除去干扰像素的方法
    {
        $res = imagecreatefromgif($this->ImagePath);        //创建图像
        $size = getimagesize($this->ImagePath); //获取图像长宽
        $data = array();
        for($i = 0; $i < $size[1]; ++$i)    //循环遍历宽度
        {
            for($j = 0; $j < $size[0]; ++$j)    //循环遍历长度
            {
                $rgb = imagecolorat($res,$j,$i);    //取得某像素的颜色索引值
                $rgbarray = imagecolorsforindex($res, $rgb);    // 取出red,green,blue 和 alpha 的键名的关联数组
                if($rgbarray['red'] < 160 || $rgbarray['green'] < 160 || $rgbarray['blue'] < 160)   //比较RGB值
                {
                    $data[$i][$j]=1;    //数字输出1
                }else{
                    $data[$i][$j]=0;    //背景输出0
                }
            }
        }
        $this->DataArray = $data;   //返回数据
        $this->ImageSize = $size;
    }
    
    public function run()   //获取每个像素点的坐标
    {
        $result = "";
        $data = array("","","","");
        
        for($i = 0; $i < 4; ++$i)   //循环,切割四个数字
        {
            $x = ($i * (WORD_WIDTH + WORD_SPACING)) + OFFSET_X; //计算每个数字的起始水平像素的值X
            $y = OFFSET_Y;
            for($h = $y; $h < (OFFSET_Y + WORD_HIGHT); ++$h)    //循环获取每个数字的竖直像素值
            {
                for($w = $x; $w < ($x + WORD_WIDTH); ++$w)  //循环获取水平像素值
                {
                    $data[$i] .= $this->DataArray[$h][$w];  //储存每个数字的二值码
                }
            }
        }


        foreach($data as $numKey => $numString) //循环每个数字的二值码
        {
            $max = 0.0;
            $num = 0;
            foreach($this->Keys as $key => $value)  //匹配每个的字模二值码
            {
                $percent = 0.0; //初始相似百分比
                similar_text($value, $numString, $percent); //计算两个字符串的相似度(以百分比计)
                if(intval($percent) > $max) //当前相似度与前一次循环的相似度比较,如果比前一次相似度高则
                {
                    $max = $percent;    //储存最大相似度百分比
                    $num = $key;    //储存最大百分比的数字
                    if(intval($percent) > 95)   //将字符串与每一个字模的字符串比较,求其相似度,取最高的相似度对应的数字,或者相似度达到95%以上就可以断定是某个数字
                        break;
                }
            }
            $result .= $num;
        }
        $this->data = $result;

        return $result;
    }

    public function Draw()  //按顺序取出每个相熟点的值(0/1)
    {
        for($i = 0; $i < $this->ImageSize[1]; ++$i)
        {
            for($j = 0; $j < $this->ImageSize[0]; ++$j)
            {
                echo $this->DataArray[$i][$j];
            }
            echo "\n";
        }
    }
    
    public function __construct()   //二值化的每个数字
    {
        $this->Keys = array(
            '0'=>'00011000001111000110011011000011110000111100001111000011011001100011110000011000',
            '1'=>'00011000001110000111100000011000000110000001100000011000000110000001100001111110',
            '2'=>'00111100011001101100001100000011000001100000110000011000001100000110000011111111',
            '3'=>'01111100110001100000001100000110000111000000011000000011000000111100011001111100',
            '4'=>'00000110000011100001111000110110011001101100011011111111000001100000011000000110',
            '5'=>'11111110110000001100000011011100111001100000001100000011110000110110011000111100',
            '6'=>'00111100011001101100001011000000110111001110011011000011110000110110011000111100',
            '7'=>'11111111000000110000001100000110000011000001100000110000011000001100000011000000',
            '8'=>'00111100011001101100001101100110001111000110011011000011110000110110011000111100',
            '9'=>'00111100011001101100001111000011011001110011101100000011010000110110011000111100',
        );
    }
    
    protected $ImagePath;
    protected $DataArray;
    protected $ImageSize;
    protected $data;
    protected $Keys;
    protected $NumStringArray;
}
?>

NRC转UTF-8编码过程

UTF-8的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

下表总结了编码规则,字母x表示可用编码的位。

<table><tr><td>Unicode符号范围(十六进制)</td><td>UTF-8编码方式(二进制)</td></tr>
<tr><td>0000 0000-0000 007F</td><td>0xxxxxxx</td></tr>
<tr><td>0000 0080-0000 07FF</td><td>110xxxxx 10xxxxxx</td></tr>
<tr><td>0000 0800-0000 FFFF</td><td>1110xxxx 10xxxxxx 10xxxxxx</td></tr>
<tr><td>0001 0000-0010 FFFF</td><td>11110xxx 10xxxxxx 10xxxxxx 10xxxxxx</td></tr></table>

下面,还是以汉字“严”为例,演示如何实现UTF-8编码。

已知“严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此“严”的UTF-8编码需要三个字节,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,从“严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,“严”的UTF-8编码是“11100100 10111000 10100101”,转换成十六进制就是E4B8A5。

//*-----------------------------------u2utf82gb.php--------------------------------------*//
<?php
function u2utf82gb($c){
    $str="";
    if ($c < 0x80) {
        $str.=chr($c);  //当NRC编码位于0000 0000-0000 007F区段时,UTF-8编码转换为0xxxxxxx
    } else if ($c < 0x800) {
        $str.=chr(0xC0 | $c>>6);    //当NRC编码位于0000 0080-0000 07FF区段时,UTF-8编码转换为110xxxxx 10xxxxxx
        $str.=chr(0x80 | $c & 0x3F);
    } else if ($c < 0x10000) {
        $str.=chr(0xE0 | $c>>12);   //当NRC编码位于0000 0800-0000 FFFF区段时,UTF-8编码转换为1110xxxx 10xxxxxx 10xxxxxx
        $str.=chr(0x80 | $c>>6 & 0x3F);
        $str.=chr(0x80 | $c & 0x3F);
    } else if ($c < 0x200000) {
        $str.=chr(0xF0 | $c>>18);   //当NRC编码位于0001 0000-0010 FFFF区段时,UTF-8编码转换为11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
        $str.=chr(0x80 | $c>>12 & 0x3F);
        $str.=chr(0x80 | $c>>6 & 0x3F);
        $str.=chr(0x80 | $c & 0x3F);
    }
        return iconv('UTF-8', 'GB2312', $str);
}
?>

通过QR获取借阅信息

<2014/7/25更新>今天灵(nao)机(can)一动想到一个新想法,能否利用当前借阅最下边的二维码来获取图书的有关信息,于是用手机扫了一下二维码,发现真的可以获取图书的借阅信息。

扫描结果
扫描结果

于是查看了一下源码,找到了生成二维码的的地址。

    <img src="../opac/ajax_qr.php?qrcode=6K%2B76ICF44CQMzEyNTAwMjA3NuOAkeeahOWcqOWAn%2BS5puWIiijpopjlkI0v5bqU6L%2BY5pel5pyfKe%2B8mg0KTHVh56iL5bqP6K6%2B6K6hIDIwMTQtMDktMTQgICAgICAgIA0KUEhQ5a6e5L6L57K%2B6YCaIDIwMTQtMDktMTQgICAgICAgIA0KQXJkdWlub%2BS4gOivleWwseS4iuaJiy4y54mILi4gMjAxNC0wOS0xNCAgICAgICAgDQrkuK3lm73lhpnmhI%2FnlLvlhaXpl6jovbvmnb7lraYs56u55a2QLi4gMjAxNC0wOS0xNCAgICAgICAgDQpBbmRyb2lk54Ot6Zeo5bqU55So5byA5Y%2BR6K%2Bm6KejLi4gMjAxNC0wOS0xNCAgICAgICAgDQrkuK3lm73lhpnmhI%2FnlLvlhaXpl6jovbvmnb7lraYs6I2J5pys6Iqx5Y2JLi4gMjAxNC0wOS0xNCAgICAgICAgDQrlkI3lrrblm73nlLvor77loIIu6Iqx6bif56%2BHLi4gMjAxNC0wOS0xNCAgICAgICAgDQpBcmR1aW5v5oqA5pyv5YaF5bmVIDIwMTQtMDktMTQgICAgICAgIA0K55av54uCQW5kcm9pZOiusuS5iS4y54mILi4gMjAxNC0wOS0xNCAgICAgICAgDQpqUXVlcnnlvIDlj5HmioDmnK%2For6bop6MuLiAyMDE0LTA5LTE0ICAgICAgICANCg%3D%3D" border="0" /></a></span>

这个二维码是由http://210.34.85.114:8080/opac/ajax_qr.php这个服务器生成的,后面的qrcode应该是GB2312转UTF-8后url编码产生,这里不去管它。
由于直接利用PHP无法解析二维码,所以调用http://zxing.org/w/decode.jspx这一个网页服务来对二维码解析。
用Fidder分析数据发送,该页面是通过_GET方法http://zxing.org/w/decode?u=后接查询图片的url来传递数据,这里的url是http://210.34.85.114:8080/opac/ajax_qr.php这个服务器生成的二维码的地址。

GET http://zxing.org/w/decode?u=http%3A%2F%2F210.34.85.114%3A8080%2Fopac%2Fajax_qr.php%3Fqrcode%3D6K%252B76ICF44CQMzEyNTAwMjA3NuOAkeeahOWcqOWAn%252BS5puWIiijpopjlkI0v5bqU6L%252BY5pel5pyfKe%252B8mg0KTHVh56iL5bqP6K6%252B6K6hIDIwMTQtMDktMTQgICAgICAgIA0KUEhQ5a6e5L6L57K%252B6YCaIDIwMTQtMDktMTQgICAgICAgIA0KQXJkdWlub%252BS4gOivleWwseS4iuaJiy4y54mILi4gMjAxNC0wOS0xNCAgICAgICAgDQrkuK3lm73lhpnmhI%252FnlLvlhaXpl6jovbvmnb7lraYs56u55a2QLi4gMjAxNC0wOS0xNCAgICAgICAgDQpBbmRyb2lk54Ot6Zeo5bqU55So5byA5Y%252BR6K%252Bm6KejLi4gMjAxNC0wOS0xNCAgICAgICAgDQrkuK3lm73lhpnmhI%252FnlLvlhaXpl6jovbvmnb7lraYs6I2J5pys6Iqx5Y2JLi4gMjAxNC0wOS0xNCAgICAgICAgDQrlkI3lrrblm73nlLvor77loIIu6Iqx6bif56%252BHLi4gMjAxNC0wOS0xNCAgICAgICAgDQpBcmR1aW5v5oqA5pyv5YaF5bmVIDIwMTQtMDktMTQgICAgICAgIA0K55av54uCQW5kcm9pZOiusuS5iS4y54mILi4gMjAxNC0wOS0xNCAgICAgICAgDQpqUXVlcnnlvIDlj5HmioDmnK%252For6bop6MuLiAyMDE0LTA5LTE0ICAgICAgICANCg%253D%253D HTTP/1.1
Host: zxing.org
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1883.0 Safari/537.36
Referer: http://zxing.org/w/decode.jspx
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4
//*-----------------------------------qrcode.php--------------------------------------*//
<?php
    function qrcode($src){  
        //切割html字符串获取图片url  
        preg_match_all('/<img\ssrc=\"[^\"]+\"/',$src,$qr);
        //截取的""中间的url
        preg_match_all('/\"[^\"]+/',$qr[0][0],$qrcode);
        //除去开头的 ..
        $qrcode = preg_replace('/\.\./','',$qrcode[0][0]);
        //除去开头的 "
        $qrcode = preg_replace('/\"/','',$qrcode);
        //拼接url
        $qrcode = 'http://210.34.85.114:8080'.$qrcode;
        //url编码
        $qrcode = urlencode($qrcode);
        //拼接查询url
        $url = 'http://zxing.org/w/decode?u='.$qrcode;
        //获取数据
        $curl = curl_init($url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $infomation = curl_exec($curl);
        curl_close($curl);
        
        //获取QRcode信息
        preg_match_all('/读者[^<]+/',$infomation,$info);
        //UTF-8转GB2312
        $info = iconv('UTF-8','GB2312',$info[0][0]);
        print_r($info);
    }
?>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • 编码问题一直困扰着开发人员,尤其在 Java 中更加明显,因为 Java 是跨平台语言,不同平台之间编码之间的切换...
    x360阅读 2,470评论 1 20
  • 可以看我的博客 lmwen.top 或者订阅我的公众号 简介有稍微接触python的人就会知道,python中...
    ayuLiao阅读 3,097评论 1 5
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,837评论 25 707
  • 01 海边有城。 我住在那城里。 父亲为我取名林安平。 这年,住在我楼上的林家辰八岁,而我七岁。 这天,我们坐在屋...
    易安阅读 486评论 0 1
  • 文/冬月之恋 艾洁躺在病床上,气若游丝,她五十一岁的生命即将走到尽头。她在医院里与病魔搏斗了两个多月,医生对她丈夫...
    冬月之恋阅读 746评论 1 27