python requests库学习
requests
python的request库官方介绍就是让http服务人类,所以从这点我们就可以知道request库是为了让我们更加方便的进行http相关的各种操作
我们学习request有什么用呢?
1)web时代我们需要熟悉掌握web交互原理
2)爬虫
3)服务器编程
4)自动化测试
实验环境准备
首先是环境的准备,首先我们肯定要装requests库 直接使用pip命令即可(注意:本文使用的是py3.6版本)
同时我们需要一个服务器来测试我们的各种操作,我们可以直接使用requests库作者写的一个网站 httpbin.org 来进行我们的各种实验操作
requests例程
我们可以从下面的简单的例程来简单了解下requests
import requests #实验web地址 http://httpbin.org url_ip='http://httpbin.org/ip' url_get='http://httpbin.org/get' #直接使用 def requests_simple(): #利用get方法得到一个response response1=requests.get(url_ip) #打印头部headders print('response headers:',response1.headers) #打印body print('response body:',response1.text) #带参数的请求 def requests_params(): params_test={'param1':'hello','param2':'world'} #发送请求 response2=requests.get(url_get,params=params_test) #处理响应 #打印头部headders print('response headers:', response2.headers) #打印status code print('response status code',response2.status_code) # 打印body 上面用到的是text 我们可以直接使用 .json()方法得到js格式 print('response body:', response2.json()) if __name__=='__main__': print('___________requests_simple方法____________') requests_simple() print('___________requests_params方法____________') requests_params()
看下输出结果:
___________requests_simple方法____________ response headers: {'access-control-allow-credentials': 'true', 'access-control-allow-origin': '*', 'content-encoding': 'gzip', 'content-type': 'application/json', 'date': 'tue, 13 aug 2019 08:21:40 gmt', 'referrer-policy': 'no-referrer-when-downgrade', 'server': 'nginx', 'x-content-type-options': 'nosniff', 'x-frame-options': 'deny', 'x-xss-protection': '1; mode=block', 'content-length': '56', 'connection': 'keep-alive'} response body: { "origin": "115.51.238.17, 115.51.238.17" } ___________requests_params方法____________ response headers: {'access-control-allow-credentials': 'true', 'access-control-allow-origin': '*', 'content-encoding': 'gzip', 'content-type': 'application/json', 'date': 'tue, 13 aug 2019 08:21:41 gmt', 'referrer-policy': 'no-referrer-when-downgrade', 'server': 'nginx', 'x-content-type-options': 'nosniff', 'x-frame-options': 'deny', 'x-xss-protection': '1; mode=block', 'content-length': '215', 'connection': 'keep-alive'} response status code 200 response body: {'args': {'param1': 'hello', 'param2': 'world'}, 'headers': {'accept': '*/*', 'accept-encoding': 'gzip, deflate', 'host': 'httpbin.org', 'user-agent': 'python-requests/2.18.4'}, 'origin': '115.51.238.17, 115.51.238.17', 'url': 'https://httpbin.org/get?param1=hello¶m2=world'}
程序中每一步都有详细注解,就不再赘述
requests发送请求
请求方法:
- get: 查看资源
- post: 增加资源
- put: 修改资源
- patch: 更新资源
- delete: 删除资源
- head: 查看响应头
- options: 查看可用请求方法
我们利用github提供的api接口来进行详解 网址
我们先来看下github上对于http请求方法的解释
例程 使用github api接口查看某个用户的公开信息
网址:
如图中最后一行显示,我们可以使用 get /users/:username 来获取信息 下面为代码:
import requests import json url='https://api.github.com' #构建url函数 我们需要提交的url是原api网址加上我们自己额外添加的参数 # 所以简单写一个组成url的函数 即添加 '/' 号 def build_url(conend): return '/'.join([url,conend]) #更好的显示返回json的打印参数 #我们从网页得到的json可以使用下面函数来更好的print def better_jsprint(json_str): return json.dumps(json.loads(json_str),indent=4) #主体函数 get请求得到回应 例如我们查看用户名为test的公开资料 def requests_method(): response=requests.get(build_url('users/test')) print(better_jsprint(response.text)) if __name__=="__main__": requests_method()
结果返回:
{ "login": "test", "id": 383316, "node_id": "mdq6vxnlcjm4mzmxng==", "avatar_url": "https://avatars3.githubusercontent.com/u/383316?v=4", "gravatar_id": "", "url": "https://api.github.com/users/test", "html_url": "https://github.com/test", "followers_url": "https://api.github.com/users/test/followers", "following_url": "https://api.github.com/users/test/following{/other_user}", "gists_url": "https://api.github.com/users/test/gists{/gist_id}", "starred_url": "https://api.github.com/users/test/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/test/subscriptions", "organizations_url": "https://api.github.com/users/test/orgs", "repos_url": "https://api.github.com/users/test/repos", "events_url": "https://api.github.com/users/test/events{/privacy}", "received_events_url": "https://api.github.com/users/test/received_events", "type": "user", "site_admin": false, "name": null, "company": null, "blog": "", "location": null, "email": null, "hireable": null, "bio": null, "public_repos": 5, "public_gists": 0, "followers": 23, "following": 0, "created_at": "2010-09-01t10:39:12z", "updated_at": "2019-02-13t02:44:23z" }
这样我们就得到了名称为test的用户的公开信息了
同样的我们也可以使用githubapi文档中的其他方法进行测试
带参数的请求
首先来看常用的带参数请求的三种方法
params参数请求例程:
还是使用github的api接口 这次我们使用下面这个提供的请求方法
可以看到该方法中需要传入 params参数 下面是程序代码:
import requests import json url='https://api.github.com' #构建url函数 我们需要提交的url是原api网址加上我们自己额外添加的参数 # 所以简单写一个组成url的函数 即添加 '/' 号 def build_url(conend): return '/'.join([url,conend]) #更好的显示返回json的打印参数 #我们从网页得到的json可以使用下面函数来更好的print def better_jsprint(json_str): return json.dumps(json.loads(json_str),indent=4) #主体函数 添加params参数来进行请求 def requests_params(): response=requests.get(build_url('users'),params={'since':11}) #打印返回信息 print (better_jsprint(response.text)) #查看具体的url地址 print (response.url) if __name__=="__main__": requests_params()
返回结果很长就不再展示 只展示下此请求的url地址为 https://api.github.com/users?since=11
json参数请求例程
例程1 我们使用api中更新用户信息的方法来测试json参数请求
下面为代码:
import requests import json url='https://api.github.com' #构建url函数 我们需要提交的url是原api网址加上我们自己额外添加的参数 # 所以简单写一个组成url的函数 即添加 '/' 号 def build_url(conend): return '/'.join([url,conend]) #更好的显示返回json的打印参数 #我们从网页得到的json可以使用下面函数来更好的print def better_jsprint(json_str): return json.dumps(json.loads(json_str),indent=4) #主体函数 添加json参数来进行请求 下面代码中的auth用来账户认证 即自己的账户密码 def requests_json(): response=requests.patch(build_url('user'),auth=('此处为你的账户','此处为你的密码'),json={'name':'此处为你想要改的名字'}) #打印返回信息 print (better_jsprint(response.text)) #查看具体的url地址 print (response.url) if __name__=="__main__": requests_json()
运行代码之后 我们可以打开github主页查看自己的账户名称是否改变
例程2 使用post json参数请求添加一个email地址
代码展示:
import requests import json url='https://api.github.com' #构建url函数 我们需要提交的url是原api网址加上我们自己额外添加的参数 # 所以简单写一个组成url的函数 即添加 '/' 号 def build_url(conend): return '/'.join([url,conend]) #更好的显示返回json的打印参数 #我们从网页得到的json可以使用下面函数来更好的print def better_jsprint(json_str): return json.dumps(json.loads(json_str),indent=4) #主体函数 添加json参数来进行请求 代码中的auth参数为认证信息 即自己的github账户密码 json参数传入要添加的email地址 def requests_json(): response=requests.post(build_url('user/emails'),auth=('账户','密码'),json=['test@qq.com']) #打印返回信息 print (better_jsprint(response.text)) #查看具体的url地址 print (response.url) if __name__=="__main__": requests_json()
返回内容:
[ { "email": "1231231230@qq.com", "primary": true, "verified": true, "visibility": "private" }, { "email": "32220+h3213@users.noreply.github.com", "primary": false, "verified": true, "visibility": null }, { "email": "test@qq.com", "primary": false, "verified": false, "visibility": null } ] https://api.github.com/user/emails
至此 我们已经完成了使用json参数进行请求
请求异常处理
我们都知道在互联网上经常会出现很多错误,比如超时,更比如404之类的,当我们写的程序遇到请求异常该如何处理呢?
我们以超时(timeout)和httperr为例写出一个例程来分析:
首先错误都在requests包中的exceptions中 所以我们要引用:
from requests import exceptions
再例如对于timeout的设置有两种方法:
- requests.get(url,timeout=(3,7))
- requests.get(url,timeout=10)
区别是什么呢? 我们都知道访问一个网站是我们发送一个请求,然后网站给予我们一个响应,所以第一种方法中的3和7分别对应这两个过程的超时时间限制,
第二种则是整个访问过程的时间限制
timeout例程:
import requests import json from requests import exceptions url='https://api.github.com' #构建url函数 我们需要提交的url是原api网址加上我们自己额外添加的参数 # 所以简单写一个组成url的函数 即添加 '/' 号 def build_url(conend): return '/'.join([url,conend]) #更好的显示返回json的打印参数 #我们从网页得到的json可以使用下面函数来更好的print def better_jsprint(json_str): return json.dumps(json.loads(json_str),indent=4) #主体函数 添加timeout参数限制访问时间,使用try except来捕获错误并打印 def requests_err(): try: response=requests.get(build_url('user/emails'),timeout=0.1) except exceptions.timeout as err: print(err) if __name__=="__main__": requests_err()
返回值:
httpsconnectionpool(host='api.github.com', port=443): max retries exceeded with url: /user/emails (caused by connecttimeouterror(<urllib3.connection.verifiedhttpsconnection object at 0x000002990f1a3f28>, 'connection to api.github.com timed out. (connect timeout=0.1)'))
httperror例程:
httperror例程中我们可以显式抛出状态值statuscode然后捕获
import requests import json from requests import exceptions url='https://api.github.com' #构建url函数 我们需要提交的url是原api网址加上我们自己额外添加的参数 # 所以简单写一个组成url的函数 即添加 '/' 号 def build_url(conend): return '/'.join([url,conend]) #更好的显示返回json的打印参数 #我们从网页得到的json可以使用下面函数来更好的print def better_jsprint(json_str): return json.dumps(json.loads(json_str),indent=4) #主体函数 下列代码中并未添加认证信息,所以状态值不为200,我们需要raise出状态值然后捕获 def requests_err(): try: response=requests.get(build_url('user/emails')) response.raise_for_status() except exceptions.httperror as err: print(err) if __name__=="__main__": requests_err()
返回值:
401 client error: unauthorized for url: https://api.github.com/user/emails
定制请求
我们可以自定义构造一些信息然后向目标网址发送来实现某些功能 比如爬虫之类的 下面举简单的修改头部信息
例程:
import requests #主体函数 在代码中添加头部信息来向目标网址发送自己定制请求 def requests_header(): response=requests.get('http://httpbin.org/get',headers={'user-agent':'fake'}) print(response.text) if __name__=="__main__": requests_header()
看看返回信息:(http://httpbin.org/get 会返回我们的信息)
{ "args": {}, "headers": { "accept": "*/*", "accept-encoding": "gzip, deflate", "host": "httpbin.org", "user-agent": "fake" }, "origin": "115.51.238.17, 115.51.238.17", "url": "https://httpbin.org/get" }
注意看我们的头部信息已经完成修改
响应的处理
# 响应的常用属性
response.text # 响应回去的文本(字符串)
response.content # 响应回去的内容(二进制),一般用来爬取视频
response.status_code # 响应的状态码
response.url # 获取请求连接地址
response.cookies # 获取返回的cookies信息
response.cookies.get_dict() # 获取返回的cookies信息
response.request # 获取请求方式
response.headers # 查看响应头
response.history # 重定向历史 即前一次请求的地址
# 返回结果为json数据处理
response.json() # 将结果进行反序列化
# 爬取文档乱码问题
response.apparent_encoding # 文档的编码的方式(从html文档找)
response.encoding # 响应体编码方式
eg: response.encoding = response.apparent_encoding # 文档的声明方式
eg: print(response.text.encode('utf-8'))
注意:response.headers是服务器发送给我们的头部信息,response.request.headers才是我们这个客服端向服务器发请求的头部信息(即自己的信息)
下载文件
例如我们现在去网络下载一张图片,只不过现在我们使用requests库来完成
就比如百度官网logo 地址:
简单的下载例程如下:
import requests url='https://www.baidu.com/img/bd_logo1.png' def download_img(): response=requests.get(url) with open('logo.png','wb') as img:#此步骤涉及文件读写操作 图片是二进制,所以要用二进制写文件 用参数 'wb' img.write(response.content) if __name__=='__main__': download_img()
然后会发现当前目录下出现了 logo.png的图片 此时已经下载成功
需要注意的是 有时候文件很大,我们需要以数据流的方式读写,否则可能造成内存溢出问题,
当下载大的文件的时候,我们可以在requests.get()中使用stream参数.
默认情况下是false,他会立即开始下载文件并存放到内存当中,倘若文件过大就会导致内存不足的情况.
当把get函数的stream参数设置成true时,它不会立即开始下载,当你使用iter_content或iter_lines遍历内容或访问内容属性时才开始下载。需要注意一点:文件没有下载之前,它也需要保持连接。
- iter_content:一块一块的遍历要下载的内容
- iter_lines:一行一行的遍历要下载的内容
使用上面两个函数下载大文件可以防止占用过多的内存,因为每次只下载小部分数据。
例程:
import requests url='https://www.baidu.com/img/bd_logo1.png' def download_img(): response=requests.get(url,stream=true)#stream参数记得选为true with open('logo.png','wb') as img:#图片是二进制,所以要用二进制写文件 用参数 'wb' for chunk in response.iter_content(1024):#规定一次读取写入大小 单位为字节 img.write(chunk) response.close()#关闭数据流连接 if __name__=='__main__': download_img()
有时候会被服务器禁止请求,原因可能是user-agent未更改,这就相当于简单的反爬虫了,所以一般我们写爬虫的时候需要修改user-agent信息,这些问题在讲解爬虫时会讲
简单总结流程就是:
浏览器模拟(修改headers)-->构建request-->读取数据-->写入数据
身份认证
auth
前面已经讲过,我们可以传入 auth 参数来将自己的账号密码传入目标网址来进行身份验证,可是这种方式是安全的吗?
例如:
import requests url='https://api.github.com' #构建url函数 我们需要提交的url是原api网址加上我们自己额外添加的参数 # 所以简单写一个组成url的函数 即添加 '/' 号 def build_url(conend): return '/'.join([url,conend]) #基础身份认证 def http_auth(): response=requests.get(build_url('user'),auth=('test','test123')) #看下我们的请求的头部数据 print(response.request.headers) http_auth()
我们再看下返回头部数据:
{'user-agent': 'python-requests/2.18.4', 'accept-encoding': 'gzip, deflate', 'accept': '*/*', 'connection': 'keep-alive', 'authorization': 'basic dgvzddp0zxn0mtiz'}
我们发现里面含有 'authorization': 'basic dgvzddp0zxn0mtiz' 一段 那这个会是我们的账号密码吗? 我们使用解码来看下
import base64 print(base64.b64decode('dgvzddp0zxn0mtiz'))
输出:
b'test:test123'
发现还真的是我们的账号密码,所以这样也不是很安全
因此现在广泛使用更安全的oauth认证
oauth
oauth是open authorization的简写,oauth协议为用户资源的授权提供了一个安全的、开放而又建议的标准。第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此oauth是安全的。
最最简单的例子就是 现在我们登录多个网站(例如微博,网盘等)应用可以直接选择qq快速登录,这就是oauth认证,它无需你输入账户密码即可认证
下面我们还是用github的api来做一个简单的oauth认证
首先打开github网址: 来申请一个自己的令牌认证 记得勾选需要的权限选项
然后我们就得到了一串神秘代码如下图
接下来我们开始写简单的例程:
import requests url='https://api.github.com' #构建url函数 我们需要提交的url是原api网址加上我们自己额外添加的参数 # 所以简单写一个组成url的函数 即添加 '/' 号 def build_url(conend): return '/'.join([url,conend]) #基础oauth认证 def http_oauth(): #构建头部数据 加上token信息 header={'authorization':'token 62646e55689b597eb076cb08c5e020e3762bc84f '} response=requests.get(build_url('user'),headers=header) print(response.text) http_oauth()
运行发现登录成功 虽然这是个个人身份令牌 但是oauth原理也是如此
cookie 和 session
cookie,有时也用其复数形式 cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。
这样做的好处就是cookie的体积变小,只存储session-id信息类似于上面讲的token,并且session信息存储在服务器端也比较安全
至此,requests库的学习就先告一段落,但是哦我们还有更多高阶操作可以去学习。
上一篇: 《静儿的服务治理私房菜》服务治理和架构
下一篇: jQuery遍历(2)