requests 是用 python 语言编写的一个开源的HTTP库,可以通过 requests 库编写 python 代码发送网络请求,其简单易用,是编写爬虫程序时必知必会的一个模块。
发送网络请求,获取响应数据。
中文文档: Requests: 让 HTTP 服务人类 — Requests 2.18.1 文档
安装命令如下:
pip install requests
或者
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple
具体安装运行如下图所示:
查看安装好的 requests 模块的信息
pip show requests
具体运行如下图所示:
知识点:
使用 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)
运行代码结果如下:
观察上边代码运行结果发现,有好多乱码;这是因为编解码使用的字符集不同早造成的;我们尝试使用下边的办法来解决中文乱码问题。
代码如下:
# 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())
运行代码结果如下:
response.content
response.content.decode('指定编码字符集')
response.content.decode()
默认使用 utf-8 编码方式response.text
response.text = response.content.decode('推测出的编码字符集')
获取网页源码的方式:
以上三种方法从前往后尝试,能够100%的解决所有网页解码的问题, 推荐优先使用: response.content.decode()
示例代码如下:
# 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
示例代码运行结果如下:
需求: 将图片http://www.itcast.cn/2018czgw/images/logo2.png
保存到本地。
思考:
分析:
http://www.itcast.cn/2018czgw/images/logo2.png
完整代码如下:
# 1. 导入request模块
import requests
# 2. 准备目标url地址
url = 'http://www.itcast.cn/2018czgw/images/logo2.png'
# 3. 向目标url地址发送get请求
response = requests.get(url)
# 4. 打开文件,将数据写入到文件中
with open('itcast.png', 'wb') as f:
# 写入响应内容的二进制数据
f.write(response.content)
在最开始,我们书写了一下代码,获取了一下百度首页的内容:
# 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 模块发送请求时携带的请求头信息:
对比一下,很明显的,代码中的 User-Agent 和 浏览器中的完全不一样,前面我们也说过,User-Agent 是浏览器的身份标识,而代码中直接发送的是 python-requets/2.25.1
这样服务器很明显的就知道我们不是使用正常的浏览器访问服务器,所以返回的数据就比较少。
response = requests.get(url, 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
作为 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"}
# 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())
运行结果如下图所示:
很明显,拿到的内容和使用浏览器拿到的网页源码内容就是一样的了。
我们在使用百度搜索的时候,经常会发现 URL 地址中会有一个 ? ,该问号后面的就是查询参数,又叫做查询字符串参数。
语法格式如下:
response = requests.get(url, params={})
需求:实现在百度中搜索 传智播客
实现方式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())
以上代码运行结果如下所示:
知识点:
思考: 那些地方会使用到 POST 请求?
所以同样的,我们的爬虫也需要在这两个地方会去模拟浏览器发送post请求。
补充:GET 请求和 POST 请求的区别
GET | POST | |
---|---|---|
后退按钮/刷新 | 无害 | 数据会被重新提交(浏览器应该告知用户数据会被重新提交)。 |
书签 | 可收藏为书签 | 不可收藏为书签 |
历史 | 参数保留在浏览器历史中。 | 参数不会保存在浏览器历史中。 |
对数据长度的限制 | 当发送数据时,GET 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是 2048 个字符)。 | 无限制。 |
对数据类型的限制 | 只允许 ASCII 字符。 | 没有限制。也允许二进制数据。 |
安全性 | 与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分。在发送密码或者其他敏感信息的时候决不能使用GET。 | POST 比 GET 更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中。 |
可见性 | 数据在 URL 中对所有人都是可见的。 | 数据不会显示在 URL 中。 |
语法格式如下:
response = requests.post(url, data={}, headers={}, params={})
如下图所示:
将请求体(form data)中的数据转换为字典:
data = {
"username": "admin",
"password": "admin"
}
登录页面 URL 地址: http://manager-health-java.itheima.net/login.html
案例分析
抓包分析,找到登录请求的url地址
右键检查 --> Network
最终分析出:
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
每次在使用浏览器请求一个网站的时候,服务器是可以获取到当前客户端的 IP 地址的,使用爬虫程序去请求服务器的速度和频率是特别快的,这样的话,我们使用同一台电脑上的浏览器去请求别人的服务器,会被服务器识别到,这样可能就会将我们的 IP 地址封掉。为了不让服务器将我们的 IP 地址封掉,在发送请求的时候可以使用代理 IP。
代理IP 是一个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 |
根据网站所使用的协议不同,需要使用相应协议的代理服务。从代理服务请求使用的协议可以分为:
为了让服务器以为是不同的客户端在请求;为了防止频繁向一个域名发送请求被封 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 网站
在学习阶段,我们直接在网上找一些免费的代理去使用即可,免费的代理的质量不是很好,真正在公司会去购买付费的高质量的代理,或者自己去搭建代理服务器。
完整代码如下:
# 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())
知识点:
为了能够通过爬虫获取到登录后的页面,或者是解决通过cookie的反扒,需要使用request来处理cookie相关的请求
使用requests携带cookie有三种方法:
网站经常利用请求头中的 Cookie 字段来做用户访问状态的保持,那么我们可以在 headers 参数中设置 Cookie 请求头,模拟普通用户的请求。我们以传智健康登陆为例:
打开浏览器,右键检查,点击 Network,勾选 Preserve log
访问url地址:http://manager-health-java.itheima.net/login.html
输入账号和密码之后,点击登录。
访问一个登录之后才可以访问的页面,例如: 传智健康
找到Network中对应的抓到的包,查看在访问时携带的 User-Agent 和 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())
运行代码验证结果:
登录之后访问该页面,会显示出来对应的左侧的菜单信息,我们查看一下在代码中获取到的响应内容中,是否包含着对应的菜单信息即可。
上一小节中,我们在请求头 headers 中携带着登录之后的 Cookie 信息,我们也可以使用专门的 cookies 参数来携带 Cookie。
cookies参数的格式:字典
cookies = {"Cookie的name": "Cookie的Value"}
cookies参数的使用方式:
response = requests.get(url, cookies={})
注意: cookie一般是有过期时间的,一旦过期需要重新获取
完整代码如下:
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('; ')
}
前面使用手动的方式使用cookie,那么有没有更好的方法在requets中携带cookie呢?
requests 提供了一个叫做session类,来实现客户端和服务端的会话保持
会话保持有两个内涵:
session = requests.session() # 实例化session类对象
response = session.get(url, headers, ...)
response = session.post(url, data, ...)
登录页面 url 地址: 传智健康
先抓包获取到登录传智健康的登录 url 地址,以及登录需要的参数
完整代码如下:
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())
打开百度贴吧首页:登录_百度贴吧
搜索访问进入某一个贴吧中,这里以传智播客贴吧为例: 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)
完整代码如下:
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)