本帖最后由 小和尚 于 2016-3-30 21:29 编辑
一次很特别的验证码识别
简单验证码识别的步骤一般是去噪->切割->对比识别,一般来说这种工作特别费时间,所以遇到一些特别的站的时候就要多想想其他方式了。
最近想做一个APP,把学校的教务,一卡通,流量查询等各种系统集中在一起,但是遇到了一个严重的问题:门户系统的验证码。
教务,一卡通,流量都是需要通过门户进入的,当然我也可以选择将验证码返回到客户端让用户自己识别,然而我还是想尝试一下的。
验证码是如下的图片:
这种验证码中间用了个扭曲的算法,将一部分图像扭曲了,如果直接识别,1和7的位置的不同会导致切割和识别出错,粗略计算了一下,如果按照这种方法,最终的识别率在百分之20左右,只能另寻生路。
其实,仔细看一下,这种验证码没有任何的随机噪点,然后对比一下第一排第二个验证码和第二排第二个验证码的最后一位数,两个“8”完全一模一样。
所以就可以考虑一下做个kv(key-value)映射表了。
经过大量的尝试,发现这些验证码里面只有1-9,所以一共有6561个验证码图片,而相同的验证码的图片也是一模一样的,所以这个kv映射表的key就是验证码图片的md5值,value就是验证码。
1、先来建立一个kv数据库,把所有的图片down下来,代码:
[Python] 纯文本查看 复制代码 <code>
#!/usr/bin python
#coding: utf-8
import requests
import MySQLdb
import md5
conn=MySQLdb.connect(host='localhost',user='root',passwd='',port=3306)
count = 9000
while count <= 50000:
print count
content = requests.get('图片地址.jpg').content
m = md5.new()
m.update(content)
m = m.hexdigest()
cur=conn.cursor()
print m
res = cur.execute("select md5 from md5.tables where md5='%s'"%m)
print res
if res == 0:
cur.execute("insert into md5.tables(num, md5) values('%d','%s')" % (count, m))
conn.commit()
f = open('./data_2/' + str(count) + '.jpg', 'wb')
f.write(content)
f.close()
count += 1
cur.close()
conn.close()
</code>
2、然后就是需要识别6000多个图片,自己识别是很浪费时间的,所以我选择了一个打码平台,我使用的是云速:www点ysdm点net,6600个图片,当时他并没有开发文档,只有测试接口,要想识别,就只能从测试接口通过一个html-upload接口上传图片上去,这是代码,中间有用户名和密码等:
[Python] 纯文本查看 复制代码 <code>
#coding: utf-8
import requests
import re
import MySQLdb
conn=MySQLdb.connect(host='localhost',user='root',passwd='',port=3306)
for num in range(5222,6000):#0,6508
cur=conn.cursor()
try:
cur.execute("select num from md5.tables order by num limit %d,1" %num)
n = cur.fetchall()[0][0]
print n,num
files={'username':(None,''),
'password':(None,''),
'softid':(None,'1'),
'timeout':(None,'60'),
'softkey':(None,''),
'typeid':(None,'5000'),
'image':('%s.jpg'%n,open(r'E:\tmp\data_2\%s.jpg'%n,'rb').read(),'image/jpeg')
}
headers = {'Cookie':'_': 'zh-CN,zh;q=0.8', 'Accept-Encoding': 'gzip,deflate', 'Referer': 'http://www.ysdm.net/home/OnTest', 'DNT': '1','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0', 'Origin': 'http://www.ysdm.net','Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Cache-Control': 'max-age=0'}
a = requests.post("http://www.ysdm.net/home/OnTest",files=files,headers=headers)
code = re.search(':([1-9]{4})"',a.content).group(1)
except Exception,e:
print e
code = '0000'
print code
cur.execute('update md5.tables set val=%s where num=%s' %(code, n))
cur.close()
conn.commit()
conn.close()
</code>
3、等了一天以后,百分之80的验证码就down下来了,不用过多,百分之80的识别率就OK了,因为不论怎样,百分之百的识别是不可能实现的。
这是最后的效果:
现在就是最后一步了,写个php来实现在线识别:
[PHP] 纯文本查看 复制代码 <code><?php
$mysqli = new mysqli('localhost','root','','md5');
if (mysqli_connect_errno()){
die('Unable to connect!'). mysqli_connect_error();
}
$mysqli->query("SET NAMES utf8");
$md5 = $_GET['md5'];
$md5_white = '';
if(!preg_match('([0-9a-f]{32})',$md5,$md5_white)) die("fuck");
$sql = "select val from tables where md5='$md5_white[0]'";
$query = $mysqli->query($sql);
while($rs=$query->fetch_array())
{
if(!empty($rs['val'])) echo $rs['val'];
else {
echo "0";
}
}
?></code>
这中间用了一些处理,只允许32位的md5传入,否则输出fu**。
最后访问http://localhost/md5.php?md5=xxxxx就可以返回验证码值了,客户端只需要将图片的md5值传过来就可以获取到验证码值。
|