网络爬虫中,网络请求是基础部分。没有网络请求以及响应,网络爬虫的后续数据分析也就失去了意义。Python中的网络请求,主要由Requests库来完成,本篇,我们就来一起认识一下Requests库及其基本使用方法。
Requests库简介
Requests库是一个简洁而优雅的Python第三方库,更好地贴合人们的使用习惯,故在http类库中,Requests库非常受开发者青睐。Requests库支持Keep-Alive
持久化连接、带有Cookie
的对话、SSL
认证、动解码、HTTP(S)
代理支持、流下载、文件分块上传等当今使用的诸多功能。其他功能介绍和说明文档参见官方文档。Requests: HTTP for Humans
Requests库的安装
pip install requests
Requests库的主要方法
Requests库主要有7个方法,大部分与HTTP(S)
协议中的请求方法相同
方法 | 说明 |
---|---|
requests.request() | 构造一个请求以支撑以下各个方法 |
requests.get() | 获取资源的方法,同HTTP GET |
requests.head() | 获取资源响应头部信息的方法,同HTTP HEAD |
requests.post() | 向网站提交post请求的方法,同HTTP POST |
requests.put() | 向网站提交put请求的方法,同HTTP PUT |
requests.patch() | 向网站提交patch局部修改请求,同HTTP PATCH |
requests.delete() | 向网站提交删除资源请求,同HTTP DELETE |
-
另外,其他的HTTP方法requests库也都支持,由于不常用就不一一列举
-
实际上,所有的其他方法都是通过调用基本方法
requests.request()
实现的
使用Requests库构造基本请求
网页访问中最常用的请求方法是GET
方法,下面我们就使用GET
方法构造一个基本的请求。
Requests.get()方法
通过以下代码
r = requests.get(url)
#url是一个字符串型变量,保存了要请求资源的URL
Requests库构造了一个向服务器请求资源的Request
请求对象,该命令返回的结果是一个包含服务器资源的Response
响应对象r
Requests.get()方法的完整使用格式
requests.get(url,params=None,**kwargs)
其中:
- url: 想要请求的资源的
URL
,统一资源定位符 - params: 可选,默认为None,表示在url中的额外参数,可以是字典或字节流格式。
- **kwargs: 可选,12个控制访问的参数,见下
实际上,requests.get()方法调用了requests.request()方法,封装前代码如下
requests.request('get',url,params=params,**kwargs)
get方法将params参数作为含有默认参数的关键字参数单独列出,所以,requests.request()方法一共有13个参数(包含params),以字典方式读入。
Response响应对象
上述请求返回了一个Response响应对象,包含了从服务器返回的资源相关信息,主要具有如下属性
属性 | 说明 |
---|---|
r.status_code | HTTP请求返回状态码 |
r.text | HTTP响应内容(字符串形式) |
r.encoding | 由HTTP header得到的响应主体编码方式,也为当前实体编码方式(也可以通过后期配置) |
r.apparent_encoding | 从响应主体中分析得到的编码方式(备选) |
r.content | HTTP响应内容的二进制形式 |
使用get方法构造请求
下面用一个样例来说明如何使用requests.get()方法构造一个get请求,以访问百度为例
r = requests.get("https://www.baidu.com")
查看响应对象的编码信息
#每一行后的注释内容为返回值
r.encoding
#'ISO-8859-1'
r.apparent_encoding
#'utf-8'
实际上,从响应头部字段中得到的主体编码格式(事实上,响应头部没有指定编码格式时,默认格式即为ISO-8859-1
)是错误的(无法正确显示中文),需要自己调整·r.encoding
为其主体实际编码utf-8
,才能正确显示中文
正确设定主体的编码格式后,就可以正确得到响应的主体内容,另外还可获取响应头部信息等
爬取网页的通用代码框架
通用代码框架是用来爬取网页的一段通用代码,通过对爬取网页的通用代码框架的定制可以可靠而灵活地爬取网页内容,也能够获得通向其他页面的链接。在使用requests.get()方法访问网页时难免会抛出异常,所以在学习通用代码框架前还要了解Requests库的异常信息以及处理方法。
Requests库的异常处理
使用Requests库发送请求时可能会收到来自各个环节的各种异常,若不加以处理,可能会导致程序异常终止。所以认识和处理异常是爬虫开发中必需的环节。
异常 | 说明 |
---|---|
requests.ConnectionError | 网络连接错误,如无法建立连接、DNS解析错误、连接被拒绝(非4xx响应代码)等 |
requests.HTTPError | HTTP错误异常(需要手动抛出,见注释) |
requests.URLRequired | 缺失URL异常 |
requests.TooManyRedirects | 响应的重定向超过阈值,产生重定向异常 |
requests.ConnectTimeout | 服务器连接超时异常 |
requests.Timeout | 请求超时(非连接超时,指连接后的请求阶段超时) |
- 其中,
ConnectionError
指在网络TCP层产生的异常,这类异常会强制终止程序;而HTTPError
指在HTTP协议(应用层)产生的异常,使用r.raise_for_status()
方法手动抛出异常,只要返回代码非200,就会抛出这个异常。
通用代码框架
import requests
def get_uri(url):
try:
r = requests.get(url, allow_redirects=False)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return "An error has been thrown"
print("Requests.get Skeleton")
print(get_uri("http://192.168.0.6:8080/a"))
- 示例中的
get_uri
函数即为封装完成的简单爬虫通用代码框架 - 示例中,服务器返回了404错误,被
raise_for_status()
方法捕获;若将示例中的url改为一个不存在的服务器或非法url,不需要该语句也会直接抛出错误。在经过try-except
语句的异常捕获后,程序不会退出,而可以进行下一步程序内错误提示与处理。 - 如果将示例中的url改为真实存在的,则会直接输出网页源代码,可以通过进一步操作解析处理。
- 对except语句捕获条件的更精确定义可以区分不同的错误,从而对不同错误定制不同的解决办法
Requests库主要方法的使用
request基本方法与参数
首先介绍一下Requests库的基本方法——Requests.request()方法,Requests库中的其他主要请求方法都是通过调用Requests.request()方法完成的
requests.request('Method', url, **kwargs)
其中
Method
为指定的方法名称url
为请求的目标资源统一标识符**kwargs
为请求附加的其他参数,其他所有基于requests.request()的方法参数与其相同,有部分方法可能将部分常用参数作为关键字参数显式定义,没有较大影响
参数 | 说明 |
---|---|
params | 作为参数加入到要访问的url链接中 |
data | 作为Request报文主题内容,以表单形式传送,可以为字典,元组等 |
json | json格式的Request报文内容 |
headers | 较常用,定义了Request报文的请求头部参数,可以为字典 |
cookies | HTTP Request中的cookie信息,可为字典、CookieJar或Request中的Cookie |
auth | 用于HTTP协议认证,为元组 |
files | Request报文主体(以文件形式),为字典,字典的Key为文件类型变量 |
timeout | 设置Request的超时时间,单位为秒 |
proxies | 设置访问的代理服务器,为字典,分别为协议指定代理服务器 |
allow_redirects | 允许重定向,boolean型,默认True,允许重定向 |
stream | 流下载(获取内容立即下载),boolean型,默认True,允许流下载 |
verify | SSL认证证书开关,默认True,使用SSL认证证书 |
cert | 本地的SSL证书路径 |
-
注意:data参数是将参数加入Request报文中,而params参数是将参数加入要访问的url链接中。比如,params={‘a’: 1},url=
'https://www.baidu.com/'
,最后请求生成的url为'https://www.baidu.com/?a=1'
-
headers是一个常用参数,用于定义请求报文的头部信息,接受字典形式的数据。如果未经定义,会输出默认参数
{ 'User-Agent': 'python-requests/2.24.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
-
files参数示例
files = { 'file1': open('data.xls', 'rb')} r = requests.post{ 'https://192.168.0.6:8080/post', files=files}
-
proxies示例
proxies = { 'http': 'http://192.168.0.4', 'https': 'https://192.168.0.5'}
关于get方法,上文已经做过讲解,本段中不再重复赘述,下面介绍一些其他的requests库方法
Requests.post()方法
Requests.post()方法通过HTTP协议的post方法向服务器传递数据,关于HTTP中的post方法请读者自行了解,下面仅介绍Requests.post()方法的具体使用
post方法的封装
def post(url, data=None, json=None, **kwargs):
r"""Sends a POST request. :param url: URL for the new :class:`Request` object. :param data: (optional) Dictionary, list of tuples, bytes, or file-like object to send in the body of the :class:`Request`. :param json: (optional) json data to send in the body of the :class:`Request`. :param \*\*kwargs: Optional arguments that ``request`` takes. :return: :class:`Response <Response>` object :rtype: requests.Response """
return request('post', url, data=data, json=json, **kwargs)
可以看到,post方法将data
和json
参数单独列出,分别表示post请求主体内容(以web表单形式编码)和以json
格式编码的主体内容。以web表单形式编码的条件为data
是一个字典;如果想要发送未经编码的数据(不要默认web表单编码),则直接以字符串作为data
下面三个示例分别用web表单格式、json格式和****作为post方法的请求主体,后附服务器收到信息(节选)
payload = { 'b': 2}
r = requests.post("http://192.168.0.6:8080/post", data=payload)
import json
payload = { 'b': 2}
r = requests.post("http://192.168.0.6:8080/post", json=json.dumps(payload))
r = requests.post("http://192.168.0.6:8080/post", data="b=2")
{ "args":{ },"data":"\"{\\\"b\\\": 2}\"","files":{ },"form":{ },"headers":{ "Content-Length":"12","Content-Type":"application/json"},"json":"{\"b\": 2}",}
{ "args":{ },"data":"","form":{ "b":"2"},"headers":{ "Content-Length":"3","Content-Type":"application/x-www-form-urlencoded"},"json":null}
{ "data":"b=2","headers":{ "Content-Length":"3"},"json":null}
-
在发送未经编码的请求实体时,首部字段中不存在
Content-Type
如果以元组作为
data
,将同样以web表单形式编码,但是如果当个元组使用同一个key的时候,使用元组可以避免字典中键名称重复的问题
payload = (('b', 1), ('b', 2))
r = requests.post("http://192.168.0.6:8080/post", data=payload)
#下面是服务器收到的主体内容
"form":{ "b":["1","2"]}
Requests.put()方法
HTTP协议中put方法与post方法基本类似,只是使用put方法时会将该url下原有数据覆盖,而post方法只是新增一个数据。在Requests库中两个方法的格式几乎相同,这里就不再赘述。
Requests.patch()方法
于HTTP协议中的patch方法类似,用法类似于Requests.put(),区别在于patch仅提供需要修改的部分数据,而put需要提交在该url下的全部数据。
Requests.delete()方法
与HTTP协议中的delete方法类似,能够请求服务器删除url所指定的资源,可能需要在请求参数中加入认证信息。
Requests.head()方法
与HTTP协议中的head方法类似,用法类似于Requests.get()方法,区别仅在于返回报文中只有头部信息而没有主体。