涨姿式了。原文:12306 售票网站新版验证码识别对抗php
近段时间 12306 订票网站验证码升级为用户识别图像内容,而后选取符合条件的图片为验证码,好比这样:html
很多媒体新闻大呼抢票工具集体失效、12306终极验证码等新闻,这种验证码的推出有好一样也有坏处:机器识别困难,一样人眼识别也轻松不到哪里去。linux
用这种方式做为验证码最大的担心就是怕脚本或人工对其图片进行爬虫遍历,而后将全部的图片保存后与关键字进行对比并关联入库,固然前提是这些图片都是静态的。git
12306 验证码到底是静态仍是动态,昨晚对这个疑问进行了实践:http://linux.im/2015/03/17/12306-captcha-md5-go.html ,简单的说测试后发现这整张图片是在服务器后端动态生成的,因此不难理解为何生成验证码页面时会比较慢。github
一样上午咱们又进行了第二个实践,将整张验证码中的八张图像拆分为8张小图而后进行感知hash处理,得到样本总数72225张,不重复的图库为15478张,重复最高为869次,绘制成图以下:后端
既然不是静态的图像(对比过近10w条图像hash),那咱们就不浪费功夫爬取静态图片进行数据关联入库了,但咱们仍然须要“破”掉这个验证码,没有什么理由。服务器
最后,下文出现的全部的片断代码将会开源,无需担忧。app
验证码流程:函数
例如上面的验证码图,他是一整张图片,识别其关键字首先要对关键字区域进行图像截取,随后识别成文字。工具
这里使用 Python 的 PIL 图像处理库来进行区域的选择:
def imgCut(): pic_file = downloadImg() pic_path = "./12306_pic/%s.jpg" % pic_file pic_text_path = './12306_pic/%s_text.jpg' % pic_file pic_obj = Image.open(pic_path) box = (120,0,290,25) region = pic_obj.crop(box) region.save(pic_text_path) print '[*] Picture Text Picture: {}'.format(pic_text_path) return pic_path, pic_text_path
imgGut函数首先会下载这张验证码大图(其中包括提示字、关键字、8张图片等),而后保存至 ./12306_pic/ 目录进行存储,随后使用 PIL 库对图像的 (120,0,290,25) 区域切割,也就是获取关键字图像区域。
如今咱们已经可以将验证码下载并切割出想要的关键字区域了,下面咱们要识别关键字,而后转换为文本文字。
使用一些开源的光学字符识别模块应该就能进行识别,但这不方便使用者运行,因此我选择了一款在线网站OCR识别,他可以对你上传的图像(咱们刚刚切割好的图像)进行文字识别转换,固然准确率并无那么高,必定得记住这一点!
这里贴出部分代码,功能实现(传入图像返回关键字的文本内容):
upload_pic_url = "http://cn.docs88.com/pdftowordupload2.php" filename_tmp = filename.split('/')[-1] pic_text_content = open(filename).read() para = {'Filename': filename_tmp, 'sourcename': filename_tmp, 'sourcelanguage': 'cn', 'desttype': 'txt', 'Upload': 'Submit Query',} upload_pic = requests.post(upload_pic_url, data=para, files={"Filedata" : open(filename, 'rb')}) text_result_url = 'http://cn.docs88.com/' + upload_pic.content[3:] text_result = requests.get(text_result_url) return text_result.content
咱们运行试试效果:
[+] Download Picture: https://kyfw.12306.cn/otn/passcode... [*] Picture Text Picture: ./12306_pic/1426580454_text.jpg [*] Text: 衬 衫 [+] Download Picture: https://kyfw.12306.cn/otn/passcod... [*] Picture Text Picture: ./12306_pic/1426580454_text.jpg [*] Text: )帽子 [+] Download Picture: https://kyfw.12306.cn/otn/passcod... [*] Picture Text Picture: ./12306_pic/1426580454_text.jpg [*] Text: 春联
效果还不错,足够咱们测试使用,还记得他的准确率吗?
以前关于图像识别我在 Buzz 发表过相关文章:使用CloudSight API进行图像识别的Python脚本,此次咱们不使用这个脚本,缘由是虽然识别准确度较高但速度略慢,因此我并非很钟爱这一套,恰巧知乎上有位朋友写了一篇利用百度识图来进行图像识别的文章及代码,Google识图固然也不错,但恰好在这咱们会用到,因此没必要纠结。
横向两行,每行四个,而后对其进行图像识别并返回:
dict_list = {} count = 0 for y in range(2): for x in range(4): count += 1 im2 = get_sub_img(pic_path, x, y) result = baidu_stu_lookup(im2) dict_list[count] = result print (y,x), result
其中函数因文章长度缘由暂不在这贴出,识别效果以下:
(0, 0) 冰雕|建筑夜景 (0, 1) 炸暑条|快餐 (0, 2) 灯塔|高塔 (0, 3) 汉堡|麦当劳薯条|开店 (1, 0) 运动外套|防御服|运动服 (1, 1) 银灰色|手机|移动版 (1, 2) 标书制做|规划 (1, 3) 手机
好,如今咱们可以识别出关键字,也能识别出验证码8个图像了,咱们还须要机器帮助咱们确认,究竟选择哪几个图。
前面两次提到使用的 OCR 在线识别准确度并无那么高,因此为了方便程序可以聪明的帮咱们思考这道选择题,咱们进行结果伪分词对比。
首先将关键字进行拆分,而后循环对比结果,这样就能将未准确识别的文字忽略并识别相应识图结果,这里我将8个图像结果按照1-8区分,第一行从左到右(1-4),第二行(5-8):
if captcha_text.strip() > 2: print '\n[*] Maybe the result of the:' maybe_result = [] for v in dict_list: for c in range(len(unicode(captcha_text.strip(), 'utf8'))): text = unicode(captcha_text, 'utf8')[c] if text in dict_list[v]: _str_res = '%s --- %s' % (v, dict_list[v]) maybe_result.append(_str_res) for r in list(set(maybe_result)): print r else: print '[-] False'
好了,这样一来就算识别率没有那么高咱们也能尽量的将答案寻找出来了,看下效果:
结束了吗? 其实没有。
咱们使用脚本进行了大量的测试,成功率可喜的足够令一些邪恶的人作些事儿了,但验证码对抗一直在进行,固然也愈来愈有趣:)
文中完整代码连接:https://gist.github.com/Evi1m0/fbbdb1ba7c66cc4e1bb2