本文主要是记录学习《Requests: HTTP for Humans™》以及崔庆才著作《python3网络爬虫开发实战》
Quickstart
requests.request(method, url, **kwargs) #构建一个请求
- method:请求方法
- url:URL链接
- params:可选参数,元组字典、元组列表,或者Request类中的字节流格式数据,作为访问参数增加到URL中
- data:字典、字节序列或者文件对象,作为Request的内容
- json:JSON格式的数据,作为Request的内容
- headers:字典,请求头
- cookies:字典或者CookieJar,Request中的cookie
- files:字典类型,传输文件
- auth:元组,支持HTTP认证功能
- timeout:设定超时时间,以秒为单位
- allow_redirects:True/False,默认为True,重定向开关
- proxies:字典类型,设定访问代理服务器,可以增加登录认证
- verify:True/False,默认为True,认证SSL证书开关
- stream:True/False,默认为True,获取内容立即下载开关
- cert:本地SSL证书路径
Make a Request
requests.get(url,params=None,**kwargs) #获取HTML网页的主要方法,对应HTTP的GET
requests.post(url,params=None,**kwargs) #向HTML服务器提交表单或者上传文件,数据包含再请求体中
requests.put(url,params=None,**kwargs) #从客户端向服务器传送数据,以取代指定文档中的内容
requests.delete(url,params=None,**kwargs) #请求服务器删除指定的页面
requests.head(url,params=None,**kwargs) #类似于GET请求,只不过返回的响应中没有具体的内容,用于获取抱头
requests.optins(url,params=None,**kwargs) #允许客户端查看服务器的性能
同上面的requests.request()方法一样,只是这里将method参数直接写出来,**为12个访问参数
r = requests.get('https://api.github.com/events') r = requests.post('https://httpbin.org/post', data = {'key':'value'}) r = requests.put('https://httpbin.org/put', data = {'key':'value'}) r = requests.delete('https://httpbin.org/delete') r = requests.head('https://httpbin.org/get') r = requests.options('https://httpbin.org/get')
r = requests.request(url="https://httpbin.org/get",method="GET") #requests.request()方法可以实现上面任何方法
requests.request(url="https://httbin.org/post",method = "POST",data = {"key":"value"})
....
Passing Parameters In URLs
import requests parameters = { "name":"Maria", "Age":"23", "PhoneNum":"1234567" } response = requests.get("https://httpbin.org/get",params = parameters) print(response.url)
运行结果:
https://httpbin.org/get?name=Maria&Age=23&PhoneNum=1234567
Response Content
如果请求成功,服务器正常返回响应,主要是查看响应体这部分内容,即网页数据,包括网页源码文本、二进制数据(图片、视频、音频)、JSON格式数据等
先看返回的HTML文档:看过HTML的都知道,HTML中有<head>(头部)和<body>(内容),<head>中一般有charset=""字段。服务器返回响应内容,Requests会自动解码来自服务器的内容,解码的时候Requests会依据<head>里面的信息对这个响应内容的编码方式作出推测,并将该编码保存在encoding中,如果<head>中有charset字段,则会依据这个字段解析该内容。如果<head>中没有charset字段,则默认为"ISO-8859-1”编码解码该内容。这是根据头部信息推测编码方式,还有就是根据<body>内容来推测编码,根据内容推测的编码保存在apparent-encoding中
import requests response = requests.get("https://www.baidu.com")
print(response.status_code) #查看请求状态
print(response.encoding) # 查看依据头部信息推测的编码方式
print(response.apparent_encoding) # 查看依据内容推测的编码方式 print(type(response)) # 查看返回的对象类型 print(response.text) #查看网页HTML文档,如果解码不正确,可能会出现乱码
运行结果:
200 # 表明请求成功 ISO-8859-1 #表示根据头部信息推测该HTML文档的编码格式为ISO-8859-1 utf-8 # 表示根据HTML内容推测该HTML文档的编码为utf-8 <class 'requests.models.Response'> # 表示该对象为响应 <!DOCTYPE html> <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge>
<meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>ç¾åº¦ä¸ä¸ï¼ä½ å°±ç¥é</title></head>
<body link=0000cc> <div id=wrapper> <div id=head> <div cla..........</div> </div> </div> </body> </html> #中间内容太多以...省略,可以看出有乱码
可以看到有乱码,所以我们应该设置,如我们用内容推测的编码设置为编码方式
response = requests.get("https://www.baidu.com") response.encoding = response.apparent_encoding print(response.text)
运行结果可以看到,HTML文档已经正常显示
二进制内容:在python3中,字符串和bytes类型彻底分开了。字符串是以字符为单位进行处理的,bytes是以字节为单位处理的。bytes对象只负责以二进制字节序列的形式表示对象,至于该对象具体是什么,则由相应的编码格式解码所决定。bytes通常用于网络数据传输、二进制图片、视频、音频和文件保存等。
下面以图片为例子,视频和音频相对图片保存更为麻烦一点点。。。后面记录
#第一种,以文件写入的方式保存,因为提供二进制数据写入 import requests def getImage(url): try: response = requests.get(url) if response.status_code == 200: with open("image.jpg","wb") as f: f.write(response.content) except requests.ConnectionError: return None getImage("http://p3.pstatp.com/origin/pgc-image/cf4d4c2fac07430fa6f825d75e538b09") #第二种,专门的库来操作 from PIL import Image from io import BytesIO import requests def getImage(url): try: response = requests.get(url) if response.status_code == 200: image = Image.open(BytesIO(response.content)) image.save("image1.jpg") except requests.ConnectionError: return None getImage("http://p3.pstatp.com/origin/pgc-image/cf4d4c2fac07430fa6f825d75e538b09")
JSON数据内容:
import requests r = requests.get('https://api.github.com/events') r.json()
上面代码中,如果JSON解码失败,r.json()会抛出一个异常。比如说,如果响应状态码为204(无内容)或者该响应不包含有效的JSON数据,那么我们调用r.json()的时候出现异常:ValueError: No JSON object could be decoded
.
另外还得注意的是,调用r.json()方法成功不代表请求响应成功。一些服务器可能会在一个失败的响应中返回一个JSON对象(比如HTTP500错误),这类JSON对象会被解码并返回。可以调用r.raise_for_status()方法检查请求是否成功,或者调用r.status_code()确定状态码是否预期的一样
Custom Headers
如果想要在请求中加入HTTP请求头部信息,可以向headers参数传递一个字典类型数据。
import requests myInfomation = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36", ":authority":"2.python-requests.org", "accept-encoding":"gzip, deflate, br" } response = requests.get("https://2.python-requests.org//en/master/user/quickstart/#make-a-request",headers = myInfomation)
注意:自定义的请求头的优先级要低于更具体的信息源。因为我们知道请求头部信息中可以设置很多,比如proxy比如cookies。而我们用requests库中的请求方法时,本身就带有这些参数(上面介绍的15个或者是其它信息参数之类的)。所以headers这个参数里面自定义的信息和外面的参数信息相同时,headers里面自定义的将会被请求方法参数的信息覆盖。比如.netrc指定了证书,那么headers里面设定的认证信息,将会被auth参数覆盖。
注意:所有头部信息必须是是字符、字节码字符或者是Unicode字符。条件允许的情况下,建议避免传递Unicode编码的头部信息
More complicated POST requests
有时候我们想要传递一些格式编码数据,比如说HTML表单,完成这个操作只需要将一个字典数据传递给data参数。你的字典数据在进行请求时将自动被格式编码。该字典数据中的键可以同时拥有一个或多个值。可以是元组列表或者值组成列表的字典,如:
import requests # 传入元组组成的列表数据 payload_tuples = [("key1","value1"),("key2","value2")] r1 = requests.post("https://httpbin.org/post",data = payload_tuples) #传入字典数据,键可以包含一个或多个值 payload_dict = {"key1":["value1","value2"]} r2 = requests.post("https://httpbin.org/post",data = payload_dict)
有时候我们想要传递非格式编码的数据,如果我们传递一个字符数据而不是字典,则该数据将之间传递,而不会被格式编码。
import json url = "https://api.github.com/some/endpoint" payload = {"some":"data"} # 通过json.dunps将字典类型数据转化为字符 r = requests.post(url,data = json.dumps(payload))
这是我们自己手动将字典类型数据转换为字符串,我们还可以直接将这个字典数据传递给json参数(在python2.4.2版本增加的参数),该字典数据将自动被编码为字符串
import requests url = "https://api.github.com/some/endpoint" payload = {"some":"data"} r = requests.post(url,json = payload)
Response Status Codes
检查响应状态码:
import requests r = requests.get("https://httpbin.org/get") r.status_code
请求同时会生成一个内置的状态码查询对象,方便我们参考
import requests r = requests.get("https://www.baidu.com") if r.status_code == requests.codes.ok: r.encoding = r.apparent_encoding return r.text
如果我们发送一个错误的请求,(比如一个4xx的客户端异常或则一个5xx服务器响应异常),我们可以用Response.raise_for_status()来抛出异常。
import requests bad_r = requests.get('https://httpbin.org/status/404') bad_r.status_code bad_r.raise_for_status()
运行结果:
404 #状态码为404,找不到页面 Traceback (most recent call last): File "requests/models.py", line 832, in raise_for_status raise http_error #抛出异常为:http_error异常 requests.exceptions.HTTPError: 404 Client Error
但是,假如我们的状态码为200的话,当我们调用raise_for_status()方法时,返回的None,即一切正常
Cookies
如果响应中包含一些Cookies,你可以快速处理他们:
import requests headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" } response = requests.get("https://www.baidu.com",headers = headers) print(response.cookies) for key,value in response.cookies.items(): print("/nKey":key) print("Value":value)
设置cookies,可以在headers中设置,可以在Cookies参数中设置。在headers中设置Cookies相对简单,可以直接从网站Headers中复制,然后粘贴在cookies参数中。而Cookies参数时一个CookieJar类型,需要构造一个RequestsCookieJar对象,然后用set来设置cookie
import requests #headers中设置cookies,从网站复制 headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36", "Cookie":"BAIDUID=40A6B0BFC576C1E7D9CED35CF3DEFDF7:FG=1; BIDUPSID=40A6B0BFC576C1E7D9CED35CF3DEFDF7; PSTM=1545209354; BD_UPN=12314753; MCITY=-%3A;
BDUSS=1Fc2pvTFBob0NEWWxnaDdQcVFGSEEwNFlEd014S0R3ci1PTHNyR2tQLTU5T2xjSVFBQUFBJCQAAAAAAAAAAAEAAACykrc-TXJfQXJheQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALlnwly5Z8JcTk;
BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; H_PS_PSSID=1424_21092_18559_20698_29064_28519_28767_28722_28964_28835_28584;
yjs_js_security_passport=8ac336bc7349cbb33b50389724866151ac976f4c_1558059446_js; BD_HOME=1; delPer=0; BD_CK_SAM=1;
PSINO=5; COOKIE_SESSION=73012_0_4_5_13_3_0_0_4_2_0_0_73011_0_2_0_1558069578_0_1558069576%7C9%230_1_1557884733%7C1; H_PS_645EC=22b7uQAwp%2B9z5LwZQNlnHS3S5v2rEfFq%2BjaZoNajO3RRDF47i40HYxsDJZk" } response = requests.get("https://www.baidu.com",headers = headers) #在cookies参数中设置,根据cookie的属性,如名称,值,域,路径、、等等设置 import requests cookie = requests.cookies.RequestsJar() cookie.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies') r = requests.get("https:httpbin.org/cookies",cookies = cookie) #也可以直接传递一个字典给cookies参数 cookie = { "name":"tasty_cookie", "value":"yum", "domain":"httpbin.org", "path":"/cookies" } r = requests.get("https://httpbin.org/cookies",cookies = cookie)
Redirection and History
默认除了HEAD方法外,其它请求都会执行重定向。我们可以用响应对象的history功能来追溯重定向。Response.history列表包含完整请求的Response对象,该列表按最初到最近的响应进行排序。通过设置allow_redirects参数可以设置是否允许重定向操作
Timeouts
我们可以通过timeout参数来设置Requests等待服务器响应的时间。如果不设置该参数,可能导致你的程序无期限挂起,即会一直等待服务器响应。
Errors and Exception
如果网络出现问题(如,DNS错误、拒绝连接等),Requests将抛出一个ConnectionError异常
如果HTTP请求返回一个请求失败的状态码,Response.raise_for_status()将抛出HTTPError
如果请求超时,则抛出Timeout异常
如果请求超过设置的最大重定向数,则抛出TooManyRedirects异常
所有Requests抛出的异常都是继承于requests.exceptions.RequestsException
Advanced Usage
Session Object
Session Object 可以维持请求之间的某一参数(cookies)。因为请求是独立无状态的,所以登录状态(cookies)不想每次请求都设置,可以使用Session对象。有了它我们可以维持一个会话,而且不用担心cookies问题,它会自动帮我们处理好。
import requests requests.get("https://httpbin.org/cookies/set/number/123456789") #先向httpbin网站请求,并设置cookies为123456789 r = requests.get("https://httpbin.org/cookies") #再一次向该网站请求,返回该cookies print(r.text) #结果为: { "cookies":{} } #用Session 对象 s = requests.Session() #实例化一个Session对象 s.get("https://httpbin.org/cookies/set/number/123456789") #请求httpbin网站,并设置cookies为123456789 r = s.get("https://httpbin.org/cookies") #再进行请求该网站的cookies print(r.text) #结果: { "cookies":"123456789" }
因为请求是无状态的,各个请求都是相互独立的,没有关联。第一个请求设置了cookies,所以这个请求中有cookies,而第二个请求中没有cookies,所以cookies为空。即前一个请求和后面一个请求之间没有关系。而Session维持请求,第一个请求设置了cookies,第二个请求返回cookies,因为Session使请求之间持续的,所以第二个请求返回了第一个请求设置的cookies
Request and Response Object
我们发起请求的时候主要是在做两件事,一:构建一个Request对象,发送到服务器,向服务器请求或查找一些资源。二:一旦请求得到响应,服务器产生并给我们返回一个Response对象。这个Response对象包含服务器返回的所有信息,同时还包含我们最初构建的Request对象。
查看服务器返回给我们的响应的头部信息:Response.headers
查看我们发送给服务器的请求的头部信息:Response.request.headers
Prepared Requests
构建一个请求
from requests import Request,Session headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" } s = Session() #实例化一个Session对象 request = Request("GET","https://www.baidu.com",headers = headers) #构建一个Request对象 prepared_request = s.prepare_request(request) #调用Session中的prepare_request()方法将Request对象转化为Prepared Request对象 response = s.send(prepared_request) #再调用send()方法发送该请求
SSL Cert Vetification
Requests对HTTPS请求进行验证SSL证书,就像一个网页浏览器一样。当我们发送HTTPS请求的时候,默认会自动验证SSL证书。如果无法验证SSL证书,Requests将会抛出SSLError异常。
import requests response = requests.get("https://www.12306.cn") print(response.status_code) #运行结果 requests.exceptions.SSLError: (” bad handshake: Error( ((’ SSL routines’,' tls_process_server_certificate',’certi干 icate veri干y failed ’)],)”,)
可以通过verify参数控制是否检查此证书,或这通过该参数,传递一个CA-BUNDLE文件或者是被官方CA信任的证书目录的路径,来指定证书。
import requests response = requests.get("https://www.12306.cn",verify = False) print(response.status_code) #运行结果
Warning (from warnings module):
File "D:\Python3\lib\site-packages\urllib3\connectionpool.py", line 847
InsecureRequestWarning)
InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
200
不过此时,他抛出了一个警告,正在发送未经验证的HTTPS请求,强烈建议添加验证证书。。。解决办法有忽略警告、捕获警告、或者指定证书
# 设置忽略警告的方式来屏蔽该警告 import requests from requests.packages import urllib3 urllib3.disable_warnings() #该模块能禁止所有urllib3所有警告 response = requests.get("https://www.12306.cn"verify = False) print(response.status_code) # 通过捕获警告到日志的方式忽略警告 import logging import requess logging.captureWarnings(True) response = requests.get("https://www.12306.cn",verify = False) print(response.status_code) # 指定证书 import requests response = requests.get("https://www.12306.cn",verify = "/path/certfile") print(response.status_code)
另外还可以通过指定一个本地证书作为客户端证书,这个本地证书可以是单个文件(包含密匙和证书)或者是一个包含两个文件路径的元组。
import requests response = requests.get("https://www.12306.cn",cert = ("/path/client.cert","/path/client")
如果指定一个错误路径或者无效证书,将会抛出一个SSLError
Proxies
如果你需要使用代理,那么你可以通过Proxies参数将单个请求设置为任何请求方法。
import requests proxies = { 'http': 'http://10.10.1.10:3128', 'https': 'http://10.10.1.10:1080', } requests.get('http://example.org', proxies=proxies)
可以通过设置环境变量HTTP_PROXY和HTTPS_PROXY来设置代理。
$ export HTTP_PROXY="http://10.10.1.10:3128" $ export HTTPS_PROXY="http://10.10.1.10:1080" $ python >>> import requests >>> requests.get('http://example.org')
如果你的代理需要使用HTTP Basic Auth ,可以使用类似http://user:password@host:port这样的语法来设置代理
import requests proxies = {'http': 'http://user:pass@10.10.1.10:3128/'} response = requests.get("https://www.baidu.com",proxies = proxies)
对一个代理给定具体的协议和主机,使用scheme://hostname这样的表单作为键(key),这样将会把所有请求与给定的协议以及精准的主机进行匹配。
import requests proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'} response = requests.get("https://www.baidu.com",proxies = proxies)
除了基本的HTTP代理外,requests还支持SOCKS协议的代理。这是一个可选特性,要求我们再使用之前安装额外的第三方库,
可以通过pip安装该第三方库
$ pip install requests[socks]
只要你安装了这些相关库,使用SOCKS代理就像使用HTTP代理一样简单
import requests proxies = { 'http': 'socks5://user:pass@host:port', 'https': 'socks5://user:pass@host:port' } response = requests.get("https://www.baidu.com",proxies = proxies)