Python访问有验证码的网站

描述:

        本篇博文针对有验证码的网站算是初阶一个爬取,万字发文,不足指出。只希望能帮助屏幕前的你。

目标:绕过登录 进入到主页面

古诗文网-古诗文经典传承

本次案例使用技术点
    1、源码获取:requests
            ● requests简介及基本使用
    2、源码解析:bs4
            ● bs4的简介及基本使用
    3、session会话
            ● session的简介及基本使用

🏆一、技术点基础知识

⭐️1.1、requests简介及基本使用

requests_简介

        在使用urllib库时可以发现,虽然这个库提供了很多关于HTTP请求的函数,但是这些函数的使用方式并不简洁,仅仅实现一个小功能就要用到一大堆代码。因此,Python提供了一个便于开发者使用的第三方库—requests。

        requests库自称“HTTP for Humans”,直译过来的意思是专门为人类设计的HTTP库,能够被开发人员安全地使用。

        requests是基于Python开发的HTTP 库,与urllib标准库相比,它不仅使用方便,而且能节约大量的工作。实际上,requests是在urllib的基础上进行了高度的封装,它不仅继承了urllib的所有特性,而且还支持一些其它的特性,比如使用Cookie保持会话、自动确定响应内容的编码等,可以轻而易举地完成浏览器的任何操作。

requests库中提供了如下常用的类:

  • requests.Request:表示请求对象,用于准备一个请求发送到服务器。

  • requests.Response:表示响应对象,其中包含服务器对HTTP请求的响应。

  • requests.Session:表示请求会话,提供Cookie持久性、连接池和配置。

requests_基本使用

import requests

url = 'http://www.baidu.com'

response = requests.get(url)

print(response)

# 一个类型六个属性
# Response类型

# 2、设置响应的编码
response.encoding = 'utf-8'

# 1、 以字符串的形式来返回网页的源码
print(response.text)


# 3、r.url 	:		返回一个url地址
print('3' + response.url)

# 4、r.content	 :	返回的是一个二进制数据
print(response.content)

# 5、r.status_code :	响应的状态码
print(response.status_code)

# 6、r.headers 	:	响应的头信息
print(response.headers)

requests_get请求

# requests
# (1)一个类型以及六个属性

# 总结
# (1)参数使用params传递
# (2)参数无需urlencode编码
# (3)不需要请求对象的定制
# (4)请求资源路径中的?可以加也可以不加

requests_post请求

# 总结
# (1)post请求 是不需要编解码
# (2)post请求的参数是data
# (3)不需要请求对象的定制
import requests

url = 'https://fanyi.baidu.com/v2transapi?from=en&to=zh'

headers = {
    'Cookie': 'BAIDUID=BBABB2B1CFF360318D8768BB9341483B:FG=1; BIDUPSID=BBABB2B1CFF360318D8768BB9341483B; PSTM=1650434664; BDUSS=I1dzdkeXJvTENqdTZHM3NCMG9oeC02T25ZS05oN1dIWH5IUnlpM3R5bVhYb2hpSVFBQUFBJCQAAAAAAAAAAAEAAABgALTSzsTX1r79xMUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJfRYGKX0WBiQU; BDUSS_BFESS=I1dzdkeXJvTENqdTZHM3NCMG9oeC02T25ZS05oN1dIWH5IUnlpM3R5bVhYb2hpSVFBQUFBJCQAAAAAAAAAAAEAAABgALTSzsTX1r79xMUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJfRYGKX0WBiQU; APPGUIDE_10_0_2=1; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; BAIDUID_BFESS=BBABB2B1CFF360318D8768BB9341483B:FG=1; delPer=0; PSINO=3; BA_HECTOR=al0h2h0g0hak8g21a5055cvv1hjramt1b; ZFY=RHUy2wIJtgAERrU2VTGqVqGuZCPU410LZaALyEaZ1LQ:C; BDRCVFR[shpneSZgko0]=mk3SLVN4HKm; ZD_ENTRY=baidu; H_PS_PSSID=26350; BDORZ=FFFB88E999055A3F8A630C64834BD6D0; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1663855974,1664876562,1664988007; ab_sr=1.0.1_ZjNjZGFjNzJkZWExZTBiNDQ5ZjQyZDVlMWY4ZTNhNzdlZjEyMDQ4NTRjM2JiNzQzN2U4NzYzMWM5Yjc1NjBiNWVjNzRmZTMxYmViNGY0ZmIyODJiNjE3NzgyN2JiZTBkNTkyYTQ5ZDhhZmVhNDFlNmNkYjAxN2NmMDc2MzBhZGJiZTIxN2VlMWYzNDY1NzE5MzY3MDMxMDk3MjMyYmJlNzI1NmI0ZWI0ZTgwMTAzNGM2OGUzOWQ0MWMwOGI1NjEy; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1664988336',
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
}
data = {
    'query':'eye',
    'simple_means_flag':'3',
    'sign':'67056.386753',
    'token':'ba3ba725180ddbbd405f6ed877333c40',
    'domain':'common'
}

response = requests.post(url=url,data=data,headers=headers)

content = response.text
# JSON数据看不懂 使用json load方法转换
import json
# obj = json.loads(content)
obj = json.loads(content,encoding='uft-8') # 这两种方式都能utf-8
print(obj)

⭐️1.2、bs4的简介及基本使用

bs4_简介

BeautifulSoup简称:

        bs4

什么是BeatifulSoup?
    BeautifulSoup库,和lxml一样,是一个html的解析器,主要功能也是解析和提取数据。是Python的一个非常好用的第三方库
优缺点?
    缺点:效率没有lxml的效率高
    优点:接口设计人性化,使用方便

目前主流的常用的可以解析和提取网页中的数据的第三方库(插件)

  • Xpath
  • JsonPath
  • BeautifulSoup

bs4_基本使用

from bs4 import BeautifulSoup

# 通过解析本地文件 来将bs4的基础语法进行讲解
# 默认打开的文件的编码格式是gbk 所以在打开文件的时候 需要指定编码
soup = BeautifulSoup(open('075_解析_bs4的基本使用.html',encoding='utf-8'),'lxml')


# 根据标签名查找节点
# 找到的是第一个符合条件的数据
print(soup.a)
# 获取标签的属性和属性值
print(soup.a.attrs)

# bs4的一些函数

# 1、find
# 返回第一个符合条件的数据
print(soup.find('a'))

# 根据title的值来找到对应的标签对象
print(soup.find('a',title='a2'))

# 根据class的值来找到对应的标签对象 注意的是clas需要添加下划线
print(soup.find('a',class_='a1'))

# 2、find_all
# 返回的是一个列表 并且返回了所有的的a标签
print(soup.find_all('a'))

# 如果想获取的是多个标签的数据 那么需要在find_all的参数中添加的是列表的数据
print(soup.find_all(['a','span']))

# limit作用是查找前几个数据
print(soup.find_all('li',limit=2))

# 3、select
# select方法返回的是一个列表并且会返回多个数据
print(soup.select('a'))

# 可以通过.代表class 我们把这种操作叫做类选择器
print(soup.select('.a1'))

# 可以通过#代表id 我们把这种操作叫做类选择器
print(soup.select('#l1'))

# 属性选择器 -- 通过属性来寻找对应的标签
# 查找到li标签中有id的标签
print(soup.select('li[id]'))

# 查找到li标签中id为l2的标签
print(soup.select('li[id = "l2"]'))

# 层级选择器
# 后代选择器
# 找到div下面的li
print(soup.select('div li'))

# 子代选择器
# 某标签的第一级子标签
# 注意 很多的计算机编程语言中 如果不加空格不会输出内容 但是在bs4中 不会报错
print(soup.select('div > ul > li'))

# 找到a标签和li标签的所有的对象
print(soup.select('a,li'))

# 节点信息
# 获取节点内容
obj = soup.select('#d1')[0]
# 如果标签对象中只用内容 那么string和get_text() 都可以使用
# 如果标签对象中除了内容还有标签那么string就获取不到数据 而get_text()是可以获取到数据的
# 一般情况下 推荐使用get_text()
print(obj.string)
print(obj.get_text())

# 节点的属性
obj = soup.select('#p1')[0]
# name是标签的名字
print(obj.name)
# 将属性值作为一个字典返回
print(obj.attrs)

# 获取节点的属性
obj = soup.select("#p1")[0]
print(obj.attrs.get('class'))
print(obj.get('class'))
print(obj['class'])

⭐️1.3、session的简介及基本使用

session简介

        session是一个英语单词,意思有开会、会议等。在计算机中,尤其是在网络应用中,称为“会话控制”。服务器会给每一个用户(浏览器)创建一个session对象。一个session独占一个浏览器,只要浏览器不关闭,这个session就一直存在。主要用来存储用户会话所需的属性和配置信息。这样,用户在应用程序的web页面跳转时,存储在session对象中的用户信息不会丢掷,而是在整个用户会话中一直保持下去。

session的经典应用场景

一个用户登陆后,访问该网站的其他网页时,一直处于登录状态。保存购物车信息等等

session在Java中的常用方法

 session在Python中的常用方法

  • 使用场景:当接口之间有cookie数据之间的传递的情况下
  • 为了确保接口之间cookie数据传递,一定要使用同一个session对象
  • 接口返回的cookie数据,存储在session对象中
# 1.创建session对象

session = requests.session()

🏆二、案例思路步骤

⭐️2.1、思路+步骤

        第一步,注册账号成功登录网站。查看成功登录时, 请求头携带的参数信息,通过登录接口我们发现,登录的时候需要的参数很多。如下:

 __VIEWSTATE: KuGEB7zpLm8OAtLXIcJ3sZ9WWNSCJ2SiE/voMyK0uNXZHd+ofgLPi1CdI5vP58TTEQCLezEvbpe98b9HaVM9eS93FzoIjMr1PTGqEgN9+icUh7cgshk1IQJgKbvl5HY3JHovH14eSPYVGgl1fo9n/C5H/dw=
 __VIEWSTATEGENERATOR: C93BE1AE
 from: http://so.gushiwen.cn/user/collect.aspx
 email: # 用户名
 pwd: # 密码
 code: 96d0
 denglu: 登录

 F12从信息中提取url、请求方式

# url = 'https://so.gushiwen.cn/user/login.aspxfrom=http://so.gushiwen.cn/user/collect.aspx'
# Request Method: GET

 第二步,分析观察请求头中携带的参数,哪些是变量(变化)哪些是常量(不变化)。

变量:
    __VIEWSTATE
    __VIEWSTATEGENERATOR
    code
常量:
    from
    email
    pwd
    denglu

技巧:

        一般情况下,参数的携带所见即所得,通过表单的输入框进行参数的填写传递。如果不是用户(自己)输入的参数进行传递,那么这个参数极有可能隐藏在表单的某个地方。在用户点击登录的那一刻,达到隐藏参数的传递效果。 

通俗讲:眼睛不到的参数 在页面源码中

  1. 通过源码查找 __VIEWSTATE、__VIEWSTATEGENERATOR确实在页面的源码中 所以我们需要获取到页面源码 进行解析获取即可
  2. code 验证码稍后分析

 OK,那么到这里,可以开始初步编码了,先拿到页面源码。

import requests
# 网站接口
url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'

headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}

response = requests.get(url=url,headers = headers)
content = response.text # 字符串的形式返回网页的源码

print(content) # 输出测试 网站是否有更高级的反爬手段

效果图

        页面源码获取到之后,在源码的基础上利用bs4解析,获取到我们想要的数据。编码如下,所有的print()语句均为开发阶段测试语句,测试通过之后可以删除,也可以整理备份一下,代码中只保留重要的注释即可

from bs4 import BeautifulSoup
# 打开文件并指定编码
soup = BeautifulSoup(content,'lxml')

viewState =soup.select('#__VIEWSTATE')[0].attrs.get('value') # 获取节点信息并返回对应的节点属性
print(viewState)

viewStateGenerator =soup.select('#__VIEWSTATEGENERATOR')[0].attrs.get('value') # 同理
print(viewStateGenerator)

效果图

第三步,验证码三个变量已经处理好了两个,最后一个验证码现在搞。

直接定位到验证码图片所在处,找到验证码图片的所属id并获取其属性src保存到本地。

imgShortCode =soup.select('#imgCode')[0].attrs.get('src') # 同理
print(imgShortCode)

效果图

为得到完整的图片链接,需要对现有的短路径链接进行组装拼接 。

imgCode = 'https://so.gushiwen.cn' + imgShortCode
# print(imgLink) # 输出结果: https://so.gushiwen.cn/RandCode.ashx
  1. 将验证码图片下载到本地,查看验证码,在控制台数据输入,将输入数据给到参数名称code,登录古诗文网站。
  2. 图片下载,第一时间会想到用urllib的urlretrieve方法,这是对的,但是不适用当前的验证码场景,目前先使用urllib图片下载方式,并在后面会说明并演示情况在修改,之所以这样做就是为了了解现象,加深印象。
  3. 纸上得来终觉浅,绝知此事要躬行。没关系,学习阶段吃的苦头越多,记忆越深刻,生产环境中所犯下的错也会尽可能的比例缩小。OK!上码!!!
import urllib.request
urllib.request.urlretrieve(url=imgLink,filename='CodeDownloadName.jpg')

效果图

知识扩展:

        Preserve log是否勾选是针对于当前Chrome浏览器的版本高低,如果Chrome的版本过低。在不勾选Preserve log的情况下,当进行页面登录抓取接口参数信息的时候,是没有信息保存的。高版本则没有这个问题,放心用。

         第四步,抓取登录接口,准备进行登录用例的测试。这里采用的方式是将验证码下载到本地,本地人工查看控制台输入验证码进行登录。

清空控制台

 点击登录

codeInput = input('验证码输入')

# 登录接口
urlPost = 'https://so.gushiwen.cn/user/login.aspx?'
# Request Method: POST

dataPost = {
    '__VIEWSTATE': viewState,
    '__VIEWSTATEGENERATOR': imgShortCode,
    'from': 'http://so.gushiwen.cn/user/collect.aspx',
    'email': '#自己的注册邮箱', 
    'pwd':   '#自己的注册密码', 
    'code': codeInput,
    'denglu': '登录'
}

# 访问
# responsePost = requests.post(url=urlPost,data=dataPost,headers=headers)
responsePost = requests.post(url=urlPost,data=dataPost,headers=headers)
contentPost = responsePost.text
print(contentPost)

ok,开始输入验证码测试。

        一次,<script>alert('提交失败,您输入的验证码有误!');history.back();</script></form>。

        二次,<script>alert('提交失败,您输入的验证码有误!');history.back();</script></form>。

        三次 <script>alert('提交失败,您输入的验证码有误!');history.back();</script></form>。

        哎?为啥四个字母+数字的验证码怎么输入都输不对呢?这个地方一开始自己也没想到,后面知道后才恍然大悟,其原理还是比较简单的。 

         因为验证码每次请求都会刷新的缘故,在前面我们也亲手测试过,只要页面刷新验证码就会变化,每次都不一样的。

        综上所述。所以 ,我只需要考虑如何去保证我第一次下载的验证码和第二次携带请求的验证码保持一致,怎么搞呢?浏览器中存储参数用户登录信息,基本都是使用cookie && session实现的,可以往这个方向考虑下。

接下来就到了修改我们的代码了,前面说的urllib的图片下载方式的时候是有点问题,还记得吗。鼠标“滚回去”注释。

# 注释代码
# import urllib.request
# urllib.request.urlretrieve(url=imgLink,filename='imgDownloadName.jpg')

 那么在怎么修改呢,需要注意的就是图片的下载要使用二进制

# requests中有一个方法 .session()方法

requests 的所有请求方法,底层都是调用的这个类的对象。
其他方法和 session 的区别在于:

        直接使用 requests 调用请求方法发送请求,每次都会创建一个新的 session(会话对象),所以没有之前请求的 cookies 信息

        直接创建一个 session 对象来发请求,那么每次发请求用的都是这个会话对象,所有能够保存之前的会话信息(cookies 数据)

session = requests.session()
# 通过链接访问 返回数据
responseCode = session.get(imgCode)
# 然后将返回数据 下载到本地 以二进制的形式保存到本地
contentCode = responseCode.content
# 然后仍旧以二进制的形式读取到验证码 wb模式就是将二级制数据写入到文件
with open('CodeDownloadImg.jpg','wb') as fp:
    fp.write(contentCode)

并修改下方登录接口的post请求,将

responsePost = requests.post(url=urlPost,data=dataPost,headers=headers)

修改为

responsePost = session.post(url=urlPost,data=dataPost,headers=headers)

再次测试效果图

 在看到控制台如下打印信息的时候,就代表成功了。

 稍微改进下代码,将print()语句替换为文件下载代码,将输出的代码下载到本地进行预览。

# print(contentPost) # <head><meta http-equiv="Cache-Control" content="no-siteapp" /><meta http-equiv="Cache-Control" content="no-transform " /><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>
	# 我的收藏_古诗文网
# </title>
# 测试成功 可以将文件写入到本地预览效果图了


with open('gushiwen.html','w',encoding='utf-8') as fp:
    fp.write(contentPost)

预览效果图

⭐️2.2、完整源码

本次的Python案例,综合性还是比较强的。所以就自己边打边记录,诞生此博客。

import requests
from bs4 import BeautifulSoup

# 网站接口
url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'

headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}

response = requests.get(url=url,headers = headers)
content = response.text

# bs4解析
soup = BeautifulSoup(content,'lxml')

viewState =soup.select('#__VIEWSTATE')[0].attrs.get('value')

viewStateGenerator =soup.select('#__VIEWSTATEGENERATOR')[0].attrs.get('value')

# 验证码
imgShortCode =soup.select('#imgCode')[0].attrs.get('src') # 同理

# 拼接
imgCode = 'https://so.gushiwen.cn' + imgShortCode

# session
session = requests.session()

responseCode = session.get(imgCode)

contentCode = responseCode.content

with open('CodeDownloadImg.jpg','wb') as fp:
    fp.write(contentCode)

codeInput = input('验证码输入')


# 登录接口
urlPost = 'https://so.gushiwen.cn/user/login.aspx?'

dataPost = {
    '__VIEWSTATE': viewState,
    '__VIEWSTATEGENERATOR': imgShortCode,
    'from': 'http://so.gushiwen.cn/user/collect.aspx',
    'email': '自己的注册邮箱',
    'pwd': '自己的注册密码',
    'code': codeInput,
    'denglu': '登录'
}

# 访问
responsePost = session.post(url=urlPost,data=dataPost,headers=headers)
contentPost = responsePost.text

with open('ancient_poetry.html','w',encoding='utf-8') as fp:
    fp.write(contentPost)

🏆三、总结

本次案例我觉得

  • requests.session()这里
  • 文件下载使用二进制包括二进制读取文件,在之前这些方法用到的场景比较少,有点略显生疏。

文章到这里告一段落,感谢大家!有帮助记得点赞+评论喔,您的支持,我的动力!

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妙趣生花

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值