Py学习  »  Python

JB的Python之旅-爬虫篇-图形验证码(3)-- 验证码的生成了解下

jb • 5 年前 • 493 次点击  

JB的Python之旅-爬虫篇-图形验证码(3)-- 验证码的生成了解下

前言

最近在看怎么绕过图形验证码,虽然有思路,但是有点吃力,估计还要折腾几天,心里就想吐槽:验证码真难搞的;
这不,既然现在还没搞定验证码,那要不看看验证码是怎么生成的?原理是怎么处理的?
于是乎就有了此篇,写的不是很好,求各大神指教下;

前几天本来也想自己搞个网站,就去了解下备案是什么鬼,然后就找到这么一个网站:
http://icp.alexa.cn/index.php

用来查询备案的,这里面也有验证码,那就用这个网站先玩玩吧;

对了,这里的验证码指的是图形验证码

流程

正常的流程:
验证码于服务器端生成,发送给客户端,并以图像格式显示。
客户端提交所显示的验证码,客户端接收并进行比较,若比对失败则不能实现登录或注册,反之成功后跳转相应界面;

如果真这么简单,是不是用穷举的方式就能破解了?

有web基础的人可能会知道,每个浏览器都会有cookie,是作为这次回话的唯一标示;

cookie

防止有同学不知道,先简单介绍下cookie:

1. 什么是Cookie,它的用途是什么?
Cookies是一些存储在用户电脑上的小文件。
它是被设计用来保存一些站点的用户数据,这样能够让服务器为这样的用户定制内容,后者页面代码能够获取到Cookie值然后发送给服务器。
比如Cookie中存储了所在地理位置,以后每次进入地图就默认定位到改地点即可。

2. Cookie是什么时候生成的,完整的生成、传递和使用过程是怎么样的?
写数据到Cookie中通常是在一个页面被加载的时候,比如提交按钮被按下,后台处理完请求跳转到相应页面后会把Cookie值带回来;
如下是一个例子:

  • 在浏览器地址栏输入了一个站点,然后浏览器会发送请求到站点的Web服务器请求该页面
  • 与此同时,浏览器会在个人电脑上行查找和该站点对应的Cookie文件,如果发现了就会把里面的键值对内容全部发送给Web服务器,如果没找到则不发送。
  • 页面服务器接收到了Cookie的数据后,可以利用这些数据决定返回到前台的内容
  • 如果没键值对被Web服务器接收到,站点就知道该用户没有访问过,就会为这个访问地址新建一个ID并且发送一些键值对给前台,这些值会被放在这次响应中的Header带给浏览器,浏览器端于是有了Cookie的值
  • 任何到服务的访问和数据请求都可能很对Cookie中的键值对进行修改

实现

大致了解下cookie,不至于那么不清不楚;那就继续拉;

一般来说,每次访问网址,浏览器都会把cookie发送给服务器,而验证码就是和这个cookie绑定在一起的;
举个例子:

  • 现在有网址T,有用户A和B两个人同事访问T
  • T给A返回的验证码是X,给B返回的验证码是Y,这两个验证码都正确
  • 如果A输入B的验证码,是验证不通过的

那服务器怎么区分A和B?那就是用cookie;

cookie是标示唯一身份的,比如有些网站,登录一次后会自动登录,但是如果清除了cookie,就无法自动登录了,而且这cookie是个别人不一样的;

说到这里,服务器后台生成验证码的流程就很容易理解了:

  • 先随机生产一个随机字符串
  • 然后和cookie绑定
  • 再写到图片上返回给你

那,怎么生成一个图片验证码?

生成图片验证码

from PIL import Image,ImageDraw,ImageFont
#PIL是python的图片库模块
import random
#随机函数

width = 80
height = 40
font = ImageFont.truetype('C:\\Windows\\Fonts\\STFANGSO.ttf', 28)
#选择字体
image = Image.new("RGB",(width,height),(120,10,200))
#新建一个Image,背景色是黑色(000),如果需要别的颜色,自己修改即可;图片大小就是开始定义的80X40
draw = ImageDraw.Draw(image)
#创建一个可以在给定图像上绘图的对象
for t in range(4):
    #4是代表几位,需要需要4位数字的验证码,这里输入4,需要10位,就10
    draw.text((20*t,10), repr(random.randint(0, 9)), font=font, fill=(255, 255, 255))
    #random.randint(0, 9),随机数,0-9
    #第一个参数代表位置,第二个代表内容,第三方代表字体,第四个代表字体颜色
image.show()
#显示图片

效果图:

验证码就这样生成啦~

验证码的获取

回到一开始的网站:http://icp.alexa.cn/index.php

打开后发现,网页自动就显示验证码了,有办法获取到这个验证码吗?F12刷新一波试试

有一个这样的请求,点击后的确就是验证码的图片了;

链接如下:
http://www.alexa.cn/api/icp/vcode?host=hcainfo&flag=f419510445e39e61cc32a7efd7552ed1&R=0.37766116804135375

从链接分析,有3个参数:

  • host=hcainfo
  • flag = f419510445e39e61cc32a7efd7552ed1
  • R=0.37766116804135375

嗯,一脸懵逼,看不懂,先不管,我们来测试下,看看这3个参数哪个是必须的;
怎么测试?简单,直接copy上面的url,在浏览器打开,然后一个个参数删除,如:

  • http://www.alexa.cn/api/icp/vcode?host=gxcainfo&flag=8f7e4e73eb36c20033ed825238266823
  • http://www.alexa.cn/api/icp/vcode?host=gxcainfo
  • http://www.alexa.cn/api/icp/vcode
  • http://www.alexa.cn/api/icp/vcode?host=hcainfo&flag=&R=

还可以多试试排列组合,最终得出一个结论:host=gxcainfo是必须的,flag跟R不是必须的,空或者没有参数,都不影响获取验证码;

但是,能获取到验证码,不代表该验证码是可用的,因为如果没有与某些类似cookie的东西绑定在一起的话,那它本身就只是图片而已,没特殊含义,也不具备验证功能;

那host的值呢?尝试了一下,把host的值改成其他乱七八糟的,也一样获取不到验证码,这里面啥情况;

把网页刷新,每次刷新,host的值都在变化,比如ahcainfo、gxcainfo、ecainfo,如果不刷新网页,host的值则一直不变;
通过上面3个host的值,发现一点疑问,都是8个字母,而且后面六位都是cainfo,只有前面两个在变;ah\gx,给人感觉就是安徽跟广西?但是ec想不出,怎么感觉,有点像省份的意思?
亲自试了下,修改成gd,hn也能获取到验证码,莫非真是省份?那ec是啥??

这里继续猜就没意义了,但可知的是:
1)没有host这个值就获取不到验证码;
2)及时有host,值不对也是获取不到验证码;
3)那是不是说明,验证码是跟这个值绑定的?

url参数就到这里了,接下来请求头吧~
没太特别,直接把整个请求头copy过来用就行了:




    
import requests
from PIL import Image
import io

getCode_url = "http://www.alexa.cn/api/icp/vcode?host=hcainfo&flag=f419510445e39e61cc32a7efd7552ed1&R=0.37766116804135375"
header = {
    "Referer":"http://icp.alexa.cn/",
    "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"
}

response = requests.get(getCode_url,headers=header).content
image = Image.open(io.BytesIO(response))
image.show()

这上面的代码,好像没什么特别,应该不需要说明的;

运行的结果就是:

整体的代码:

import requests
from PIL import Image
import io

def getCode(url):
    print("获取验证码")
    getCode_url = "http://www.alexa.cn/api/icp/vcode?host=jlcainfo&flag=61684048f21350aa2767b82315a0f487&R=0.5917477648057996"
    header = {
        "Referer":"http://icp.alexa.cn/",
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"
    }

    response = requests.get(getCode_url,headers=header).content
    image = Image.open(io.BytesIO(response))
    print("获取验证码成功")
    image.show()

def checkcode(url,code):
    print("开始检查验证码")
    checkcode_url = "http://www.alexa.cn/home/index/query?token=1a63a83b2xwDdXlEmmIldi-Cx729izemOE54BDjY8jnT-JCvU3Atg0W1gCrBVbMSs-O&flag=61684048f21350aa2767b82315a0f487&host=jlcainfo&flag&domain=qq.com&type=icp&type=icp&vcode="+code
    header = {
        "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "Accept-Encoding":"gzip, deflate",
        "Accept-Language":"zh-CN,zh;q=0.9",
        "Connection":"keep-alive",
        "Cookie":"SL_GWPT_Show_Hide_tmp=1; SL_wptGlobTipTmp=1; exi_query_history=nseyfqfp4SgNKMagNO-DctlkEDqameDvLLDbwUIuYCiHNkoLJPniHTUs0RIAq5jNEZ8ojjeoe8W8y1Df6vuMiy8r-H37690i99d0LZ3iyTWMVmstIOcGqb5H-DY1k2Gn3FNdj02TwpVtlca1b1lrvrdSfE-HbUGwP3Lfex0D9Hzeu48-N",
        "Host":"www.alexa.cn",
        "Referer":"http://icp.alexa.cn",
        "Upgrade-Insecure-Requests":"1",
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"
    }
    response = requests.get(checkcode_url,headers=header)
    print(response.content)
    print(response.status_code)


if __name__ == "__main__":
    getCode("qq.com")
    code = input("请输入验证码:")
    checkcode("qq.com",code)

本来是想弄个,自动拉取验证码图片,手动输入后自动拉取结果,但是执行的时候,还是被网站识别出来了,想了很久还没想明白,算了留下了一个烂摊子吧~

小结

本文主要介绍了验证码是怎么生成的,本来也想结合做个机器识别的,但在调试过程,依然被网站识别出来了,折腾半天,目前无果,先弃疗,后面想到原因再来更新吧~

谢谢大家~


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/WhJjqZKhUg
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/13587
 
493 次点击