在 Python 众多的 HTTP 客户端中,最有名的莫过于 requests 、 aiohttp 和 httpx 。

在不借助其他第三方库的情况下, requests 只能发送同步请求; aiohttp 只能发送异步请求; httpx 既能发送同步请求,又能发送异步请求。

那么怎么选择呢

  • 只发同步请求用 requests ,但可配合多线程变异步。
  • 只发异步请求用 aiohttp ,但可以配合await变同步。
  • httpx 可以发同步请求也可以异步,但是请求速度同步略差于 requests ,异步略差于 aiohttp

Asyncio 的强大。但是,任何一种方案都不是完美的,都存在一定的局限性,Asyncio 同样如此。

实际使用中,想用好 Asyncio,特别是发挥其强大的功能,很多情况下必须得有相应的 Python 库支持。

比如

requests 库并不兼容 Asyncio,而 aiohttp 库兼容。

requests

这里先说 requests

安装依赖

pip install requests

响应

响应的类型

#获取接口返回的字符串数据
r.text#获取接口返回的json数据,即直接将json格式的数据转换为json对象
r.json()#获取接口返回的二进制数据,假设二进制数据如果为图片可以继续转换成图片
r.content#获取原始套接字,使用r.raw请在 requests 请求中加上参数 stream=True
r.raw

获取请求响应的其他信息

#获取状态码
r.status_code#获取请求的url
r.url#获取指定cookies信息
r.cookies['token']#获取访问服务器返回给我们的响应头部信息
r.headers#获取指定访问服务器返回给我们的响应头部信息
r.headers['Content-Type']#获取发送到服务器的请求的头部的信息
r.request.headers

请求

GET请求

get请求:

res = requests.get(url,data=data,cookies=cookie,headers=header,verify=False,files=file)

data可传可不传,data是字典格式。

如果url是https的话,加上verify=False。如果url是http的话,可不加。

示例1

import requestsif __name__ == '__main__':r = requests.get("https://www.psvmc.cn")print(r.text)

示例2

import requestsif __name__ == '__main__':data = {'username': 'admin', 'passwd': '123456'}r = requests.get("https://www.psvmc.cn/login.json", params=data)print(r.status_code)print(r.json()["obj"])

POST请求

url_post = "https://www.psvmc.cn/login.json"#不包含任何参数的请求
r = requests.post(url_post)#不包含任何参数的请求,设置超时10s,timeout不设置则默认60s
r = requests.post(url_post,timeout=10)#携带参数的请求,dict_param为参数字典,默认data=dict_param,使用data=则表示post的是form请求
#即 application/x-www-form-urlencoded 。
r = requests.post(url_post, data=dict_param)#携带参数的请求,dict_param为参数字典,使用json=则表示post的是json请求
r = requests.post(url_post, json=dict_param)#携带参数的请求,body传字符串,这里是JSON字符串。
r = requests.post(url_post, data=json.dumps(dict_param))#携带参数的请求,dict_param为参数字典,设置超时10s,并携带headers属性
r = requests.post(url_post,data=dict_param,timeout=10,headers={'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X)'})#post请求上传文件
url = 'http://apihost/upload/post'
files = {'file': open('report.xls', 'rb')}
r = requests.post(url, files=files)

其他类型请求

r = requests.put(url, data =dict_param)
r = requests.delete(url)
r = requests.head(url)
r = requests.options(url)

代理

跨域的时候可以考虑代理访问,不管是post请求还是get请求,只需要添加proxies即可。

客户端开发时不用考虑跨域问题,没有必要设置代理访问。

proxies = {"http": "http://10.10.1.10:3128","https": "http://10.10.1.10:1080",
}
requests.get(url_get, proxies=proxies)

查看代理是否有效

和telnet作用一样

import telnetlibif __name__ == '__main__':try:telnetlib.Telnet('110.242.68.4', port='80', timeout=3)except:print('ip无效!')else:print('ip有效!')

异步请求

aiohttp 的代码与 httpx 异步模式的代码重合度90%,只不过把 AsyncClient 换成了 ClientSession ,

另外,在使用 httpx 时,当你 await client.post 时就已经发送了请求。但是当使用 aiohttp 时,只有在 awiat resp.json() 时才会真正发送请求。

aiohttp

import aiohttp
import asyncioasync def main():async with aiohttp.ClientSession() as client:resp = await client.post('https://www.psvmc.cn/login.json',json={'ts': '2020-01-20 13:14:15'})result = await resp.json()print(result)asyncio.run(main())

httpx

import httpx
import asyncioasync def main():async with httpx.AsyncClient() as client:resp = await client.post('https://www.psvmc.cn/login.json',json={'ts': '2020-01-20 13:14:15'})result = resp.json()print(result)asyncio.run(main())

JSON

字符串转对象

import json# 一些 JSON:
x =  '{ "name":"Bill", "age":63, "city":"Seatle"}'# 解析 x:
y = json.loads(x)# 结果是 Python 字典:
print(y["age"])

对象转字符串

import json# Python 对象(字典):
x = {"name": "Bill","age": 63,"city": "Seatle"
}# 转换为 JSON:
y = json.dumps(x)# 结果是 JSON 字符串:
print(y)

当 Python 转换为 JSON 时,Python 对象会被转换为 JSON(JavaScript)等效项:

Python JSON
dict Object
list Array
tuple Array
str String
int Number
float Number
True true
False false
None null

异步IO(协程)和请求

pip install aiohttp

简单示例

import asyncioasync def test():await asyncio.sleep(3)return "123"async def main():result = await test()print(result)if __name__ == '__main__':asyncio.run(main())

异步请求

import asyncio
import aiohttp
import timeasync def download_one(url):async with aiohttp.ClientSession() as session:async with session.get(url) as resp:print('Read {} from {}'.format(resp.content_length, url))async def download_all(sites):tasks = [asyncio.ensure_future(download_one(site)) for site in sites]await asyncio.gather(*tasks)def main():sites = ['https://www.psvmc.cn/index.html','https://www.psvmc.cn/login.json','https://www.psvmc.cn/userlist.json']start_time = time.perf_counter()loop = asyncio.get_event_loop()try:loop.run_until_complete(download_all(sites))finally:if loop.is_running():loop.close()end_time = time.perf_counter()print('Download {} sites in {} seconds'.format(len(sites), end_time - start_time))if __name__ == '__main__':main()

多线程

from concurrent.futures import ThreadPoolExecutor
import threading# 定义一个准备作为线程任务的函数
def action(num):print(threading.current_thread().name)return num+100# 创建一个包含4条线程的线程池
with ThreadPoolExecutor(max_workers=3) as pool:future1 = pool.submit(action, 1000)def get_result(future):print(f"单个任务返回:{future.result()}")# 为future1添加线程完成的回调函数future1.add_done_callback(get_result)print('------------------------------')# 使用线程执行map计算results = pool.map(action, (50, 100, 150))for r in results:print(f"多个任务返回:{r}")

异步 IO/多进程/多线程对比

异步 IO(asyncio)、多进程(multiprocessing)、多线程(multithreading)

IO 密集型应用CPU等待IO时间远大于CPU 自身运行时间,太浪费;

常见的 IO 密集型业务包括:浏览器交互、磁盘请求、网络爬虫、数据库请求等

Python 世界对于 IO 密集型场景的并发提升有 3 种方法:多进程、多线程、异步 IO(asyncio);

理论上讲asyncio是性能最高的,原因如下:

  1. 进程、线程会有CPU上下文切换
  2. 进程、线程需要内核态和用户态的交互,性能开销大;而协程对内核透明的,只在用户态运行
  3. 进程、线程并不可以无限创建,最佳实践一般是 CPU*2;而协程并发能力强,并发上限理论上取决于操作系统IO多路复用(Linux下是 epoll)可注册的文件描述符的极限

那asyncio的实际表现是否如理论上那么强,到底强多少呢?我构建了如下测试场景:

访问500台 DB,并sleep 100ms模拟业务查询

  • 方法 1;顺序串行一台台执行
  • 方法 2:多进程
  • 方法 3:多线程
  • 方法 4:asyncio
  • 方法 5:asyncio+uvloop

最后的 asyncio+uvloop 和官方asyncio 最大不同是用 Cython+libuv 重新实现了asyncio 的事件循环(event loop)部分,

官方测试性能是 node.js的 2 倍,持平 golang。

以下测试代码需要 Pyhton3.7+:

顺序串行一台台执行

import recordsuser = "root"
pwd = "123456"
port = 3306
hosts = []  # 500台 db列表def query(host):conn = records.Database(f'mysql+pymysql://{user}:{pwd}@{host}:{port}/mysql?charset=utf8mb4')rows = conn.query('select sleep(0.1);')print(rows[0])def main():for h in hosts:query(h)# main entrance
if __name__ == '__main__':main()

多进程

from concurrent import futures
import recordsuser = "root"
pwd = "123456"
port = 3306
hosts = []  # 500台 db列表def query(host):conn = records.Database(f'mysql+pymysql://{user}:{pwd}@{host}:{port}/mysql?charset=utf8mb4')rows = conn.query('select sleep(0.1);')print(rows[0])def main():with futures.ProcessPoolExecutor() as executor:for future in executor.map(query,hosts):pass# main entrance
if __name__ == '__main__':main()

多线程

from concurrent import futures
import recordsuser = "root"
pwd = "123456"
port = 3306
hosts = []  # 500台 db列表def query(host):conn = records.Database(f'mysql+pymysql://{user}:{pwd}@{host}:{port}/mysql?charset=utf8mb4')rows = conn.query('select sleep(0.1);')print(rows[0])def main():with  .ThreadPoolExecutor() as executor:for future in executor.map(query,hosts):pass# main entrance
if __name__ == '__main__':main()

asyncio

import asyncio
from databases import Database user = "root"
pwd = "123456"
port = 3306
hosts = []  # 500台 db列表async def query(host):DATABASE_URL = f'mysql+pymysql://{user}:{pwd}@{host}:{port}/mysql?charset=utf8mb4'async with Database(DATABASE_URL) as database:query = 'select sleep(0.1);'rows = await database.fetch_all(query=query)print(rows[0])async def main():tasks = [asyncio.create_task(query(host)) for host in hosts]await asyncio.gather(*tasks)# main entrance
if __name__ == '__main__':asyncio.run(main())

asyncio+uvloop

import asyncio
import uvloop
from databases import Databaseuser = "root"
pwd = "123456"
port = 3306
hosts = []  # 500台 db列表async def query(host):DATABASE_URL = f'mysql+pymysql://{user}:{pwd}@{host}:{port}/mysql?charset=utf8mb4'async with Database(DATABASE_URL) as database:query = 'select sleep(0.1);'rows = await database.fetch_all(query=query)print(rows[0])async def main():tasks = [asyncio.create_task(query(host)) for host in hosts]await asyncio.gather(*tasks)# main entrance
if __name__ == '__main__':uvloop.install()asyncio.run(main())

运行时间对比

方式 运行时间
串行 1m7.745s
多进程 2.932s
多线程 4.813s
asyncio 1.068s
asyncio+uvloop 0.750s

可以看出: 无论多进程、多进程还是asyncio都能大幅提升IO 密集型场景下的并发,但asyncio+uvloop性能最高,运行时间只有原始串行运行时间的 1/90,相差快 2 个数量级了!

Python网络请求、JSON转换、多线程、异步IO相关推荐

  1. Python|并发编程|爬虫|单线程|多线程|异步I/O|360图片|Selenium及JavaScript|Scrapy框架|BOM 和 DOM 操作简介|语言基础50课:学习(12)

    文章目录 系列目录 原项目地址 第37课:并发编程在爬虫中的应用 单线程版本 多线程版本 异步I/O版本 总结 第38课:抓取网页动态内容 Selenium 介绍 使用Selenium 加载页面 查找 ...

  2. python 网络请求类库 requests 使用

    python 网络请求类库 requests 使用 requests是 为python封装的强大 REST 操作类库 github https://github.com/kennethreitz/re ...

  3. python 网络请求

    python 网络请求相比Android 网络请求要简单 1,你可以定义一个自己的请求头,也可以不写. 如: http_headers = {'Accept': 'application/json', ...

  4. IOS之Swift5.x和OC网络请求JSON

    IOS之Swift5.x和OC网络请求JSON // // ViewController.m // 基于OC网络解析JSON // // Created by 鲁军 on 2021/3/11. //# ...

  5. python字典与json转换_python字典与json转换的方法总结

    在python中json分别由列表和字典组成,本文主要介绍python中字典与json相互转换的方法.使用json.dumps可以把字典转成json字符串.使用json.loads可以把json字符串 ...

  6. Python网络请求urllib和urllib3详解

    1. 简介 urllib是Python中请求url连接的官方标准库,在Python2中主要为urllib和urllib2,在Python3中整合成了urllib. 而urllib3则是增加了连接池等功 ...

  7. Python网络请求库Requests,妈妈再也不会担心我的网络请求了(一)

    本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 极客导航 即可关注,每个工作日都有文章更新. 一.概况 网络请求可能是每门语言比较重要的一部分了,在Python语言中,虽然有urll ...

  8. Python JS Jquery Json 转换关系

    一.JS对象与JSON格式数据相互转换 目前的项目数据交互几乎都用JQuery,所以处理流程是: 前端页面数据->JS对象->jQuery提交->python处理,另外一种就是倒过来 ...

  9. swift 同步 网络请求_IOS开发中异步网络请求上实现同步逻辑

    IOS开发中异步网络请求上实现同步逻辑 前提: 可能遇到一些问题,比如上传多个数据,需要等多个数据上传成功后做一定的处理,而且一个个上传,万一哪个上传失败了,后面就不需要上传了,直接报错. 之前ASI ...

最新文章

  1. python 函数 默认参数
  2. Android Studio androidx 包冲突解决方法
  3. python 搭建ftp服务器
  4. 【数学和算法】如何理解特征值为复数的情况
  5. 使用存储过程及触发器案例
  6. linux之netstat使用--10个常用的命令
  7. js保存当前html,JavaScript保存当前页面
  8. 计算机科学申请文书,美国留学:看牛人怎么写申请计算机CS专业的文书
  9. Linux查询命令man手册各章节解释
  10. [转]setTimeout() 函数未定义错误
  11. Framework4.0 IIS7下urlrewriter设置问题
  12. WiFi无法连接?解决macOS Big Sur / Mojave / Catalina上的Wi-Fi问题
  13. Oracle 临时表、数据闪回、系统常用表、及常用操作
  14. day16 Python 类的继承关系
  15. 游戏测试面试总结(网易雷火、飞鱼科技、冰川网络、完美世界、搜狐畅游)
  16. 公务员 or 996 ?
  17. Android 屏幕旋转流程分析
  18. G_分包具体详情及处理
  19. 启动solidworks时显示VBE6EXT.OLB不能被加载
  20. webstorm 移动到末尾并且换行快捷键

热门文章

  1. ROM、RAM、SRAM、DRAM、Flash、SDRAM区别
  2. 29.4米/天!安百拓智能岩心钻机在川藏铁路创造深孔钻进新纪录
  3. 生成随机数(高斯分布)
  4. 全球名校AI课程库(41)| WUSTL · 深度神经网络应用全案例实操系列课程『Applications of Deep Neural Networks』
  5. 携程 雇主_4标志您的雇主将破坏您的职业
  6. 回调?是什么,简单讲解一下
  7. ZZULIOJ1027
  8. 【面经】230331模拟面试记录
  9. Thread类的run方法和start方法
  10. scala slick基本使用教程