参考博客:CSDN:javastart,原文链接:https://blog.csdn.net/javastart/article/details/103832997,这哥们是跑去GitHub上发帖求助,还得到了作者之一的回复老开心了,这是他在GitHub上的原帖地址:https://github.com/python-pillow/Pillow/issues/3192
再次感谢这位仁兄javastart,感谢Pillow的作者们的奉献,向全世界的开源者致敬!!!
一、验证码和爬虫的关系
验证码可以说是一种反爬虫措施,常见的有图片验证码、语音验证码、短信验证码!而图片验证码花样最多,最开始是文字验证码、选择验证码、拼图验证码等等,有了这些措施,我们的爬虫程序无法直接爬取目标网站的数据。
下面,我们以最常见的文字验证码为例,如何让我们的爬虫程序能够破解它!
二、验证码识别
能够做到识别图片文字的技术手段和平台有很多,这里我们选择百度云文字识别OCR,为什么是百度云?因为它免费的次数多。。。标准版文字识别,免费50000次/月(还有谁!!!),高精准版500次/月。官方文档:https://cloud.baidu.com/doc/OCR/s/1k3h7y3db。
2.1、准备工作
可以参考这里的操作说明:https://ai.baidu.com/forum/topic/show/867951,我只写需要提醒的部分。
2.2、封装API
access_token有效期默认是30天,你可以直接复制官方代码,这里我把它封装进函数里,实时更新。
# encoding:utf-8
import requests
import base64
def get_access_token(): # 获取access_token
# client_id 为官网获取的AK, client_secret 为官网获取的SK
host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=[你的access_key]&client_secret=[你的secret]'
response = requests.get(host)
if response:
return response.json().get('access_token')
def get_code(img_path): # 通用文字识别(高精度版)
request_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic"
# 二进制方式打开图片文件
with open(img_path, 'rb') as f:
img = base64.b64encode(f.read())
params = {"image": img} # 图片二进制数据
access_token = get_access_token() # 实时获取最新access_token
request_url = request_url + "?access_token=" + access_token # 拼接完整URL
headers = {'content-type': 'application/x-www-form-urlencoded'} # 请求类型
response = requests.post(request_url, data=params, headers=headers) # 发起POST请求
if response:
return response.json() # 返回JSON格式字符串
2.3、爬虫调用API
这里有小插曲,我之所以用PIL来转换格式,是因为很多网站的图片并不是常见的jpg、png,而是ashx,这样的话我们无法直接用open然后调用write的方式转成png格式,如果直接转然后交给百度云API处理,会有216101报错,如下所示,当然,这个错误也有可能是其它编程语言没有处理好base64头文字的原因,但是Python自动处理。
{
"log_id": 1385594470434603008,
"error_msg": "param image not exist",
"error_code": 216101
}
因此我们先用Image标记从网站请求得来的验证码图片,接着生成一个
import requests
from lxml import etree
import AipOcr # 导入封装好的API函数
import io
from PIL import Image
import urllib.request as urllib
login_url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 ',
}
response = requests.get(url=login_url, headers=headers).text
code_uri = etree.HTML(response).xpath('//*[@id="imgCode"]/@src')[0]
code_link = 'https://so.gushiwen.cn' + code_uri
def picture_format_conversion(file_url, formats):
open_file = Image.open(urllib.urlopen(file_url)) # 获取图片字节流
bytes_obj = io.BytesIO() # 实例化BytesIO对象
img_format = Image.registered_extensions().get('.' + formats) # 指定图片字节流格式
open_file.save(bytes_obj, img_format) # 保存指定格式的字节流在内存中
return bytes_obj.getvalue() # 返回bytes字节流
png_bin = picture_format_conversion(code_link, 'png') # 获取图片bytes数据
with open('img.png', 'wb') as file: # 写成png文件
file.write(png_bin)
result = AipOcr.get_code('img.png') # 调用API,识别图片文字,返回结果是JSON字符串。
print(result.get('words_result')[0].get('words'))
# 执行结果,大功告成,可以拿这个结果去登录了。
PROK
三、模拟登录
在第二节成功完成后,我们成功识别到了网站的登录验证码,这时候就可以进行模拟浏览器登录网站了。但是你会发现,你爬虫直接爬取很多需要登录的页面要么告诉你必须携带cookie,要么直接访问未登录给你,这是为什么呢?
人家话已经够明白了,必须携带cookie才能保存你的登录状态,不然无法识别你,因为HTTP协议是无状态连接!怎么办?先行者们早就帮我们想好了,requests模块有Session类专门用于处理session和token!
首先,F12打开开发者工具,定位到Network -- XHR,接着正常手动登录一下,截取到POST请求需要提交的参数并记录下来。
3.1、未携带cookie
上面的验证码识别后,先来演示下不携带cookie直接获取个人中心页面,看看是否正常!
data = {
'from': 'http://so.gushiwen.cn/user/collect.aspx',
'email': '你的个人帐号',
'pwd': '帐号密码',
'code': code, # 识别后的验证码
'denglu': '登录',
}
center_url = 'https://so.gushiwen.cn/user/collect.aspx'
html = requests.get(url=center_url, headers=headers).text
with open('user_center.html', 'wt', encoding='utf-8') as f:
f.write(html)
访问下这个存到本地的页面可以看到,直接就抛出cookie提示了。
3.2、携带cookie
未完待续