requests 是用 python 语言编写的一个开源的HTTP库,可以通过 requests 库编写 python 代码发送网络请求,其简单易用,是编写爬虫程序时必知必会的一个模块。
requests 模块的作用
发送网络请求,获取响应数据。
中文文档: https://docs.python-requests.org/zh_CN/latest/index.html
requests 模块的安装
安装命令如下:
pip install requests
或者
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple
具体安装运行如下图所示:
查看安装好的 requests 模块的信息
pip show requests
具体运行如下图所示:
requests 模块的基本使用
知识点:
- 掌握 requests 发送 GET 请求
- 掌握 response 对象的基本属性
- 掌握 response.text 和 response.content 的区别
- 掌握 requests 发送自定义请求头的方式
- 掌握 requests 发送带参数的get请求
1. requests 发送 GET 请求
使用 requests 模块发送 GET 请求的语法是: requests.get(url), 调用完该方法之后会返回一个 response 响应对象。
需求:通过 requests 向百度首页发送请求,获取百度首页的数据。
# 1. 导入request模块
import requests
# 2. 准备目标url地址
url = 'https://www.baidu.com'
# 3. 向目标url地址发送get请求
response = requests.get(url)
# 4. 打印响应内容
print(response.text)
运行代码结果如下:
2. response 响应对象
观察上边代码运行结果发现,有好多乱码;这是因为编解码使用的字符集不同早造成的;我们尝试使用下边的办法来解决中文乱码问题。
代码如下:
# 1. 导入request模块
import requests
# 2. 准备目标url地址
url = 'https://www.baidu.com'
# 3. 向目标url地址发送get请求
response = requests.get(url)
# 4. 打印响应内容
# print(response.text)
print(response.content.decode()) # 原始响应内容,类型bytes:没有进行解码响应内容
# 注意:爬虫程序默认不会自动保存cookie,爬虫程序请求时默认也不会自动携带cookie
运行代码结果如下:
2.1 response.content 和 response.text 的区别
response.content
- 返回类型: bytes
- 解码类型: 没有指定,原始响应内容,没有进行解码
- 指定编码方式: response.content.decode('指定编码字符集')
- 注意: response.content.decode() 默认使用 utf-8 编码方式
response.text
- 返回类型: str
- 解码类型: requests 模块自动根据 HTTP 头部对响应的编码作出有根据的推测,推测的文本编码
- response.text = response.content.decode('推测出的编码字符集')
获取网页源码的方式:
- response.content.decode()
- response.content.decode('gbk')
- response.text
以上三种方法从前往后尝试,能够100%的解决所有网页解码的问题, 推荐优先使用: response.content.decode()
2.2 response 响应对象其他属性和方法
- response.url : 响应的url地址,有时候响应的 url 地址和请求的 url 地址不一样。
- response.status_code : 获取响应状态码。
- response.request.headers : 获取响应对应的请求头信息。
- response.headers : 获取响应头信息。
- response.request._cookies : 响应对应请求携带的cookie,返回cookieJar类型。
- response.cookies : 响应时设置的 cookie,返回cookieJar类型。
- response.json() : 自动将 json 字符串类型的响应内容转换为 python 对象(dict or list)。
示例代码如下:
# 1. 导入request模块
import requests
# 2. 准备目标url地址
url = 'https://www.baidu.com'
# 3. 向目标url地址发送get请求
response = requests.get(url)
# 4. 打印响应内容
print(response.url) # 打印响应的url
print(response.status_code) # 打印响应的状态码
print(response.request.headers) # 打印响应对象的请求头
print(response.headers) # 打印响应头
print(response.request._cookies) # 打印请求携带的cookies
print(response.cookies) # 打印响应设置的cookies
示例代码运行结果如下:
2.3 练习-保存网络图片
需求: 将图片https://i-blog.csdnimg.cn/direct/d145c8acdfac46f0bf9ab2c9316c22e0.png 保存到本地。
思考:
- 以什么方式打开文件。
- 保存什么格式的内容。
分析:
- 图片的url地址: https://i-blog.csdnimg.cn/direct/d145c8acdfac46f0bf9ab2c9316c22e0.png
- 利用 requests 模块发送请求,获取到图片的响应。
- 以二进制的方式打开文件,并将 response 响应的二进制内容写入到文件。
完整代码如下:
# 1. 导入request模块
import requests
# 2. 准备目标url地址
url = 'https://i-blog.csdnimg.cn/direct/d145c8acdfac46f0bf9ab2c9316c22e0.png'
# 3. 向目标url地址发送get请求
response = requests.get(url)
# 4. 打开文件,将数据写入到文件中
with open('itcast.png', 'wb') as f:
# 写入响应内容的二进制数据
f.write(response.content)
代码运行结果如下:
图片已保存在同一目录下。
2.4 练习-保存多张网络图片
需求: 将网页中的图片保存到本地。
地址:https://www.tupianzj.com/meinv/20210219/224797.html
思考:
- 以什么方式打开文件。
- 保存什么格式的内容。
分析:
- ① 先请求 https://www.tupianzj.com/meinv/20210219/224797.html,获取响应内容
- ② 从上一步的响应内容中提取所有图片的地址
- ③ 遍历每一个图片地址,向每个图片地址发送请求,并将响应的内容保存成图片文件
完整代码如下:
import re
import requests
# 思路
# ① 先请求 https://www.tupianzj.com/meinv/20210219/224797.html,获取响应内容
# ② 从上一步的响应内容中提取所有图片的地址
# ③ 遍历每一个图片地址,向每个图片地址发送请求,并将响应的内容保存成图片文件
# ① 先请求 https://www.tupianzj.com/meinv/20210219/224797.html,获取响应内容
url = 'https://www.tupianzj.com/meinv/20210219/224797.html'
# 发送请求
response = requests.get(url)
# 获取响应的内容
html_str = response.content.decode('')
HTML1 = response.text
print(html_str)
print(HTML1)
# ② 从上一步的响应内容中提取所有图片的地址
image_url_list = re.findall('<img src="(.*?)"', html_str)
# 去除最后无效地址
image_url_list = image_url_list[:-1]
print(image_url_list)
# ③ 遍历每一个图片地址,向每个图片地址发送请求,并将响应的内容保存成图片文件
for image_url in image_url_list:
# 发送请求
image_response = requests.get(image_url)
# 将响应内容保存成本地图片
filename = image_url[-10:]
with open(filename, 'wb') as f:
f.write(image_response.content)
print('保存图片完毕!!!')
3. requests 请求时设置请求头
在最开始,我们书写了一下代码,获取了一下百度首页的内容:
# 1. 导入request模块
import requests
# 2. 准备目标url地址
url = 'https://www.baidu.com'
# 3. 向目标url地址发送get请求
response = requests.get(url)
# 4. 打印响应内容
print(response.content.decode())
运行以上代码的结果如下:
接下来,我们对比一下,使用浏览器查看的百度的源码和我们代码中拿到的源码有什么区别。
打开浏览器,访问百度首页,然后 鼠标右键 –> 查看网页源代码
可以看到,很明显使用浏览器获取到的百度首页的内容要比使用代码获取到的内容要多得多。这是为什么呢?
回顾爬虫的概念:模拟浏览器,欺骗服务器,获取和浏览器一致的内容, 所以在这里我们需要带上一些请求头信息。
查看一下浏览器的请求头信息:鼠标右键 –> 检查 –> 打开Network –> 地址栏访问百度,抓包,查看请求信息
再在代码中,使用 response.requests.headers 查看使用 requests 模块发送请求时携带的请求头信息:
# 1.导入request模块
import requests
# 2.准备目标url地址
url = 'http://www.baidu.com'
# 3.向目标url地址
# 发送请求
response = requests.get(url)
# 查看请求头
print(response.request.headers)
对比一下,很明显的,代码中的 User-Agent 和 浏览器中的完全不一样,前面我们也说过,User-Agent 是浏览器的身份标识,而代码中直接发送的是 python-requets/2.25.1 这样服务器很明显的就知道我们不是使用正常的浏览器访问服务器,所以返回的数据就比较少。
3.1 设置请求头的语法
response = requests.get(url, headers={})
- headers 参数接收的字典形式的请求头
- 请求头字段名作为字典的 key,字段名对应的值作为字典的 value。
例如:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36
作为 headers 的参数,可以写为:
{"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"}
3.2 设置请求头完整代码实现
# 1. 导入request模块
import requests
# 2. 准备目标url地址
url = 'https://www.baidu.com'
# 准备请求头字典
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"
}
# 3. 向目标url地址发送get请求
response = requests.get(url, headers=headers)
# 4. 查看请求头信息
print(response.request.headers)
# 5. 打印响应内容
print(response.content.decode())
运行结果如下图所示:
很明显,拿到的内容和使用浏览器拿到的网页源码内容就是一样的了。
4. 发送带查询参数的请求
我们在使用百度搜索的时候,经常会发现 URL 地址中会有一个 ? ,该问号后面的就是查询参数,又叫做查询字符串参数。
注意:查询参数的格式为key=value的格式,如果有多个参数,每个参数之间使用 & 符号连接,例如:https://tieba.baidu.com/f?ie=utf-8&kw=python&fr=search。
4.1 requests请求携带查询参数的语法
语法格式如下:
response = requests.get(url, params={})
- params参数接收的是一个字典。
- 查询参数中,等号左边的内容作为字典的 key,等号右边的内容作为字典的 value。
- 注意点:在url地址中, 很多查询参数是没有用的,比如百度搜索的url地址,其中参数只有一个字段有用,其他的都可以删除。如何确定那些请求参数有用或者没用:挨个尝试! 对应的,在后续的爬虫中,遇到很多参数的url地址,都可以尝试删除参数。
4.2 发送带查询参数请求的应用
需求:实现在百度中搜索 csdn
实现方式1:对 百度安全验证 发起请求可以使用 requests.get(url, params=kw) 的方式
实现代码如下所示:
# 1. 导入request模块
import requests
# 2. 准备目标url地址
url = 'https://www.baidu.com/s?'
# 准备请求头字典
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"
}
# 准备请求参数的字典
params = {
"wd": "南京"
}
# 3. 向目标url地址发送get请求
response = requests.get(url, headers=headers, params=params)
# 4. 打印响应的内容
print(response.content.decode())
上述代码运行结果如下图所示:
实现方式2:直接对 百度安全验证 完整的url地址进行请求,不使用 params 参数
实现代码如下:
# 1. 导入request模块
import requests
# 2. 准备目标url地址
url = 'https://www.baidu.com/s?wd=南京'
# 准备请求头字典
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"
}
# 3. 向目标url地址发送get请求
response = requests.get(url, headers=headers)
# 4. 打印响应的内容
print(response.content.decode())
以上代码运行结果如下所示:
总结
- requests 发送 GET 请求的语法: requests.get(url)
- response 对象的属性和方法
- response.text: 获取响应内容的字符串类型,可能会乱码。
- response.content: 获取响应的二进制类型的数据,获取字符串类型的响应数据可以使用 response.content.decode()。一般使用这种方式来获取响应的数据内容。
- response.url : 响应的url地址,有时候响应的 url 地址和请求的 url 地址不一样。
- response.status_code : 获取响应状态码。
- response.request.headers : 获取响应对应的请求头信息。
- response.headers : 获取响应头信息。
- response.request._cookies : 响应对应请求携带的cookie,返回cookieJar类型。
- response.cookies : 响应时设置的 cookie,返回cookieJar类型。
- response.json() : 自动将 json 字符串类型的响应内容转换为 python 对象(dict or list)。
- 发送请求设置请求头的目的: 为了更好的伪装成浏览器,拿到正确的响应数据。
- 发送请求设置请求头的语法: requests.get(url, headers={})。
- 查询参数的格式: 以 ? 开始,key=value 的格式,每个参数之间使用 & 符号连接。
- 发送带查询参数的请求语法: requests.get(url, params={})
requests 模块的深入使用
知识点:
- 了解 GET 和 POST 请求的区别
- 掌握 使用 requests 发送 POST 请求
- 了解 代理IP的分类
- 掌握 requests 模块使用代理IP的方法
1. requests 模块发送POST请求
思考: 那些地方会使用到 POST 请求?
- 登录注册:POST 比 GET 更安全。
- 需要传输大文本内容的时候,POST请求对数据长度没有要求。
所以同样的,我们的爬虫也需要在这两个地方会去模拟浏览器发送post请求。
补充:GET 请求和 POST 请求的区别
后退按钮/刷新 | 无害 | 数据会被重新提交(浏览器应该告知用户数据会被重新提交)。 |
书签 | 可收藏为书签 | 不可收藏为书签 |
历史 | 参数保留在浏览器历史中。 | 参数不会保存在浏览器历史中。 |
对数据长度的限制 | 当发送数据时,GET 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是 2048 个字符)。 | 无限制。 |
对数据类型的限制 | 只允许 ASCII 字符。 | 没有限制。也允许二进制数据。 |
安全性 | 与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分。在发送密码或者其他敏感信息的时候决不能使用GET。 | POST 比 GET 更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中。 |
可见性 | 数据在 URL 中对所有人都是可见的。 | 数据不会显示在 URL 中。 |
1.1 requests 发送 POST 请求语法
语法格式如下:
response = requests.post(url, data={}, headers={}, params={})
- data参数接收的数据类型是字典。
- POST请求体(form data)中,冒号左边的作为字典的 key,冒号右边的作为字典的 value。
如下图所示:
将请求体(form data)中的数据转换为字典:
data = {
"username": "admin",
"password": "admin"
}
1.2 案例-传智健康登录
登录页面 URL 地址:http://manager-health-java.itheima.net/login.do
案例分析
抓包分析,找到登录请求的url地址
-
右键检查 –> Network
-
用户名和密码输入框输入用户名和密码: 用户名 admin,密码 admin,然后点击登录,抓取请求
-
根据抓包发现,登录的 URL 地址为:(登录成功之后会进行重定向到首页,所以响应状态码是302)
http://manager-health-java.itheima.net/login.do
-
请求体数据为:
-
最终分析出:
-
POST 请求 URL 地址为: http://manager-health-java.itheima.net/login.do
-
请求方式为: POST
-
请求体参数为:
data = {
"username": "admin",
"password": "admin"
}
-
案例代码实现
完整代码如下:
import requests
# 1. 准备登录的url地址,请求头,请求体数据
login_url = "http://manager-health-java.itheima.net/login.do"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"
}
data = {
"username": "admin",
"password": "admin"
}
# 2. 发起登录请求
response = requests.post(login_url, data=data, headers=headers)
# 打印登录成功之后的响应状态码和响应的内容
print(response.status_code)
print(response.content.decode())
上述代码运行结果如下:
响应状态码:
200
响应内容部分截图
2. requests 使用代理
每次在使用浏览器请求一个网站的时候,服务器是可以获取到当前客户端的 IP 地址的,使用爬虫程序去请求服务器的速度和频率是特别快的,这样的话,我们使用同一台电脑上的浏览器去请求别人的服务器,会被服务器识别到,这样可能就会将我们的 IP 地址封掉。为了不让服务器将我们的 IP 地址封掉,在发送请求的时候可以使用代理 IP。
2.1 使用代理 IP 的目的
- 让服务器以为是不同的客户端在请求
- 防止我们的真实地址被泄露,防止被追究
2.2 代理的使用过程
-
代理IP 是一个IP,指向的是一个代理服务器
-
代理服务器能够帮我们向目标服务器转发请求
2.3 代理 IP 的分类
根据代理 IP 的匿名程度,代理 IP 可以分为以下三类:
透明代理 | 透明代理虽然可以直接“隐藏”你的IP地址,但是还是可以查到你是谁。 | REMOTE_ADDR = Proxy IP HTTP_VIA = Proxy IP HTTP_X_FORWARDED_FOR = Your IP |
匿名代理 | 使用匿名代理,别人只能知道你用了代理,无法知道你是谁。 | REMOTE_ADDR = Proxy IP HTTP_VIA = Proxy IP HTTP_X_FORWARDED_FOR = Proxy IP |
高匿代理 | 无法发现你是在用代理,所以是最好的选择毫无疑问使用高匿代理效果最好。 | REMOTE_ADDR = Proxy IP HTTP_VIA = not determined HTTP_X_FORWARDED_FOR = not determined |
根据网站所使用的协议不同,需要使用相应协议的代理服务。从代理服务请求使用的协议可以分为:
- http代理:目标url为http协议
- https代理:目标url为https协议
- socks隧道代理(例如socks5代理)等:
- socks 代理只是简单地传递数据包,不关心是何种应用协议(FTP、HTTP和HTTPS等)。
- socks 代理比http、https代理耗时少。
- socks 代理可以转发http和https的请求
2.4 requests 模块中使用代理
为了让服务器以为是不同的客户端在请求;为了防止频繁向一个域名发送请求被封 IP,所以我们需要使用代理 IP;那么我们接下来要学习requests 模块是如何使用代理 IP 的。
语法格式如下:
response = requests.get(url, proxies={})
proxies 参数接收的数据类型为字典。
字典的格式如下:
proxies = {
"协议类型": "协议类型://代理IP地址:端口号"
}
例如:
proxies = {
# 目标地址为 http 协议,会使用 http 这个 key 对应的代理服务
"http": "http://113.121.255.26:9999",
# 目标地址为 https 协议,会使用 https 这个 key 对应的代理服务
"https": "https://219.151.157.130:3128"
}
免费代理 IP 网站
- 快代理: 免费私密代理IP_IP代理_HTTP代理 – 快代理
- 89免费代理:89免费代理IP_优质HTTP代理服务器_免费IP代理服务平台
在学习阶段,我们直接在网上找一些免费的代理去使用即可,免费的代理的质量不是很好,真正在公司会去购买付费的高质量的代理,或者自己去搭建代理服务器。
2.5 案例 – 使用代理 IP 请求唱吧
完整代码如下:
# 1. 导入request模块
import requests
# 2. 准备目标url地址
url = 'http://changba.com/now/stars/index.html'
# 准备请求头字典
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"
}
# 准备代理IP的字典
proxies = {
'http': 'http://113.108.190.50:8080'
}
# 3. 向目标url地址发送get请求
response = requests.get(url, headers=headers, proxies=proxies)
# 4. 打印响应的内容
print(response.content.decode())
运行结果如下:
总结
- GET 和 POST 的区别
- GET 请求发送数据的长度是有限制的,POST请求没有限制
- POST 请求要比 GET 请求安全
- GET 请求传递的数据会显示到 URL 地址中,POST请求不会显示在 URL 地址中。
-
requests 发送 POST 请求的语法: requests.post(url, data={})
-
在爬虫中使用代理IP是为了不让服务器将我们的IP地址封掉。
- requests 使用代理的语法: requests.get(url, proxies={})
- proxies 字典的格式: key 为 协议的类型,value 为 协议://代理IP:端口号
requests 请求携带 Cookie
知识点:
- 了解 爬虫中为什么要使用 Cookie
- 掌握 在请求头中携带 Cookie
- 掌握 使用cookies参数携带 Cookie
- 掌握 使用requests.session 进行状态保持
- 掌握 RequestsCookieJar 类型和字典之间的相互转换
1. 爬虫中使用Cookie
为了能够通过爬虫获取到登录后的页面,或者是解决通过cookie的反扒,需要使用request来处理cookie相关的请求
1.1 爬虫中携带Cookie的利弊
- 带上 Cookie 的好处
- 能够访问登录后的页面
- 能够实现部分反反爬
- 带上 Cookie 的弊端
- 一套cookie往往对应的是一个用户的信息,请求太频繁有更大的可能性被对方识别为爬虫
- 那么上面的问题如何解决 ?使用多个账号
1.2 requests中使用 Cookie的方法
使用requests携带cookie有三种方法:
- cookie字符串放在headers中,设置 Cookie 请求头
- 把cookie字典放传给请求方法的cookies参数接收
- 使用requests提供的session模块发送请求
2. Cookie 添加在 headers 中
网站经常利用请求头中的 Cookie 字段来做用户访问状态的保持,那么我们可以在 headers 参数中设置 Cookie 请求头,模拟普通用户的请求。我们以传智健康登陆为例:
2.1 抓包找到登录之后的Cookie
-
打开浏览器,右键检查,点击 Network,勾选 Preserve log
-
访问传智健康登陆的url地址: 传智健康
-
输入账号和密码之后,点击登录。
-
访问一个登录之后才可以访问的页面,例如: 传智健康
-
找到Network中对应的抓到的包,查看在访问时携带的 User-Agent 和 Cookie 的信息
2.2 完成代码
- 从浏览器中复制User-Agent和Cookie
- 浏览器中的请求头字段和值与headers参数中必须一致
- headers请求参数字典中的Cookie键对应的值是字符串
完整代码如下:
import requests
# 1. 准备目标url地址
url = 'http://manager-health-java.itheima.net/pages/main.html'
# 2. 准备请求头信息,cookie 和 User-Agent,(从浏览器抓包复制)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36",
"Cookie": "JSESSIONID=4BBDB822771C712E3570B8AD3BED780F"
}
# 3. 发送请求,获取响应数据
response = requests.get(url, headers=headers)
# 4. 查看响应的内容
print(response.content.decode())
运行代码验证结果:
登录之后访问该页面,会显示出来对应的左侧的菜单信息,我们查看一下在代码中获取到的响应内容中,是否包含着对应的菜单信息即可。
3. 使用 Cookies 参数
上一小节中,我们在请求头 headers 中携带着登录之后的 Cookie 信息,我们也可以使用专门的 cookies 参数来携带 Cookie。
3.1 Cookies 参数语法以及格式
-
cookies参数的格式:字典
cookies = {"Cookie的name": "Cookie的Value"}
- 该字典对应请求头中Cookie字符串,以分号、空格分割每一对字典键值对
- 等号左边的是一个cookie的name,对应cookies字典的key
- 等号右边对应cookies字典的value
-
cookies参数的使用方式:
response = requests.get(url, cookies={})
-
注意: cookie一般是有过期时间的,一旦过期需要重新获取
3.2 使用cookies参数完成登录传智健康
- 登录传智健康,获取到登录之后的cookie字符串
- 将浏览器抓包获取到的cookie字符串,转换成字典,以便用于发送请求时传递给cookies参数。
完整代码如下:
import requests
# 1. 准备目标url地址
url = 'http://manager-health-java.itheima.net/pages/main.html'
# 2. 准备请求头信息,User-Agent,(从浏览器抓包复制)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
}
# 准备cookies字典
cookie_dict = {
'JSESSIONID': '14C7DFBF244EB81DC281FC1B96EF88D6'
}
# 3. 发送请求,获取响应数据
response = requests.get(url, headers=headers, cookies=cookie_dict)
# 4. 查看响应的内容
print(response.content.decode())
对比网页内容和使用代码获取到的网页响应内容:
我们发现,代码中获取到的响应内容和页面中的内容是一致的,表示我们使用 cookies参数携带cookie登录传智健康是成功的。
cookie有过期时间 ,所以直接复制浏览器中的cookie可能意味着下一程序继续运行的时候需要替换代码中的cookie,对应的我们也可以通过一个程序专门来获取cookie供其他程序使用;当然也有很多网站的cookie过期时间很长,这种情况下,直接复制cookie来使用更加简单。
补充:将浏览器复制的含有多个Cookie的字符串转换为字典
cookies_dict = {
cookie.split('=')[0]:cookie.split('=')[1] for cookie in cookies_str.split('; ')
}
4. 使用 requests.session 携带Cookie
前面使用手动的方式使用cookie,那么有没有更好的方法在requets中携带cookie呢?
requests 提供了一个叫做session类,来实现客户端和服务端的会话保持
会话保持有两个内涵:
- 自动保存cookie,下一次请求会带上前一次的cookie
- 实现和服务端的长连接,加快请求速度
4.1 使用方式
session = requests.session() # 实例化session类对象
response = session.get(url, headers, …)
response = session.post(url, data, …)
- session对象发送get或post请求的参数,与requests模块发送请求的参数完全一致
4.2 使用 requests.session 登录传智健康
-
登录页面 url 地址: 传智健康
-
先抓包获取到登录传智健康的登录 url 地址,以及登录需要的参数
- 先使用 request.session 完成登录,获取登录之后的 cookie
- 在使用 request.session 对象请求登录之后的页面。
完整代码如下:
import requests
# 1. 准备登录的url地址(抓包获取到的登录的post url地址)
login_url = "http://manager-health-java.itheima.net/login.do"
# 准备请求头User-Agent
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
}
# 准备请求体字典
data = {
"username": "admin",
"password": "admin"
}
# 2. 创建session对象
session = requests.session()
# 3. 使用session对象发送post请求 登录传智健康
session.post(login_url, headers=headers, data=data)
# 使用 session对象再次请求登录之后的url地址
url = "http://manager-health-java.itheima.net/pages/main.html"
# 发送请求 获取响应
response = session.get(url, headers=headers)
# 4. 打印响应内容
print(response.content.decode())
查看运行之后的结果:
5. requests 中 cookiejar 处理
在使用requests模块发送完请求之后,获取到的response对象中具有 cookies属性,该属性是一个 RequestsCookieJar 类型。包含了对方服务器设置在本地的cookie。我们如何将其转换为cookies字典呢?
-
转换方式
# 将cookiejar转换成字典类型
cookies_dict = requests.utils.dict_from_cookiejar(response.cookies)
# 将字典类型转换成cookiejar类型
cookies_jar = requests.utils.cookiejar_from_dict(cookies_dict) -
其中response.cookies返回的就是cookieJar类型的对象
-
requests.utils.dict_from_cookiejar函数返回cookies字典
5.1 案例 RequestsCookieJar 的转换
完整代码如下:
import requests
# 1. 准备url地址
url = "https://www.baidu.com"
# 发送请求 获取响应对象
response = requests.get(url)
# <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
print(f"获取到的cookiejar对象为:{response.cookies}")
# 将cookiejar类型 转换为字典
cookie_dict = requests.utils.dict_from_cookiejar(response.cookies)
print(f"将cookiejar转换为字典:{cookie_dict}")
# 将cookie字典再转换为cookiejar
cookie_jar = requests.utils.cookiejar_from_dict(cookie_dict)
print(f"将cookie_dict 转换成cookiejar:{cookie_jar}")
代码运行结果如下所示:
总结
-
爬虫中使用 Cookie 的目的:
- 访问登录之后的页面
- 实现反反爬
-
在请求头中设置 Cookie
- 先在浏览器中登录到目标网站。
- 使用浏览器抓包工具获取到登录之后的 Cookie。
- 将获取到的 Cookie 设置到请求头headers的字典中。
- 在发送请求的时候,携带请求头headers字典: response = requests.get(url, headers=headers)
-
使用cookies参数携带 Cookie:
- 先在浏览器中登录到目标网站。
- 使用浏览器抓包工具获取到登录之后的 Cookie。
- 将获取到的 Cookie 字符串,通过字典推导式转换为 Cookie 字典。
- 在发送请求的时候,将构造好的 Cookie 字典,传递给cookies参数: response = requests.get(url, cookies = {})
-
使用requests.session可以在每一次请求的时候,自动携带上一次获取到的Cookie。
- 创建 requests.session 对象
- session对象的使用方式(session对象发送get或post请求的参数,与requests模块发送请求的参数完全一致): session = requests.session() # 实例化session对象
response = session.get(url, headers, …)
response = session.post(url, data, …) - 使用 session 对象发起POST请求,登录到目标网站
- 后续的请求使用 session对象来发起,就可以实现状态保持。
-
使用requests模块发送请求获取到的response对象中的cookies属性获取到的是一个 RequestsCookieJar 类型,该类型可以和字典进行相互转换,转换方式如下:
# 将cookiejar转换成字典类型
cookies_dict = requests.utils.dict_from_cookiejar(response.cookies)
# 将字典类型转换成cookiejar类型
cookies_jar = requests.utils.cookiejar_from_dict(cookies_dict)
requests 模块的其他用法
知识点:
- 掌握 requests解决https证书错误的问题
- 掌握 requests中超时参数的使用
- 掌握 retrying模块的使用
1. requests 模块处理证书错误
经常我们在网上冲浪时,经常能够看到下面的提示:
- 原因:该网站的CA证书没有经过【受信任的根证书颁发机构】的认证。
- 关于CA证书以及受信任的根证书颁发机构点击了解更多,课上我们不做展开讲解。
上述情况,我们在使用浏览器请求网址的时候,就会出现,那么我们在代码中去请求CA证书有问题的网站的时候会怎么样呢?
注意: 12306 完整大概在 18年10月份左右进行升级,现在已经不会出现证书错误的问题了,我们使用以下网址来演示该问题:
https://sam.huat.edu.cn:8443/selfservice/
1.1 代码中发起请求演示
实例代码如下:
import requests
# 准备目标url地址
url = "https://sam.huat.edu.cn:8443/selfservice/"
# 发送请求 获取响应对象
response = requests.get(url)
以上代码运行结果如下:
很明显。我们发现,在代码中发起请求,如果目标的url地址,存在 CA 证书问题是,会抛出 SSLError 这么一个错误。
1.2 解决方案
为了在代码中能够正常的请求,我们使用verify=False参数,此时requests模块发送请求将不做CA证书的验证:verify参数能够忽略CA证书的认证
代码如下:
import requests
# 注意:取消安全证书有InsecureRequestWarning 不安全的警告
# 取消警告
requests.packages.urllib3.disable_warnings()
# 准备目标url地址
url = "https://sam.huat.edu.cn:8443/selfservice/"
# 发送请求 获取响应对象
response = requests.get(url, verify=False)
print(response.content.decode('gbk'))
上述代码运行结果如下:
注意:如果后续在请求的时候,程序抛出了 SSLError 的异常,那么就可以使用 verify=False 来解决 CA证书错误的问题。
2. 超时参数timeout的使用
在平时网上冲浪的过程中,我们经常会遇到网络波动,这个时候,一个请求等了很久可能任然没有结果。
在爬虫中,一个请求很久没有结果,就会让整个项目的效率变得非常低,这个时候我们就需要对请求进行强制要求,让他必须在特定的时间内返回结果,否则就报错。
超时参数 timeout的使用方式
response = requests.get(url, timeout=3)
timeout=3表示:发送请求后,3秒钟内返回响应,否则就抛出异常
示例代码如下:
import requests
url = 'https://twitter.com'
response = requests.get(url, timeout=3) # 设置超时时
上述代码运行结果如下:
超出了设置设置的超时时间,程序中就会抛出: ConnectTimeout 异常。
3. retrying模块的使用
使用超时参数能够加快我们整体的请求速度,但是在正常的网页浏览过成功,如果发生速度很慢的情况,我们会做的选择是刷新页面,那么在代码中,我们是否也可以刷新请求呢?
对应的,retrying模块就可以帮助我们解决
3.1 retrying模块的使用
retrying模块的地址:retrying · PyPI
安装:
pip install retrying
或者
pip install retrying -i https://pypi.tuna.tsinghua.edu.cn/simple
安装如下图所示:
retrying 模块的使用
- 用retrying模块提供的retry模块
- 通过装饰器的方式使用,让被装饰的函数反复执行
- retry中可以传入参数stop_max_attempt_number,让函数报错后继续重新执行,达到最大执行次数的上限,如果每次都报错,整个函数报错,如果中间有一次成功,程序继续往后执行
3.2 retrying和requests的简单封装
实现一个发送请求的函数,每次爬虫中直接调用该函数即可实现发送请求,在其中
- 使用timeout实现超时报错
- 使用retrying模块实现重试
参考代码如下:
import requests
from retrying import retry
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
}
# 最大重试3次,3次全部报错,才会报错
@retry(stop_max_attempt_number=3)
def _parse_url(url):
print('-' * 30)
# 超时的时候会报错并重试
response = requests.get(url, headers=headers, timeout=3)
# 状态码不是200,也会报错并重试
assert response.status_code == 200
return response
def parse_url(url):
try:
# 进行异常捕获
response = _parse_url(url)
except Exception as e:
print(e)
# 报错返回None
response = None
return response
parse_url('https://twitter.com')
运行结果如下所示:
总结
-
如果在使用requests模块请求某一个url地址的时候,抛出了 SSLError 的错误,可以使用 verify=False,来忽略CA证书的验证。
response = requests.get(url, verify=False)
-
可以在请求某一个url地址的时候,设置超时参数,超过超时参数设置的时间还没有请求成功,就会抛出异常。
response = requests.get(url, timeout=秒数)
-
如果对于一个url地址,设置了超时参数,没有请求成功,我们需要重复的再次的去请求,可以借助 retrying 模块重复的去请求。
import requests
from retrying import retry# 最大重试3次,3次全部报错,才会报错
@retry(stop_max_attempt_number=3)
def _parse_url(url):
print('-' * 30)
# 将重复发生的请求的操作 放到封装的函数中,使用装饰器中的 stop_max_attempt_number 设置最大的重试次数
# 超时的时候回报错并重试
response = requests.get(url, headers=headers, timeout=3)
# 状态码不是200,也会报错并重试
assert response.status_code == 200
return response
案例-百度贴吧爬虫
1. 需求
- 给定一个贴吧名字,给定要抓取的页数。
- 将贴吧的每一页数据保存到html中。
2. 需求分析
-
打开百度贴吧首页:百度贴吧——全球领先的中文社区
-
搜索访问进入某一个贴吧中,这里以传智播客贴吧为例: https://tieba.baidu.com/f?kw=传智播客
-
上述过程中,我们可以分析出来,在url地址中的 kw 参数,对应的就是我们要访问的贴吧的名字。我们可以根据给定的贴吧名字进行替换要抓取的贴吧的名字。
-
进入到传智播客贴吧中,我们目前看到的就是第一页的数据,需求中,我们是需要获取到多页的数据的,那么我们就需要去分析一下,每一页的url地址的规律,从而构造出多页的url地址。
-
页面url地址规律分析
第一页的url地址: https://tieba.baidu.com/f?kw=传智播客
第二页的url地址: https://tieba.baidu.com/f?kw=传智播客&pn=50
第三页的url地址: https://tieba.baidu.com/f?kw=传智播客&pn=100
第四页的url地址: https://tieba.baidu.com/f?kw=传智播客&pn=150
第五页的url地址: https://tieba.baidu.com/f?kw=传智播客&pn=200 -
通过分析发现,每一页的url地址中,都是在参数 pn 的基础上 加 50。第一页的url地址中,是没有pn参数的,那么我们可以尝试一下,第一页的url地址中加上 pn=0,传智播客吧-百度贴吧–传智播客-国内口碑最好的IT培训机构!–中国的软件教育已经坑害了不少软件工程师苗子,传智播客自成立之日起就立志于改变中国的软件教育,目前已经出版IT教程书籍十多本,教学视频几十套 , 看看能不能访问成功
-
通过上一步的验证,我们发现第一页的 pn 值为 0
-
那么综上所述,我们可以得出百度贴吧的url地址的规律为:
tb_name:要访问的贴吧的名字
page_num: 当前要访问的页码数
https://tieba.baidu.com/f?kw=tb_name&pn=50 * (page_num – 1) -
比如我们需要抓取10页的数据,可以先将所有页的url地址构造好,放到一个列表中。
import requests
# url地址的模板
base_url = "https://tieba.baidu.com/f?kw={}&pn={}"# 获取要抓取的贴吧的名字
tb_name = input('请输入要抓取的贴吧的名字:')
# 获取要抓取的页数
page_num = int(input('请输入要抓取的页数:'))# 构造url地址
url_list = [base_url.format(tb_name, i * 50) for i in range(page_num)]print(url_list)
-
构造好的url地址如下图所示:
-
遍历构造好的url地址,分别对url地址去发送请求,获取到响应数据,再分别将每一页的数据保存到文件中。
# 准备headers中的User-Agent
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
}
# 遍历每一页的url地址,发送请求 获取响应
for url in url_list:
response = requests.get(url, headers=headers)
# 将获取到的响应的内容,保存到文件中
# 构造文件名
file_name = tb_name + "第{}页.html".format(url_list.index(url) + 1)
# 打开文件,将数据保存到文件中
with open(file_name, 'w', encoding='utf-8') as f:
f.write(response.content.decode())print(file_name, '保存成功')
# 设置time.sleep() 防止访问速度过快
time.sleep(2)
3. 完整代码
完整代码如下:
import time
import requests
# url地址的模板
base_url = "https://tieba.baidu.com/f?kw={}&pn={}"
# 获取要抓取的贴吧的名字
tb_name = input('请输入要抓取的贴吧的名字:')
# 获取要抓取的页数
page_num = int(input('请输入要抓取的页数:'))
# 构造url地址
url_list = [base_url.format(tb_name, i * 50) for i in range(page_num)]
# 准备headers中的User-Agent
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
}
# 遍历每一页的url地址,发送请求 获取响应
for i, url in enumerate(url_list):
response = requests.get(url, headers=headers)
# 将获取到的响应的内容,保存到文件中
# 构造文件名
file_name = tb_name + "第{}页.html".format(i + 1)
# 打开文件,将数据保存到文件中
with open(file_name, 'w', encoding='utf-8') as f:
f.write(response.content.decode())
print(file_name, '保存成功')
# 设置time.sleep() 防止访问速度过快
time.sleep(2)
程序运行结果如下:
评论前必须登录!
注册