Python安全 Requests学习
简介
开发哲学
Beautiful is better than ugly.(美丽优于丑陋)
Explicit is better than implicit.(直白优于含蓄)
Simple is better than complex.(简单优于复杂)
Complex is better than complicated.(复杂优于繁琐)
Readability counts.(可读性很重要)
Apache2 协议
Requests 协议
功能特性
Requests 完全满足今日 web 的需求。
Keep-Alive & 连接池
国际化域名和 URL
带持久 Cookie 的会话
浏览器式的 SSL 认证
自动内容解码
基本/摘要式的身份认证
优雅的 key/value Cookie
自动解压
Unicode 响应体
HTTP(S) 代理支持
文件分块上传
流下载
连接超时
分块请求
支持 .netrc
Requests 支持 Python 2.6—2.7以及3.3—3.7,而且能在 PyPy 下完美运行。
安装Requests
pip install requests -i https://mirror.aliyun.com/pypi/simple
快速上手
发送请求
导入Requests模块
import requests
各种请求方法
r = requests.get('https://http://httpbin.org/get')
r = requests.post('http://httpbin.org/post',data={'key':'value'})
r = requests.put('http://httpbin.org/put',data={'key':'value'})
r = requests.delete('http://httpbin.org/delete')
r = requests.head('http://httpbin.org/get')
r = requests.options('http://httpbin.org/get')
传递URL参数
为URL的查询字符串传递某种数据,数据以键/值对的形式置于URL中,跟在一个问号后面
httpbin.org/get?key=val
Requests允许使用params关键字参数,以一个字符串字典来提供这些参数。
传递 key1=value1和key2=value2到httpbin.org/get
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.get("http://httpbin.org/get", params=payload)
打印输出该URL
>>> print(r.url)
http://httpbin.org/get?key2=value2&key1=value1
注意字典里值为 None 的键都不会被添加到 URL 的查询字符串里。
将一个列表作为值传入:
>>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}
>>> r = requests.get('http://httpbin.org/get', params=payload)
>>> print(r.url)
http://httpbin.org/get?key2=value2&key2=value3&key1=value1
二进制响应内容
读取服务器端响应的内容
>>> import requests
>>> r = requests.get('http://httpbin.org/get')
>>> r.text
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.19.1"
},
"origin": "223.104.170.227",
"url": "http://httpbin.org/get"
}
编码属性 r.encoding
设置文本编码属性
r.encoding = 'ISO-8859-1'
如果你改变了编码,每当你访问 r.text ,Request 都将会使用 r.encoding 的新值。你可能希望在使用特殊逻辑计算出文本的编码的情况下来修改编码。比如 HTTP 和 XML 自身可以指定编码。这样的话,你应该使用 r.content 来找到编码,然后设置 r.encoding 为相应的编码。这样就能使用正确的编码解析 r.text 了.
以字节的方式访问请求响应体
r.content
Requests 会自动为你解码 gzip 和 deflate 传输编码的响应数据。
JSON响应内容
Requests 中也有一个内置的 JSON 解码器,助你处理 JSON 数据:
import requests
r = requests.get('http://httpbin.org/get')
print r.json()
{u'origin': u'223.104.170.227', u'headers': {u'Connection': u'close', u'Host': u'httpbin.org', u'Accept-Encoding': u'gzip, deflate', u'Accept': u'*/*', u'User-Agent': u'python-requests/2.19.1'}, u'args': {}, u'url': u'http://httpbin.org/get'}
原始响应内容
获取来自服务器的原始套接字响应 r.raw 在初始请求中设置stream=True
r = requests.get('https://httpbin.org/get',stream=True)
print r.raw
<urllib3.response.HTTPResponse object at 0x0000000003D1F5C0>
定制请求头
为请求添加HTTP头部,使用字典给headers传递参数
import requests
url = 'https://httpbin.org/get'
headers = {'user-agent':'my-app/0.0.1'}
r = requests.get(url,headers=headers)
print r.text
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Host": "httpbin.org",
"User-Agent": "my-app/0.0.1"
},
"origin": "223.104.34.96",
"url": "https://httpbin.org/get"
}
注意: 所有的 header 值必须是 string、bytestring 或者 unicode。尽管传递 unicode header 也是允许的,但不建议这样做。
更加复杂的POST请求
发送一些编码为表单形式的数据,通过传递一个字典data参数,数据字典在发出请求时自动编码为表单形式
payload = {'key1':'value1','key2':'value2'}
r = requests.post("http://httpbin.org/post",data = payload)
print(r.text)
{
"args": {},
"data": "",
"files": {},
"form": {
"key1": "value1",
"key2": "value2"
},
"headers": {
"Accept": "*/*",
"Connection": "close",
"Content-Length": "23",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.19.1"
},
"json": null,
"origin": "223.104.34.96",
"url": "http://httpbin.org/post"
}
为data参数传入一个元组列表,在表单中多个元素使用同一key的时候,尤为有效
payload = (('key1','value1'),('key1','value2'))
r = requests.post('http://httpbin.org/post',data=payload)
print(r.text)
{
"args": {},
"data": "",
"files": {},
"form": {
"key1": [
"value1",
"value2"
]
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Content-Length": "23",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.19.1"
},
"json": null,
"origin": "223.104.34.96",
"url": "http://httpbin.org/post"
}
很多时候你想要发送的数据并非编码为表单形式的。如果你传递一个 string 而不是一个 dict,那么数据会被直接发布出去。
import json
url = 'https://api.github.com/some/endpoint'
payload = {'some':'data'}
r = requests.post(url,data=json.dumps(payload))
除了可以自行对 dict 进行编码,你还可以使用 json 参数直接传递,然后它就会被自动编码。这是 2.4.2 版的新加功能:
import json
url = 'https://api.github.com/some/endpoint'
payload = {'some':'data'}
r = requests.post(url,data=json.dumps(payload))
print r.text
POST一个多部分编码的文件
Requests 使得上传多部分编码文件变得很简单
url = 'http://httpbin.org/post'
files = {'file':open('report.xls','rb')}
r = requests.post(url,proxies=proxies,verify=False,files=files)
r.text
可以显示地设置文件名,文件类型和请求头:
url = 'http://httpbin.org/post'
files = {'file':('report.xls',open('report.xls','rb'),'application/vnd.ms-excel',{'Expires':'0'})}
r = requests.post(url,proxies=proxies,verify=False,files=files)
r.text
也可以发送作为文件来接收的字符串
url = 'http://httpbin.org/post'
files = {'file':('report.csv','some,data,to,send\nanother,row,to,send\n')}
r = requests.post(url,proxies=proxies,files=files)
r.text
请求如下
POST /post HTTP/1.1
Host: httpbin.org
Connection: close
Accept: */*
User-Agent: python-requests/2.19.1
Content-Length: 184
Content-Type: multipart/form-data; boundary=8511d039a04c9bce4ac34eb58060d326
--8511d039a04c9bce4ac34eb58060d326
Content-Disposition: form-data; name="file"; filename="report.csv"
some,data,to,send
another,row,to,send
--8511d039a04c9bce4ac34eb58060d326--
如果发送一个非常打的文件作为multipart/form-data请求,想把请求做成数据流。默认下requests不支持,但是有第三方包requests-toolbelt是支持的
响应状态码
检测响应状态码
r = requests.get('http://httpbin.org/get')
r.tatus_code
200
为了方便引用,Requests还附带了一个内置的状态码查询对象:
r.status_code == requests.codes.ok
True
如果发送了一个错误请求(一个 4XX 客户端错误,或者 5XX 服务器错误响应),我们可以通过 Response.raise_for_status() 来抛出异常:
bad_r = requests.get('http://httpbin.org/status/404')
print bad_r.status_code
404
bad_r.raise_for_status()
requests.exceptions.HTTPError: 404 Client Error:
响应头
可以查看以一个python字典形式展示的服务器响应头
r = requests.get('http://httpbin.org/get')
print r.headers
{
'Content-Length': '266',
'Via': '1.1 vegur',
'Server': 'gunicorn/19.9.0',
'Connection': 'keep-alive',
'Access-Control-Allow-Credentials': 'true',
'Date': 'Thu, 09 Aug 2018 02:41:29 GMT', 'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json'
}
但是这个字典比较特殊:它是仅为 HTTP 头部而生的。根据 RFC 2616, HTTP 头部是大小写不敏感的。
因此,我们可以使用任意大写形式来访问这些响应头字段:
r = requests.get('http://httpbin.org/get')
print r.headers['Content-Type']
application/json
r = requests.get('http://httpbin.org/get')
print r.headers['content-type']
application/json
它还有一个特殊点,那就是服务器可以多次接受同一 header,每次都使用不同的值。但 Requests 会将它们合并,这样它们就可以用一个映射来表示出来,参见 RFC 7230
Cookie
如果某个响应中包含一些cookie,通过下面方式快速访问
发送cookies到服务器,可以使用cookies参数:
url = 'http://httpbin.org/cookies'
cookies = dict(cookie_are='working')
r = requests.get(url,cookies=cookies)
print r.text
{
"cookies": {
"cookie_are": "working"
}
}
Cookie 的返回对象为 RequestsCookieJar,它的行为和字典类似,但接口更为完整,适合跨域名跨路径使用。你还可以把 Cookie Jar 传到 Requests 中:
jar = requests.cookies.RequestsCookieJar()
jar.set('tasty_cookie','yum',domain='httpbin.org',path='/cookies/')
jar.set('gross_cookie','blech',domain='httpbin.org',path='/elsewhere')
url = 'http://httpbin.org/cookies'
r = requests.get(url,cookies=jar)
print r.text
{
"cookies": {"tasty_cookie":"yum"}
}
重定向与请求历史
默认情况下,除了 HEAD, Requests 会自动处理所有重定向。
可以使用响应对象的 history 方法来追踪重定向。
Response.history 是一个 Response 对象的列表,为了完成请求而创建了这些对象。这个对象列表按照从最老到最近的请求进行排序。
例如,GitHub将所有的HTTP请求重定向到HTTPS
r = requests.get('http://github.com')
print r.url
https://github.com/
r.status_code
r.history
https://github.com/
200
[<Response [301]>]
如果你使用的是GET、OPTIONS、POST、PUT、PATCH 或者 DELETE,那么你可以通过 allow_redirects 参数禁用重定向处理:
r = requests.get('http://github.com',allow_redirects=False)
print r.status_code
print r.history
301
[]
如果使用了HEAD,可以启用重定向:
r = requests.head('http://github.com',allow_redirects=True)
print r.url
print r.history
https://github.com/
[<Response [301]>]
超时
可以告诉 requests 在经过以 timeout 参数设定的秒数时间之后停止等待响应。基本上所有的生产代码都应该使用这一参数。如果不使用,你的程序可能会永远失去响应:
r = requests.head('http://github.com',timeout=0.1)
requests.exceptions.ConnectTimeout: HTTPConnectionPool(host='github.com', port=80):
注意
timeout 仅对连接过程有效,与响应体的下载无关。 timeout 并不是整个下载响应的时间限制,而是如果服务器在 timeout 秒内没有应答,将会引发一个异常(更精确地说,是在 timeout 秒内没有从基础套接字上接收到任何字节的数据时)If no timeout is specified explicitly, requests do not time out.
错误与异常
遇到网络问题(如:DNS 查询失败、拒绝连接等)时,Requests 会抛出一个 ConnectionError 异常。
如果 HTTP 请求返回了不成功的状态码, Response.raise_for_status() 会抛出一个 HTTPError 异常。
若请求超时,则抛出一个 Timeout 异常。
若请求超过了设定的最大重定向次数,则会抛出一个 TooManyRedirects 异常。
所有Requests显式抛出的异常都继承自 requests.exceptions.RequestException
练习
#coding:utf-8
#auther:Ahodor
import requests
import sys
url = "https://www.baidu.com"
#代理
proxies = {
"http":"http://127.0.0.1:8080",
"https":"https://127.0.0.1:8080",
}
headers = {'user-agent':'my-aasjkfsjfgjl/0.0.1','asnk':'test for headers'}
cookies = dict(cookies_arg='working')
r = requests.get(url,proxies=proxies,verify=False,headers=headers,cookies=cookies)
#响应码
print r.status_code
#查看响应内容
print r.text
print r.content
#重定向
r = requests.get(url,allow_redirects=False)
print r.history
#修改系统默认编码格式
reload(sys)
sys.setdefaultencoding('utf-8')
#查看请求头
print r.request.headers
#查看请求参数
print r.request.body
#查看响应头
print r.headers
#保存返回的结果
fh = open("abc.png",'w')
fh.write(r.content)
fh.close()
#查看返回的编码格式
print r.encoding
#更改返回的编码格式
r.encoding = 'utf-8'
#获得响应头里的cookies
print r.cookies
#如何发送post请求
payload = {'name':'Ahodor','age':'12'}
r = requests.post("https://",data=payload)
#设置超时
r = requests.get(url,timeout=5)
r = requests.get(url,timeout=None)
#使用session
conn = requests.session()
r = conn.get(url)
print r.request.headers
r = conn.get(url)
print r.request.headers
高级用法
身份认证
在GitHub中搜索关键字
这里尝试简单搜索 baidu
#auther:Ahodor
import requests
import json
key = "baidu"
url = "https://api.github.com/search/code?q=%s" % key
TOKEN = "d39xxxxxxxxxx061b6b10e2c1e0331xxxxxxxxxx"
headers = {"Authorization":"token %s" % TOKEN}
params = {"per_page":10,"page":0}
r = requests.get(url,headers=headers,params=params)
d = r.json()
print json.dumps(d,indent=4)