5.3 验证码的处理
5.3.1 带验证码的网站登录分析
这一节简单分析一下带验证码登录的情况。
假设不小心登录豆瓣的时候连续输错密码,再重新登录豆瓣网站,就会发现需要填写验证码才可以登录,如图 5-6 所示。

图 5-6 需填写验证码的豆瓣登录页面
为了搞清楚带验证码登录都向服务器发送了什么数据,可以先登录一次,使用 Chrome 浏览器的检查功能分析一下,填写验证码登录时,系统都向服务器 POST 了哪些数据。
按照上节的步骤,在 Chrome 浏览器中打开“检查”功能,填写用户名、密码和验证码并单击“登录豆瓣”,然后在浏览器中查看这次登录的 Form Data,会发现系统向服务器提交的数据多了两项内容,如图 5-7 所示。

图 5-7 系统向服务器提交的数据
一项是 captcha-solution: music。显然这个 captcha 就是验证码的意思,captchasolution 的值就是用户输入的验证码。
另一项是 captcha-id: sF1wibCmaB1dHiJ5u4dNY6TS:en。这一项看上去很复杂,也不了解它的含义,那这一项是否是变化的呢?若退出并重新登录一次,会发现这一项是变化的,也就是说这一项是需要提供给网站服务器的变量。假设在打开网站登录页面准备登录的时候,这一项应该是网站提供给用户,然后用户在登录的时候将此项内容提交给网站。为了验证这一想法,现在退出登录,直接来到豆瓣登录页面,如图 5-8 所示。

图 5-8 豆瓣登录页面
在这个页面单击右键,在弹出的菜单中选择“查看网页源码”,在打开的网页源码页面中按 Ctrl+F 组合键,系统弹出搜索框,搜索 captcha-id,结果如图 5-9 所示。

图 5-9 豆瓣登录页面源码搜索结果
从搜索结果可见,验证码代码段的下面有 hidden 项,它的 value 正是需要向网站提交的 captcha-id 的值。如果要使用验证码登录豆瓣网站,需要另外提供 captchasolution 和 captcha-id 两个字段的值,而这两个字段的值是在打开的登录页面中提供给用户的。这就说明用户不能像上一节那样直接登录了,必须首先打开一次登录页面,获取 captcha-solution 和 captcha-id 这两个字段的值,然后再构造表单登录。
5.3.2 验证码的识别和处理
在编写代码之前,先简单分析一下验证码的识别和处理。
验证码用于测试用户是真实的人类还是计算机机器人,它实质上可以说是一种反爬虫的手段。一般来说,用户遇到验证码,有 3 种常见的处理选择。
(1)下载并显示验证码图片,然后由人工识别。这种方式的优势是正确率高、成本低,但缺点也很明显;这种方式无法规模化和自动化,因为人们不能总在计算机前等着输入验证码。当然,可以设置一个邮件提醒,在遇到验证码时将验证码图片实时发送到手机上,然后人工识别出验证码并回传给爬虫服务器。总的来说,人工识别验证码的方式适合个人小型的爬取任务,对于大型的爬取任务,必须考虑自动化的方法。
(2)使用算法识别图像。用户可以充分利用现在的机器学习和深度学习算法,实现对验证码的自动识别。这样虽然可以自动化完成识别,但是开发成本比较高,而且识别准确率也不会太高。
(3)使用专业打码平台提供的服务。当遇到大型识别验证码的任务时,可以将识别任务交给专业的打码平台,由这个平台使用人工智能或人工处理。这种方式适合大规模的自动化爬虫任务,但会增加一定的成本。
总体来说,如果是小型的任务,建议使用第一种方法;如果是大型爬虫,建议直接使用打码平台的服务,但对于近几年出现的中文顺序单击验证码和极验验证等验证方式,打码平台也不能很好地解决。验证码的处理对于爬虫工程师来说,是个比较难以逾越的障碍。
本节登录豆瓣网站是一个相对简单的任务,为了简单演示,在这里使用第一种方式,由自己人工来识别验证码。
5.3.3 编写带验证码的豆瓣网站登录代码
先梳理一下登录流程。在有验证码的情况下,使用 Requests 登录网站,需要先爬取一次登录页面。
第一步,爬取登录页面,获得 captcha-solution 和 captcha-id 这两项的值。
第二步,构造 POST 表单数据。
第三步,登录并爬取首页。
下面按照这个流程来编写全部登录代码。
首先需要获取登录页面。
import requests
from lxml import etree
headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) \
AppleWebKit/537.36 (KHTML, like Gecko) \
Chrome/46.0.2490.80 Safari/537.36'}
url = 'https://www.douban.com/accounts/login'
response = requests.get(url, headers=headers) #获取背景页面
html = etree.HTML(response.text) 然后解析出 captcha-solution 和 captcha-id 这两项的值。这里要先判断是否出现了验证码,如果出现,就下载并使用 Python 的图片处理函数 PIL,打开验证码图片。
captcha = html.xpath('//*[@id="captcha_image"]/@src')
if captcha: #判断是否有验证码
captcha_url = captcha[0] #获取验证码图片 URL
captcha_image = requests.get(captcha_url) #下载验证码图片
from PIL import Image
from io import BytesIO
img = Image.open(BytesIO(captcha_image.content)) #打开验证码图片
img.show() #显示验证码图片
captcha_text = input(u'请输如验证码:') #输入识别出的验证码
#下面解析出 captcha_id 的值
captcha_id = captcha_url.split('=')[1].split('&')[0]
else:
captcha_id = None #若没有验证码,设置为 None
captcha_text = None上面代码中使用 Image 函数打开图片,自己人工查看并填写识别出的验证码,最后赋值给 captcha_text。
第四步,构造 POST 表单。
formdata = {'source':'index_nav',
'form_email':'your_email@xxx.com',
'form_password':'your_password',
'captcha-solution':captcha_text,
'captcha-id':captcha_id}
s = requests.session() #初始化会话
r = s.post(url, data=formdata, headers=headers) 为了验证是否已经成功登录,还是要查看一下返回的内容中是否包含个人昵称。
print('日月光华' in r.text)
True 运行显示的结果为 True,说明已经完成了带验证码的登录,可以继续爬取豆瓣其他的页面了。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论