Request 是一个简答优雅的 python HTTP 库,相较于 python 标准库中的 urllib 和 urllib2 的库,Requests 更加的便于理解使用, 本篇文章将带你详细了解 Requests 的基本用法。
Requests 模块
安装
注意:在 python 3.8 中的安装包中默认安装了 pip 工具,只需要我们配置一下环境变量即可
1
| PAHT = 'C:\Users\Administrator\AppData\Local\Programs\Python\Python38\Scripts'
|
简单使用
1
2
3
4
5
6
7
8
9
10
| #!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
r = requests.get('https://dp2px.com/demo/image-alert/')
print('文本编码:', r.encoding)
print('响应状态码:', r.status_code)
print('字符串方式的响应体:', r.text)
|
第一步:导入 Requests 模块:
第二步:通过 Get 方式获取某个网页数据:
1
| r = requests.get('https://dp2px.com/demo/image-alert/')
|
第三步:得到 Response 对象,从中取得我们想要的数据。
1
2
3
| print('文本编码:', r.encoding)
print('响应状态码:', r.status_code)
print('字符串方式的响应体:', r.text)
|
基本用法介绍
添加参数
1
| r = requests.post('https://dp2px.com/demo/image-alert/', params = {'key':'value'})
|
添加参数很简单,如上面代码所示,添加 key-value 到 params 即可。
你还可以将一个列表作为值传入:
1
2
| payload = {'key1': 'value1', 'key2': ['value2', 'value3']}
r = requests.post('https://dp2px.com/demo/image-alert/', params = payload)
|
RESTful API
RESTful
的核心思想就是,客户端发出的数据操作指令都是 “动词 + 宾语” 的结构。比如,GET/articles
这个命令,GET 是动词,/articles 是宾语。
常用的 HTTP 动词有下面五个(括号里是对应的 SQL 命令):
- GET(SELECT):从服务器取出资源(一项或多项)。
- POST(CREATE):在服务器新建一个资源。
- PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
- PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
- DELETE(DELETE):从服务器删除资源。
还有两个不常用的 HTTP 动词:
- HEAD:获取资源的元数据。
- OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。
对于这些 HTTP 请求类型,requests 也是支持的:
1
2
3
4
| r = requests.put('http://httpbin.org/put', params = {'key':'value'})
r = requests.delete('http://httpbin.org/delete')
r = requests.head('http://httpbin.org/get')
r = requests.options('http://httpbin.org/get')
|
POST 请求和文件上传
上面我们说明了如何发送表单数据,通过 params
, 有时候我们还需要上传文件或者 json 格式数据。
在 requests 2.4.2 之前需要先对 json 格式转码,才可以赋值给 data 使用:
1
2
3
4
5
6
| import json
url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
r = requests.post(url, data=json.dumps(payload))
|
而在 requests 2.4.2 之后则可以直接赋值给 json
,会自动转换为json:
1
2
3
4
| url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
r = requests.post(url, json=payload)
|
而文件上传就显得更加简单了,直接使用 files
来指定文件对象:
1
2
3
4
| url = 'https://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}
r = requests.post(url, files=files)
|
上面的 open()
是 python 的内置函数,用来读取并获得文件对象。
响应内容
r.text
是服务器的响应内容,会自动根据响应头部的字符编码进行编码。我们上面获取的 r.text
是中文乱码:
我们上面 r.encoding
得知返回编码是 ISO-8859-1
所以添加一行 r.encoding = 'utf-8'
对结果重新编码:
1
2
3
4
| r = requests.get('https://dp2px.com/demo/image-alert/')
r.encoding = 'utf-8'
print('字符串方式的响应体:', r.text)
|
二进制响应
你也能以字节的方式访问请求响应体,对于非文本请求:
1
| print('字节方式的响应体:', r.content)
|
Requests 会自动为你解码 gzip 和 deflate 传输编码的响应数据。例如,以请求返回的二进制数据创建一张图片,你可以使用如下代码:
1
2
3
4
| from PIL import Image
from io import BytesIO
i = Image.open(BytesIO(r.content))
|
JSON内容响应
通过 r.json()
会自动解析 json 字符串:
1
2
3
4
5
6
7
8
9
10
11
| #!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
params = {'page': 0, 'count': 5}
r = requests.get('https://api.apiopen.top/getImages', params = params)
print('文本编码:', r.encoding)
print('响应状态码:', r.status_code)
print('字节方式的响应体:', r.json())
|
如果返回的字符串不是 JSON 格式,将会解码失败抛出异常 ValueError: No JSON object could be decoded
.
定制请求头
添加请求头很容易,和添加 params
参数类似:
1
2
3
4
| url = 'https://api.github.com/some/endpoint'
headers = {'user-agent': 'my-app/0.0.1'}
r = requests.get(url, headers=headers)
|
当然,我们也可以打印响应头 r.headers
:
1
| print('响应header头:', r.headers)
|
获取 Cookie 数据
如果某个响应中包含一些 cookie,你可以快速访问它们:
1
| r.cookies['example_cookie_name']
|
要想发送你的 cookies 到服务器,可以使用 cookies 参数:
1
2
3
4
| url = 'http://httpbin.org/cookies'
cookies = dict(cookies_are='working')
r = requests.get(url, cookies=cookies)
|
上面创建了一个 字典 dict
来设置 cookies, 当然你也可以用上面 headers
和 params
类似的方式来设置 cookies.
设置超时时间
可以添加 timeout
来设置连接服务器的超时时间,单位是秒。例如下面我设置的超时时间是 3 秒。
1
2
3
4
5
6
7
8
9
10
11
12
| #!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
params = {'page': 0, 'count': 5}
r = requests.get('https://api.apiopen.top/getImages', params = params, timeout = 3)
print('文本编码:', r.encoding)
print('响应状态码:', r.status_code)
print('响应header头:', r.headers)
print('字节方式的响应体:', r.json())
|
本文出自水寒的博客,转载请说明出处:https://dp2px.com
案例演示一
一般我们做好的网站在发布新内容的时候为了让搜索引擎更快的收录需要主动提交链接地址到站长平台,接下来我们通过 Requests 来实现这个功能。
官方还有一个 curl 推送示例(curl 是常用的命令行工具,用来请求 Web 服务器):
将要提交的链接按照每行一条的格式写入一个文本文件中,命名此文件为urls.txt,然后进入该文件所在目录,执行如下命令:
1
| curl -H 'Content-Type:text/plain' --data-binary @urls.txt "http://data.zz.baidu.com/urls?site=https://dp2px.com&token=xxxxxxxxx"
|
我们参考这个指令,使用 python 模拟 curl 请求,可以指定 User-Agent
为 curl/7.12.1
,同样可以指定 Host
和 Content-Type
.
1
2
3
4
5
| headers = {
'User-Agent': 'curl/7.12.1',
'Host': 'data.zz.baidu.com',
'Content-Type': 'text/plain'
}
|
然后使用内置函数 open()
打开文件读取内容,该函数返回 file 对象然后传递给 files
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| #!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
headers = {
'User-Agent': 'curl/7.12.1',
'Host': 'data.zz.baidu.com',
'Content-Type': 'text/plain'
}
url = 'http://data.zz.baidu.com/urls?site=https://dp2px.com&token=xxxxxxxx'
files = {
'file': open('urls.txt', 'rb')
}
r = requests.post(url, headers=headers, files=files, timeout=3)
print('响应状态码:', r.status_code)
print('响应结果:', r.text)
|
当然了,如果你要同时提交百度平台、熊掌号可以使用如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # 百度平台提交
url = "http://data.zz.baidu.com/urls?site=https://xxx.com&token=xxx"
# 熊掌号 周级提交
url2 = "http://data.zz.baidu.com/urls?appid=xxx&token=xxx&type=batch"
# 熊掌号 天级提交
url3 = "http://data.zz.baidu.com/urls?appid=xxx&token=xxx&type=realtime"
headers = {
"User-Agent":"curl/7.12.1",
"Host":"data.zz.baidu.com",
"Content-Type":"text/plain"
}
body = 'https://xxx.com/xxx.html'
r = requests.post(url, headers=headers, data=body, timeout=3)
r2 = requests.post(url2, headers=headers, data=body, timeout=3)
r3 = requests.post(url3, headers=headers, data=body, timeout=3)
|
案例演示二
本文出自于水寒的个人博客,转载请说明出处:https://dp2px.com
上面的 urls.txt 里面的新文章链接可能需要你手动添加,这样太麻烦了,于是我就想了一个办法从 RSS 里面取得最新的 10 篇文章自动保存到 urls.txt 文件中。
这里就会用到 xml 文件解析问题,python 中常用的解析方式如下:
方法 | 特点 |
---|
SAX | SAX解析通过流模式在解析XML的过程中触发对应的事件(start_element、char_data、end_element)并调用用户定义的回调函数来处理XML文件。 |
DOM | 将XML数据在内存中解析成一个树,通过对树的操作来操作XML,占用内存大,解析速度较慢,优点是可以任意遍历树的节点。 |
ElementTree | 类似一个轻量级的DOM,也是接下来咱们要用的。 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| #!/usr/bin/python
# -*- coding: UTF-8 -*-
import xml.etree.ElementTree as ET
tree = ET.parse('../public/index.xml')
urlsfile = open('urls.txt', 'w+')
urlsfile.truncate() #清空文件
root = tree.getroot()
print('root-tag:', root.tag)
for child in root:
for sub in child[7:17]:
#print('sub:', sub.tag)
for sc in sub:
if sc.tag == 'link':
print('sb-tag', sc.text)
urlsfile.write(sc.text + '\n')
urlsfile.close()
|
这样在生成静态文件 /public
之后脚本自动调用该 python 文件就可以在 urls.txt 中自动生成最近文章链接,最后再自动调用上面的站长平台提交 python 文件即可自动上传。
案例演示三
我们来使用 Requests 爬取一下豆瓣电影Top250页面:https://movie.douban.com/top250
首先我们打开网页,F12 的 Network 选项中查看 Headers 中的 Requests Headers
:
请求头 Headers 提供了关于请求、响应或其他发送的实体信息。对于爬虫而言请求头是很重要的,如果没有正确的请求头可能不会得到正确的结果。
我们只需要提取请求头中的重要部分即可:
1
2
3
4
5
6
7
8
9
| #!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
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',
'Host': 'movie.douban.com'
}
|
接下来我们来看看点击下一页地址会发生变化:https://movie.douban.com/top250?start=25&filter=
如果再次点击下一页又会发生改变,但是这种变化是有规律的,我们要提取出这种路径规律来抓取所有的 250
条数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| #!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
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',
'Host': 'movie.douban.com'
}
for i in range(0, 10):
link = 'https://movie.douban.com/top250?start=' + str(i * 25) + '&filter='
r = requests.get(link, headers=headers, timeout=3)
print("响应状态码:", r.status_code)
print(r.text)
|
执行结果:
这时候我们已经得到了所有网页的数据,接下来我们需求从这些网页数据中提取我们想要的内容,首先介绍一下一个利器 Beautiful Soup
.
Beautiful Soup
Beautiful Soup 提供一些简单的、python 式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。
Beautiful Soup 自动将输入文档转换为 Unicode 编码,输出文档转换为 utf-8 编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup 就不能自动识别编码方式了。然后,你仅仅需要说明一下原始编码方式就可以了。Beautiful Soup 已成为和 lxml、html6lib 一样出色的 python 解释器,为用户灵活地提供不同的解析策略或强劲的速度。
安装:
Beautiful 支持的常见的 HTML 解析器比较如下:
解析器 | 使用方法 | 优势 | 劣势 |
---|
Python标准库 | BeautifulSoup(markup, “html.parser”) | Python 内置的标准库,执行速度适中,文档容错能力强 | 在 Python 3.2.2 之前版本容错能力差 |
lxml HTML解析器 | BeautifulSoup(markup, “lxml”) | 速度快,文档容错能力强 | 需要安装 C 语言库 |
lxml XML解析器 | BeautifulSoup(markup, [“lxml”, “xml”]) | 速度快,唯一支持 XML 的解析器 | 需要安装 C 语言库 |
html5lib | BeautifulSoup(markup, “html5lib”) | 最好的容错性,以浏览器的方式解析文档 | 速度慢,不依赖外部扩展 |
这里推荐大家使用 lxml XML 解析器,这个不仅仅容错性高,而且速度很快。
所以我们还应该安装一下 lxml:
接下来我们使用 lxml 解析器首先找到 class 是 hd 的 <div>
标签,然后找到里面的 <a><span></span></a>
内的文本内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
from bs4 import BeautifulSoup
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',
'Host': 'movie.douban.com'
}
for i in range(0, 10):
link = 'https://movie.douban.com/top250?start=' + str(i * 25) + '&filter='
r = requests.get(link, headers=headers, timeout=3)
print("响应状态码:", r.status_code)
soup = BeautifulSoup(r.text, "lxml")
div_list = soup.find_all('div', class_='hd')
for each in div_list:
movie = each.a.span.text.strip()
print(movie)
|
当然了,你如果不想使用 lxml
而用 python 自带的标准库解析也是可以的,只需要替换上面的一行代码:
1
2
| - soup = BeautifulSoup(r.text, "lxml")
+ soup = BeautifulSoup(r.text, "html.parser")
|