爬虫中的验证码识别(简介以及打码平台,包括实战,cookie的作用,session对象的介绍)

验证码识别

一、验证码识别简介

1. 验证码和爬虫之间的关系?(验证码是门户网站中的一种反爬机制)

  • 反爬机制:验证码,识别验证码图片中的数据,用于模拟登陆操作。
  • 在爬虫中有相关的需求,是爬取基于用户的某些相关数据,这就需要登录了才行。在登录时,或许需要输入验证码。在浏览器中输入账号,密码,验证码是方便的。但是基于爬虫,编写程序进行当前用户登录的时候就很麻烦了。登录成功后,进行页面跳转,再将当前用户的相关信息进行爬取。核心就是登录操作,而在登录过程中,若是验证码错误,则登录会不成功。

2. 识别验证码的操作:

  • 人工肉眼识别。(不推荐)耗时,效率低。
  • 第三方自动识别(会推荐一些第三方线上平台,这些平台制定了相关的操作和机制,帮助我们去识别某些类型验证码中的相关数据)【推荐,相关流程懂了就行】
    • 云打码:(目前用不了了)
    • 超级鹰:http://www.chaojiying.com/

二、超级鹰使用流程(下载的样例简单,易懂)

  • 注册:用户

  • 登录:

    • 查询该用户是否还有剩余的题分(题分充足才可以验证,绑定微信即有1000分)

    • 创建一个软件ID:用户中心——> 软件ID ——> 生成一个软件ID——>录入软件的名称和说明,接着提交就行。这样就获得了软件的ID和软件KEY。

    • 到开发者文档那里下载示例代码:选择Python语言的Demo下载

    • 下载完后解压缩,然后放进当前的工程目录中

    • 样例:(运行样例试一下)

    #!/usr/bin/env python
    # coding:utf-8
    
    import requests
    from hashlib import md5
    
    class Chaojiying_Client(object):
    
        def __init__(self, username, password, soft_id):
            self.username = username
            password =  password.encode('utf8')
            self.password = md5(password).hexdigest()
            self.soft_id = soft_id
            self.base_params = {
                'user': self.username,
                'pass2': self.password,
                'softid': self.soft_id,
            }
            self.headers = {
                'Connection': 'Keep-Alive',
                'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
            }
    
        def PostPic(self, im, codetype):
            """
            im: 图片字节
            codetype: 题目类型 参考 http://www.chaojiying.com/price.html
            """
            params = {
                'codetype': codetype,
            }
            params.update(self.base_params)
            files = {'userfile': ('ccc.jpg', im)}
            r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
            return r.json()
    
        def ReportError(self, im_id):
            """
            im_id:报错题目的图片ID
            """
            params = {
                'id': im_id,
            }
            params.update(self.base_params)
            r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
            return r.json()
    
        
    # 这一块将数据替换
    if __name__ == '__main__':
    	chaojiying = Chaojiying_Client('超级鹰用户名', '超级鹰用户名的密码', '96001')	#用户中心>>软件ID 生成一个替换 96001
    	im = open('a.jpg', 'rb').read()													#本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
    	print chaojiying.PostPic(im, 1902)												#1902 验证码类型  官方网站>>价格体系 3.4+版 print 后要加()
    
    • 经过测试可以识别出来:(账号题分也扣除了)
    • 请添加图片描述

三、古诗文网验证码识别实战

1. 需求:

  • 识别古诗文网登录页面中的验证码。

2. 使用打码平台识别验证码的编码流程

  • 将验证码图片进行本地下载
  • 调用平台提供的示例代码进行图片数据识别

3. 代码:修改账号,密码以及软件ID等等具体数据(超级鹰)

import requests
from lxml import etree
from chaojiying import Chaojiying_Client # 就是样例里面的代码引入 class 类Chaojiying_Client
# 封装识别验证码图片的函数
def getCodeText(imgPath, codeType):
    chaojiying = Chaojiying_Client('超级鹰账号', '密码', '软件ID')  # 用户中心>>软件ID 生成一个替换 96001
    im = open(imgPath, 'rb').read()  # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
    print(chaojiying.PostPic(im, codeType))
    return chaojiying.PostPic(im, codeType)['pic_str']

headers = {
    'User-Agent':'Moz。。。'
}
url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
page_text = requests.get(url=url, headers=headers).text

# 解析验证码图片img中src属性值
tree = etree.HTML(page_text)
# //*[@id="imgCode"]  定位到元素直接复制xpath路径
img_src = tree.xpath('//div[@class="mainreg2"]/img[@id="imgCode"]/@src')[0]
img_src = 'https://so.gushiwen.cn' + img_src
# print(img_src)
# 获取图片并保存下来
img_data = requests.get(url=img_src,headers=headers).content
with open('./yzm.jpg','wb') as fp:
    fp.write(img_data)
    print('下载结束')

# 调用打码平台的示例程序进行验证码图片数据识别
code_text = getCodeText('yzm.jpg',1902)
print("识别结果为:", code_text)
// 会返回以下些数据 PostPic
{
  'err_no': #,
  'err_str': '#',
  'pic_id': '#',
  'pic_str': '#',
  'md5': '#'
}

四、模拟登录(结合下面五的无状态概念)

爬取基于某些用户的用户信息。(需要先登录)

1. 需求:

  • 对古诗文网进行模拟登录:https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx
    • 点击登录按钮之后会发起一个post请求
    • post请求中会携带登录之前录入的相关的登录信息(用户名,密码,验证码。。。。。)
    • 验证码:每次请求都会变化

2. 编码流程

  • 验证码的识别,获取验证码图片的文字数据
  • 对post请求进行发送(处理请求参数)
  • 对响应数据进行持久化存储

3. 代码:修改密码账号等等,本实战注意可能会产生session的请求都用session发起

  • 若是不用requests.Session(),会出现请求不到个人主页的页面数据的情况,原因会在下面的五中解释。后面会提到cookie
  • 可以把session的部分用requests.get/post来替代,看一下效果。
  • 此代码也修改了一部分代码,只有一个post请求(不完整,但是用了session,可以成功获得个人页面数据)。
  • 按原来的设想,是两次请求:
    • 第一次的post请求,是请求用户登录;(可以登录成功,返回码200)
    • 第二次的get请求,是请求用户个人主页的页面数据。(但是要考虑到无状态的问题,所以请求回来的页面无数据);
  • 本实战代码是:post请求登录成功后,页面会直接跳转到个人主页,url是一样的,(用了session)所以post请求后,保存返回的数据就是个人页面的数据
import requests
from lxml import etree
from chaojiying import Chaojiying_Client


# 封装识别验证码图片的函数
def getCodeText(imgPath, codeType):
    chaojiying = Chaojiying_Client('超级鹰账号', '密码', '软件ID')  # 用户中心>>软件ID 生成一个替换 96001
    im = open(imgPath, 'rb').read()  # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
    # print(chaojiying.PostPic(im, codeType))
    return chaojiying.PostPic(im, codeType)['pic_str']

session = requests.Session()

# 1. 对验证码图片进行捕获和识别
headers = {
    'User-Agent': 'Mozilla。。。'
}

url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
page_text = session.get(url=url, headers=headers).text
# print(page_text)
tree = etree.HTML(page_text)

img_src_code = 'https://so.gushiwen.cn' + tree.xpath('//*[@id="imgCode"]/@src')[0]
# print(img_src_code)
img_code_data = session.get(url=img_src_code, headers=headers).content
with open('./gushi.jpg', 'wb') as fp:
    fp.write(img_code_data)
    # print('存储结束')

# 使用超级鹰打码平台提供的示例代码对验证码图片进行识别
results = getCodeText('./gushi.jpg', 1004)

# 获取动态变化的请求值
__VIEWSTATE = tree.xpath('//*[@id="__VIEWSTATE"]/@value')[0]
__VIEWSTATEGENERATOR = tree.xpath('//*[@id="__VIEWSTATEGENERATOR"]/@value')[0]

# post 请求的发送(模拟登录)
login_url = 'https://so.gushiwen.cn/user/login.aspx?from=http%3a%2f%2fso.gushiwen.cn%2fuser%2fcollect.aspx'


# 模拟登录
data = {
    '__VIEWSTATE': __VIEWSTATE,
    '__VIEWSTATEGENERATOR': __VIEWSTATEGENERATOR,
    'from': 'http://so.gushiwen.cn/user/collect.aspx',
    'email': '账号修改',
    'pwd': '密码',
    'code': results, # 验证码
    'denglu': '登录',
}

login_text = session.post(url=login_url,headers=headers,data=data).text
with open('test.html','w',encoding='utf-8') as fp:
    fp.write(login_text)
    
# 通用方法验证登录成功   (一般情况下)
#response = session.post(url=login_url, headers=headers, data=data)
#print(response.status_code) # 200 响应成功

五、模拟登录cookie操作

1. http/https协议特性:无状态

无状态即:当用户向服务器端发起请求之后,这个服务器端并不会记录当前用户相关的用户状态。
# 对应到程序中
	我们想要爬取古诗文网的当前用户主页,我们会发两次请求,其中第二次请求是想获得“个人页的信息”,那么我们是想让服务器端知道,在第二次的get请求的发送之前,
我们已经“登录通了”。(让服务器端知道我们是在登录后才发起的第二次请求,这样才有用)。

但是,在服务器端是不会保留用户的用户状态。
也就是说,在第一次POST请求成功后,我们是可以进行模拟登录成功的,但是服务器端并不会记录当前请求对应的一个状态。即当我们第二次对个人主页发起请求的时候,
服务器端并不会知道,这个请求是基于当前用户已经“登录的状态”发起的请求,服务器端无任何记录。这就是程序没有请求到对应页面数据的原因

2. 没有请求到对应页面数据的原因:(接着古诗文网的案例,程序若按照发起两次请求来编写)

  • 发起的第二次基于个人主页页面请求的时候,服务器端并不知道该请求是基于登录状态下的请求。(就不会响应回对应的个人主页数据)

3. cookie(这个问题可以用cookie解决)

(1)简单概括:
  • cookie由服务器端所创建,最终存储在客户端的。
(2)cookie最本质的作用:
  • cookie:用来让服务器端记录客户端的相关状态。
    • 手动处理:通过抓包工具获取cookie值,将该值封装到header中。(不建议)
    • 自动处理:(前提要明白cookie的来源)
      • cookie值的来源是哪里?(结合作用可知:cookie可能是进行模拟登录所发起的post请求后,由服务器端所创建的)
        • 模拟登录post请求后,有服务器端创建。
      • session会话对象:(携带了cookie的session对象)
        • 作用:
          • 1.可以进行请求的发送
          • 2.如果请求过程中产生了Cookie,则该cookie会被自动存储/携带在该session对象中。
          • 当我们第一次进行模拟登录所发起的post请求后,如果我们使用session对象请求发送,该次请求会让服务器端产生cookie,这个cookie会自动被存储到session对象中。那第二次发起的get请求是需要携带cookie的,就可以用此session对象发起请求。则个人主页数据就可以爬取到
      • 先创建一个session对象:session = requests.Session()
      • 使用session对象进行模拟登录post请求的发送(Cookie就会被存储在session中)
      • session对象对个人主页对应的get请求进行发送(携带了Cookie,这组cookie值表示的就是服务器端用来记录客户端的一个登录状态)。意味着携带了cookie值进行get请求发送,当请求成功之后,服务器端就知道此次请求是在登录状态的基础上进行的请求发送。
(3)分析
  • 使用抓包工具分析:在request headers发现这个浏览器对此url发起请求时,是携带了Cookie值的;爬虫中的验证码识别(简介以及打码平台,包括实战,cookie的作用,session对象的介绍)_第1张图片

  • 这个cookie表示的是什么含义?

    • 这个值表示的就是用来让服务器端记录客户端状态的一组数据值;
    • 当浏览器对服务器端发起请求的过程中,携带了此请求头(包含cookie),cookie值会发生给服务器端,服务器端接收到这组值后就可以通过对这组cookie值的判定,用来获取这次请求是基于登录状态所对应的请求。意味着服务器端就可以响应回客户端对应用户的相关用户数据。
  • 用抓包工具分析:在Response Headers中发现Set-Cookie,这个参数表示的就是当这次对应的url的post请求,请求成功之后就让我们的服务器端为当前请求所对应的客户端,创建的,设置的一组Cookie值;这组Cookie值就是让服务器端用来记录当前客户端的登录状态。

  • 由此可以知道,我们第二次get请求所需要的Cookie值,是由第一次post请求,请求之后由服务器端所创建的;如果可以获取服务器端所创建的这个cookie值(Set-Cookie)之后,将此Cookie值交给第二次get请求携带发送,就可以获取对应个人主页的页面数据了。

4. 代码:修改密码账号等等

import requests
from lxml import etree
from chaojiying import Chaojiying_Client


# 封装识别验证码图片的函数
def getCodeText(imgPath, codeType):
    chaojiying = Chaojiying_Client('超级鹰账号', '密码', '软件ID')  # 用户中心>>软件ID 生成一个替换 96001
    im = open(imgPath, 'rb').read()  # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
    # print(chaojiying.PostPic(im, codeType))
    return chaojiying.PostPic(im, codeType)['pic_str']

# 创建一个session对象
session = requests.Session()
# 1. 对验证码图片进行捕获和识别
headers = {
    'User-Agent': 'Mozilla。。。'
}

url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
page_text = session.get(url=url, headers=headers).text
# print(page_text)
tree = etree.HTML(page_text)

img_src_code = 'https://so.gushiwen.cn' + tree.xpath('//*[@id="imgCode"]/@src')[0]
# print(img_src_code)
img_code_data = session.get(url=img_src_code, headers=headers).content
with open('./gushi.jpg', 'wb') as fp:
    fp.write(img_code_data)
    # print('存储结束')

# 使用超级鹰打码平台提供的示例代码对验证码图片进行识别
results = getCodeText('./gushi.jpg', 1004)

# 获取动态变化的请求值
__VIEWSTATE = tree.xpath('//*[@id="__VIEWSTATE"]/@value')[0]
__VIEWSTATEGENERATOR = tree.xpath('//*[@id="__VIEWSTATEGENERATOR"]/@value')[0]

# post 请求的发送(模拟登录)
login_url = 'https://so.gushiwen.cn/user/login.aspx?from=http%3a%2f%2fso.gushiwen.cn%2fuser%2fcollect.aspx'
# 模拟登录
data = {
    '__VIEWSTATE': __VIEWSTATE,
    '__VIEWSTATEGENERATOR': __VIEWSTATEGENERATOR,
    'from': 'http://so.gushiwen.cn/user/collect.aspx',
    'email': '账号',
    'pwd': '密码',
    'code': results,
    'denglu': '登录',
}



# 使用session进行post请求的发送(这个请求发送成功后,若是产生cookie,这个cookie会自动存储在session对象中)
response = session.post(url=login_url,headers=headers,data=data)
print(response.status_code)
# 爬取当前用户的个人主页对应的页面数据
detail_url = 'https://so.gushiwen.cn/user/collect.aspx' # 其实本实战中当登录成功就会跳转到个人主页,即url是一样的

# 手动cookie处理
# headers = {
#     'Cookie':'XXXX' # 在抓包工具中获取cookie值
# }

# 使用携带了cookie的session进行get请求的发送
detail_page_text = session.get(url=detail_url,headers=headers).text
with open('test.html','w',encoding='utf-8') as fp:
    fp.write(detail_page_text)
    print('存储结束')

你可能感兴趣的:(python,爬虫,爬虫,python,开发语言)