前面我们已经说过了请求,那么现在我们就通过常见的验证逻辑来聊一聊验证这事。
这个就是互联网中常见的验证逻辑。网络验证就像是居民去派出所办身份证一样。居民先携带确认自身身份的信息去派出所,派出所验证后,为居民办理身份证并派发给居民。居民拿到身份证之后,就可以通过身份证在社会中证明自己的合法身份。
我们在爬虫中的验证,就是要通过模拟登录网站的手段,获取凭证。然后在每一次数据请求中携带该凭证并检查凭证是否过期。
模拟登录
在模拟登录之前,我们要先弄清楚目标网站以下三点特征:
- 是什么类型的凭证(
会话凭证
还是令牌凭证
) - 凭证什么时候,怎样获得(
首次访问网站时
还是登录成功后
) - 验证成功后,要携带到请求的哪个位置
会话型凭证一般就是沿用Web容器的Session
机制。在浏览器打开后,用户访问网站时,Web服务就会为这次会话创建一个Session
。这个Session
是未经验证的,只用于代表和存储用户与Web服务的这段访问信息(就想打电话一样)。用户在登录后,Web服务会标记这个Session
为已通过验证。Session
的唯一信息一般由Web服务的第一个响应头中的Set-Cookie
头返回并命令浏览器创建Cookie
对Session
的标识进行存储。
令牌型凭证正好就相反。采用令牌型凭证的网站一般就是直接摒弃Session
机制,利用登录接口的响应体返回凭证信息,然后Cookie
,localStorage
,sessionStorage
等存储凭证,用自定义的请求头或Cookie
携带请求。这种类型多出现于SPA型前后端分离架构。
我们要模拟登录验证,要做的就是在请求登录接口验证后,能正确地提取并存储登录接口所返回的凭证信息。因此对于会话型凭证,我们要做的就是要做到验证前后的Session
标识一致。而对于令牌型凭证,我们要做的是保证登录后请求都要带上登录所返回的凭证信息。
知道以上我们要做的事,那么我们的代码自然也应运而生。下面我们用HttpClient
作为请求工具的方式,编写两个模拟登录的Demo代码。
为了保护具体网站,也懒得去找具体网站,这里模拟的目标均为假想目标。
1 |
|
令牌型模拟登录demo
1 |
|
基本的登录验证模拟一般就是以上这么个套路,下面我们再说说验证码。
图片文字验证码解析
在解决验证码之前,我们首先要判断清楚网站登录的验证码是否真的有用,避免浪费不必要的开发成本。有些网站的验证码实际是虚设的,验证码与登录实质脱钩不沾边(当然也有用于先验证Session,和登录接口不挂钩的)。
首先,我们来说说验证码与登录挂钩。
通常,验证码是一个置于<img>
标签的一个图片生成接口。当采用会话型验证
时,验证码的答案会存储在Session
中,当提交登录时,与Session
中的答案对照进行验证,从而达到登录验证码挂钩。当采用令牌型验证
时,在客户端请求验证码图片的接口时,后端会通过相应头中的Set-Cookie
头,指示浏览器生成图片对应的Cookie
来存储验证码的token。当登录时,浏览器需要携带Cookie
中的验证码token和验证码进行请求,从而进行登录验证码挂钩。
因此我们带验证码模拟登录时,需要注意是否需要携带验证码凭证进行登录。
下面我们再说说如何解析出图片中的验证码答案。
OCR文字识别
对于解析图片中的文字,我们一定离不开OCR文字识别
。下面介绍两个常用的免费OCR。
- Tesseract-OCR
- 百度OCR
Tesseract-OCR
Tesseract-OCR是一个由C++编写,开源的OCR程序。它支持window与linux平台,具有多种语言的预训练包可供使用。这些语言包可以直接适用于一些标准的文字识别(如:word,pdf档的文字截图)。但对于爬虫而言,这并不是好的选择。因为标准字体训练库只对一些常见的字体识别准确,对于验证码常见的扭扭咧咧的字符经常会验证不到。如果想训练一个特别字库,所需的开发时间成本也不低,因此我们一般不会选择它。
百度OCR
百度OCR文字识别是百度AI开放平台中的一类产品。它以限量免费的政策提供免费提供给开发者。对于抓取验证频率不高的网站,我们可以考虑它。在低并发的情况下,百度提供的OCR基本等于是白嫖。具体免费政策如下:
OCR预处理
)这是一张常见的验证码。因为CSDN文章各种复制粘贴的缘故,这一类的验证码真的非常常见。这一类验证码如果直接交给OCR进行识别,即使是百度的高精度OCR,准确率可能也只有60%-70%。因此为了避免较高的登录失败几率和百度免费OCR的次数浪费我们往往需要去预先做一些简单的图形处理,再交给OCR,以提高准确率。
对于这一类验证码,我们要解决的干扰素有两个。
- 干扰线(去除/减少)
- 字体颜色(统一颜色,建议统一转黑色)
以下是解决思路:
1、确认验证码图片长宽,指定局部扫描区域
2、扫描局部区域,根据像素点颜色比例,找出背景色和干扰线的颜色。
3、扫描全图,将于干扰线颜色相近的的所有像素点转为背景色,将非背景颜色色和非干扰线颜色色的像素点转为黑色,达成去像素点。
那么问题来了,我们如何去确定两个像素点颜色是否相近?
我们知道像素点的颜色有色光三原色(红R,绿G,蓝B)的数值决定的。那么我们如果将RGB(R,G,B)转变为XYZ(X,Y,Z)的三维直角坐标系,限定每条轴线的上下限为0和255。然后在这直角坐标系中的限定范围内铺满对应的像素点。就会得出一个三维颜色连续渐变的正方体。假设我们要判断两个像素点是否颜色相近,就可以转化为判断这两个点的在这个直角坐标系中的距离是否足够近的问题。这就变成了一个简单的求三维空间点距离的简单数学问题了。
我们现在只需根据数学公式 $$d=\sqrt[]{(R_1-R_2)^2+(G_1-G_2)^2+(B_1-B_2)^2}$$ 确定一个距离允许的范围进行扫描比较即可。
demo效果
以上方式比较简单粗暴。在人工智能范畴,还有利用卷积,对图像进行腐蚀
和膨胀
的方式进行图像处理来去除干扰线的方式。这部分内容目前接触不深,以后搞透了再继续填坑。