转载

Python 爬虫入门(四)—— 验证码上篇(主要讲述验证码验证流程,不含破解验证码)

本篇主要讲述验证码的验证流程,包括如何验证码的实现、如何获取验证码、识别验证码(这篇是人来识别,机器识别放在下篇)、发送验证码。同样以一个例子来说明。目标网址 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).分析目标网站,寻找检验验证码的请求。我们在输入框输入正确的验证码,点击备案查询,如图所示:

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

Python 爬虫入门(四)—— 验证码上篇(主要讲述验证码验证流程,不含破解验证码)

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

三个参数:

:查询的域名,必不可少

:验证码,必不可少

 #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).转载请注明出处

原文  http://www.cnblogs.com/hearzeus/p/5166299.html
正文到此结束
Loading...