博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

  本篇主要讲述验证码的验证流程,包括如何验证码的实现、如何获取验证码、识别验证码(这篇是人来识别,机器识别放在下篇)、发送验证码。同样以一个例子来说明。目标网址 http://icp.alexa.cn/index.php(查询域名备案信息)

  1.验证码的实现:

  简单的说,验证码就是一张图片,图片上有字符串。网站是如何实现的呢?有WEB基础的人可能会知道,每个浏览器基本都有cookie,作为这次回话的唯一标示。每次访问网站,浏览器都会把这个cookie发送给服务器。验证码就是和这个cookie绑定到一起的。如何理解呢?举个例子,现在有网站W,有A和B两个人,同时访问W,W给A返回的验证码是X,给B返回的验证码是Y,这两个验证码都是正确的,但是如果A输入了B的验证码,肯定验证不通过。那服务器是怎么区分A和B呢,就是用到的cookie。再举个例子,有些网站你登录一次之后,下次继续访问可能就自动登陆了,也是用cookie来标示唯一身份的,如果清除了cookie也就无法自动登陆了。cookie具体是什么生成的,我们不必关心,只需要知道是一长串字符串就行了,你的和别人的都不一样。(例子中的目标网址并不是用cookie,而是用的其他方式,所以可能会存在一些BUG)

  服务器后台生成验证码的流程就很容易理解了:首先,生成一个随机字符串,然后和cookie绑定,然后写到图片上返回给你。那么,如何生成一个图片验证码呢?下面是一个简单的生成验证码源码:

from PIL import Image
import ImageFilter,ImageDraw,ImageFont
import random

width = 80
height = 40
font = ImageFont.truetype('C:\\Windows\\Fonts\\AdobeFangsongStd-Regular.otf', 28)
image = Image.new("RGB",(width,height),(0,0,0))
draw = ImageDraw.Draw(image)
for t in range(4):
    draw.text((20*t,10),`random.randint(0,9)`,font=font,fill=(255,255,255))
image.show()

  代码说明:

    a).PIL是python的图片库模块,需要自己安装

    b).ImageFont.truetype()是选择字体

    c).Image.new("RGB",(width,height),(0,0,0))新建一个Image,背景色是白色((0,0,0)就代表的颜色),如果需要别的颜色,可自己查询颜色代码。window自带的画板就可以看到:

    

    d).random.randint(0,9)随机数 范围大于等于0,小于等于9

    e).draw.text((20*t,10),`random.randint(0,9)`,font=font,fill=(255,255,255),anchor=False) 第一个参数代表位置,带二个代表内容,第三个代表字体,第四个代表字体颜色

    f).image.show()显示图片,第一词会提示选择默认图片查看器。

   运行结果如下图:

    

  2).验证码的获取

    a).分析目标网站,可以看到当鼠标点击验证码那个输入框时会显示验证码,如图:

  

  那么获取验证码的请求是什么?以及请求发送的时间?(验证码显示的时间不一定是验证码获取的时间,虽然这个例子中是,以为验证码可能是页面刚开始的时候一起加载的,只是一直被隐藏)。火狐浏览器F12打开控制台,找到网络标签,刷新页面,可以看到如下图所示:

  

  并没有发现获取验证码的请求,那么我们点击验证码的那个输入框,发现多了一个请求,没错,这就是获取验证码的请求。

  

  b).下面我们开始分析这个请求,首先点击这个请求,可以看到如下图所示:

  

  完整URL:http://icp.alexa.cn/captcha.php?q=sina.com.cn&sid=82&icp_host=sxcainfo。可以看到三个参数:

  q=sina.com.cn:查询的域名

  sid=82:这个ID暂时不知道是什么,后面查看JS源代码会看到

  icp_host:这个暂时也不知道。

  这三个参数,那些是必须的呢?可以一个一个测试。测试方法就是删掉某个元素,然后再发请求。测试发现,三个参数缺省都可以获取到验证码,获取到验证码,不代表验证码可用,因为,没有与某些类似cookie的值绑定到一起,它就和图片没有任何区别,不具备验证的功能。经我测试,(测试很简单,不过要用到后面的东西,看完这篇,就知道怎么测了),这三个参数都需要。在测试的过程中,我发现了sid就是一个随机数,没有什么特殊含义,基本可以确定可以随便输入:js代码如下:

  

  icp_host的取值有很多:sccainfo、ahcainfo、jscainfo......有没有发现什么规律?首先八个字母,最后六个都是cainfo,那么前面两个代表什么?sc=四川、ah=安徽、js=j江苏。所以,我们可以猜测这个是省份的简写。那这个值有什么用呢?作用一,如果不按这个规则输入字符串(比如,aaaaaaaa),就获取不到验证码;作用二,验证码就是和这个绑定的。也就是说,你获取验证码的时候用sccainfo,那么验证的时候也要用sccainfo。

  分析完参数,然后再分析请求头,方法和参数的分析方法一样,一个个删除,看能不能获取正确的结果。这个时候,可以自己写python代码测试,具体代码如下:

#encoding=utf8
import urllib2
from PIL import Image
import cStringIO
getCode_url = "http://icp.alexa.cn/captcha.php?q=163.com&sid=0&icp_host=hncainfo"
header={"Referer":"http://icp.alexa.cn/captcha.php?q=163.com&sid=0&icp_host=hncainfo"}
# header['Host']="icp.alexa.cn"
# header['User-Agent']="Mozilla/5.0 (Windows NT 6.3; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0"
header['Cache-Control']="max-age=0"
request = urllib2.Request(getCode_url,headers=header)
res = urllib2.urlopen(request).read()
image = Image.open(cStringIO.StringIO(res))
image.show()

  代码说明:

    a).cStringIO python的流模块,无论是图片、文本、音频、视频都是流文件,可以相互转化。这里的作用是将图片流还原成图片

    b).header中添加参数可直接用header['']="",这样就可以测试了。具体哪些参数必须,自己测试。

  运行结果:

  

  3).检验验证码

   a).分析目标网站,寻找检验验证码的请求。我们在输入框输入正确的验证码,点击备案查询,如图所示:

    

    可以看到控制台中多了一个请求

    

    点击请求,查看请求详情:http://icp.alexa.cn/index.php?q=163.com&code=65a89c&icp_host=lncainfo

    三个参数:

     q=163.com:查询的域名,必不可少

     code=65a89c:验证码,必不可少

     icp_host=lncainfo:和获取验证码相对应

    然后再分析header,与上面的方法一样。这两次检验不同的是:检验获取验证码时,是自己写代码获取验证码,然后放到网站上检验,验证码的正确性(必须保证icp_host一致);检验检查验证码时,是用网站获取验证码,填到代码里面,看看参数对不对。

    验证代码如下:

#encoding=utf8
import urllib2

checkcode_url = "http://icp.alexa.cn/index.php?q=163.com&code=N3PE37&icp_host=hncainfo"
header={}
# header['Pragma']="Pragma"
# header['Referer']="http://icp.alexa.cn/index.php?q=163.com&code=CUXWDV&icp_host=sccainfo"
header['User-Agent']="Mozilla/5.0 (Windows NT 6.3; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0"
request = urllib2.Request(checkcode_url,headers=header)
res = urllib2.urlopen(request).read()
print res

 

    代码说明:icp_host=hncainfo这个参数必须和你获取验证码时候的参数一致

    运行结果:

  

    如果验证码不正确或者别的地方不一致,会返回:

    

  到此,我们就分析完了,不过现在是把获取和验证放在两个代码中运行,怎么放在一起呢?代码如下:

#encoding=utf8
import urllib2
from PIL import Image
import cStringIO
import BeautifulSoup

def getCode(domain):
    print "获取验证码...."
    getcode_url="http://icp.alexa.cn/captcha.php?q="+domain+"&sid=0&icp_host=hncainfo"
    getcode_headers = {}
    getcode_headers['Referer']="http://icp.alexa.cn/captcha.php?q=163.com&sid=0&icp_host=hncainfo"
    getcode_headers['Cache-Control']="max-age=0"
    getcode_request = urllib2.Request(getcode_url,headers=getcode_headers)
    getcode_res = urllib2.urlopen(getcode_request).read()
    image = Image.open(cStringIO.StringIO( getcode_res))
    print "获取验证码成功"
    image.show()
def checkcode(domain,code):
    # print "您输入的验证码为:"+`code`
    print "开始检查验证码..."
    checkcode_url = "http://icp.alexa.cn/index.php?q="+domain+"&code="+code+"&icp_host=hncainfo"
    checkcode_headers={}
    checkcode_headers['User-Agent']="Mozilla/5.0 (Windows NT 6.3; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0"
    checkcode_request = urllib2.Request(checkcode_url,headers=checkcode_headers)
    checkcode_res = urllib2.urlopen(checkcode_request).read()
    if(checkcode_res.count("主办单位名称")>0):
        print "验证成功"
        checkcode_soup = BeautifulSoup.BeautifulSoup(checkcode_res)
        print "所属单位名称:"+checkcode_soup.findAll("table")[0].findAll("tr")[0].findAll("td")[1].text.encode("utf8")
    else:
        print "验证失败"
domain = raw_input("请输入域名:")
getCode(domain)
code = raw_input("请输入验证码:")
checkcode(domain,code)

    代码说明:

      a).def getCode(domain) 声明一个函数,getCode是函数名,domain是参数

      b).raw_input() 获取用户输入

      c).在获取和验证的时候,我把icp_host都写成了hncainfo,这样就可以保证一致。

      d).encode("utf8") 对变量以utf8格式编码

      e).验证码要人工识别输入

    运行结果:

    

    

  到此,整个验证码的获取,验证都讲述完了,验证码的识别放在下一节。

  说明:

  a).代码仅供学习交流

  b).如有错误,多多指教

  c).转载请注明出处