前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python网络请求-aiohttp

Python网络请求-aiohttp

作者头像
码客说
发布2021-11-29 09:16:52
1.2K0
发布2021-11-29 09:16:52
举报
文章被收录于专栏:码客

前言

在 Python 众多的 HTTP 客户端中,最有名的莫过于requestsaiohttphttpx

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

那么怎么选择呢

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

这里不建议使用多线程来做异步请求,建议使用异步IO的方式。

asyncio的优势:

  • 可以异步请求。
  • 可以普通请求也可以作为WS客户端连接。
  • 可以作为WEB服务器和WEBSOCKET服务器。
  • 性能较好。

安装依赖

代码语言:javascript
复制
pip install aiohttp

客户端

默认超时时间

代码语言:javascript
复制
aiohttp.ClientTimeout(
    total=5*60, 
    connect=None,
    sock_connect=None, 
    sock_read=None
)

GET请求

基本请求

代码语言:javascript
复制
import aiohttp
import asyncio


async def main():
    async with aiohttp.ClientSession() as session:
        params = {'key1': 'value1', 'key2': 'value2'}
        resp = await session.get(
            'https://www.psvmc.cn/login.json',
            params=params
        )
        result = await resp.text()
        result2 = await resp.json()
        print(result)
        print(result2)


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

获取状态码

代码语言:javascript
复制
import aiohttp
import asyncio


async def main():
    async with aiohttp.ClientSession() as session:
        params = {'key1': 'value1', 'key2': 'value2'}
        async with session.get(
            'https://www.psvmc.cn/login.json',
            params=params
        )as resp:
            print(resp.status)
            print(await resp.text())


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

文件下载

代码语言:javascript
复制
import aiohttp
import asyncio


async def main():
    async with aiohttp.ClientSession() as session:
        params = {'key1': 'value1', 'key2': 'value2'}
        async with session.get(
            'https://www.psvmc.cn/search.json',
            params=params
        )as resp:
            filename = "D://search.json"
            chunk_size = 1000
            with open(filename, 'wb') as fd:
                async for chunk in resp.content.iter_chunked(chunk_size):
                    fd.write(chunk)


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

POST请求

基本用法

代码语言:javascript
复制
import aiohttp
import asyncio


async def main():
    async with aiohttp.ClientSession() as session:
        resp = await session.post(
            'https://www.psvmc.cn/login.json',
            json={'keyword': '123'}
        )
        result = await resp.text()
        print(result)


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Form-encoded

代码语言:javascript
复制
import aiohttp
import asyncio


async def main():
    async with aiohttp.ClientSession() as session:
        resp = await session.post(
            'https://www.psvmc.cn/login.json',
            data={'keyword': '123'}
        )
        result = await resp.text()
        print(result)


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Multipart-Encoded File

To upload Multipart-encoded files:

代码语言:javascript
复制
url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}

await session.post(url, data=files)

You can set the filename and content_type explicitly:

代码语言:javascript
复制
url = 'http://httpbin.org/post'
data = FormData()
data.add_field('file',
               open('report.xls', 'rb'),
               filename='report.xls',
               content_type='application/vnd.ms-excel')

await session.post(url, data=data)

Streaming uploads

代码语言:javascript
复制
with open("D://search.json", 'rb') as f:
   await session.post('http://httpbin.org/post', data=f)

其它请求

代码语言:javascript
复制
session.put('http://httpbin.org/put', data=b'data')
session.delete('http://httpbin.org/delete')
session.head('http://httpbin.org/get')
session.options('http://httpbin.org/get')
session.patch('http://httpbin.org/patch', data=b'data')

WebSocket

代码语言:javascript
复制
async with session.ws_connect('http://example.org/ws') as ws:
    async for msg in ws:
        if msg.type == aiohttp.WSMsgType.TEXT:
            if msg.data == 'close cmd':
                await ws.close()
                break
            else:
                await ws.send_str(msg.data + '/answer')
        elif msg.type == aiohttp.WSMsgType.ERROR:
            break

发送消息

代码语言:javascript
复制
await ws.send_str('data')

服务端

WEB服务器

文本

代码语言:javascript
复制
from aiohttp import web

async def hello(request):
    print(request.url)
    return web.Response(text="Hello, world")


app = web.Application()
app.add_routes([web.get('/', hello)])
web.run_app(app, port=8080)

注解

代码语言:javascript
复制
from aiohttp import web

routes = web.RouteTableDef()

@routes.get('/')
async def hello(request):
    print(request.url)
    return web.Response(text="Hello, world")


app = web.Application()
app.add_routes(routes)
web.run_app(app, port=8080)

JSON

代码语言:javascript
复制
from aiohttp import web

routes = web.RouteTableDef()


@routes.get('/')
async def hello(request):
    print(request.url)
    data = {'some': 'data'}
    return web.json_response(data)


app = web.Application()
app.add_routes(routes)
web.run_app(app, port=8080)

From参数获取

代码语言:javascript
复制
async def do_login(request):
    data = await request.post()
    username = data['username']
    password = data['password']

文件上传服务

First, make sure that the HTML <form> element has its enctype attribute set to enctype="multipart/form-data".

代码语言:javascript
复制
<form action="/store/mp3" method="post" accept-charset="utf-8"
      enctype="multipart/form-data">

    <label for="mp3">Mp3</label>
    <input id="mp3" name="mp3" type="file" value=""/>

    <input type="submit" value="submit"/>
</form>

Then, in the request handler you can access the file input field as a FileField instance.

FileField is simply a container for the file as well as some of its metadata:

代码语言:javascript
复制
async def store_mp3_handler(request):
    # WARNING: don't do that if you plan to receive large files!
    data = await request.post()
    mp3 = data['mp3']
    # .filename contains the name of the file in string format.
    filename = mp3.filename
    # .file contains the actual file data that needs to be stored somewhere.
    mp3_file = data['mp3'].file
    content = mp3_file.read()
    return web.Response(
        body=content,
        headers={'CONTENT-DISPOSITION': mp3_file}
    )

WebSockets

服务端

代码语言:javascript
复制
import aiohttp
from aiohttp import web

routes = web.RouteTableDef()


@routes.get('/ws')
async def websocket_handler(request):
    ws = web.WebSocketResponse()
    await ws.prepare(request)
    async for msg in ws:
        if msg.type == aiohttp.WSMsgType.TEXT:
            print(f"receive:{msg.data}")
            if msg.data == 'close':
                await ws.close()
            else:
                await ws.send_str("响应:"+msg.data)
        elif msg.type == aiohttp.WSMsgType.BINARY:
            print("receive:BINARY")
        elif msg.type == aiohttp.WSMsgType.ERROR:
            print('ws connection closed with exception %s' %
                  ws.exception())

    print('websocket connection closed')

    return ws


app = web.Application()
app.add_routes(routes)
web.run_app(app, port=8888)

客户端测试页面

页面

代码语言:javascript
复制
<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>聊天客户端</title>
  </head>

  <body>
    <div>
      <div id="content"></div>
      <input type="text" style="width: 100%" id="msg" />
      <button type="button" onclick="emit()">发送</button>
    </div>

    <script
      type="text/javascript"
      src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"
    ></script>

    <script type="text/javascript" src="js/index.js"></script>
  </body>
</html>

JS

代码语言:javascript
复制
var socket = new WebSocket("ws://127.0.0.1:8888/ws");

$(function () {
  listen();
})

function encodeScript(data) {
  if (null == data || "" == data) {
    return "";
  }
  return data.replace("<", "&lt;").replace(">", "&gt;");
}

function emit() {
  var text = encodeScript($("#msg").val());
  text = replace_em(text);
  socket.send(text);

  $("#content").append("<kbd style='color: #" + "CECECE" + "; font-size: " + 12 + ";'>" + text + "</kbd><br/>");
  $("#msg").val("");
}

//替换为HTML上的标签
function replace_em(str) {
  str = str.replace(/\</g, '&lt;');
  str = str.replace(/\>/g, '&gt;');
  str = str.replace(/\n/g, '<br/>');
  str = str.replace(/\[em_([0-9]*)\]/g, '<img src="arclist/$1.gif" border="0" />');
  return str;
};

function listen() {
  socket.onopen = function () {
    $("#content").append("<kbd>连接成功! 时间(s):" + parseInt(new Date().getTime() / 1000) + "</kbd></br>");
    heartCheck();
  };
  socket.onmessage = function (evt) {
    $("#content").append(evt.data + "</br>");
  };
  socket.onclose = function (evt) {
    $("#content").append("<kbd>" + "连接关闭! 时间(s):" + parseInt(new Date().getTime() / 1000) + "</kbd></br>");
  }
  socket.onerror = function (evt) {
    $("#content").append("<kbd>" + "ERROR!" + "</kbd></br>");
  }
}

//心跳包
function heartCheck() {
  setInterval(function () {
    if (socket) {
      let buffer = new ArrayBuffer(2); // 初始化14个Byte的二进制数据缓冲区
      let dataView = new DataView(buffer);
      dataView.setInt16(0, 1);
      socket.send(dataView);
      console.info("发送心跳", " 时间(s):" + parseInt(new Date().getTime() / 1000));
    }
  }, 30000);
}

document.onkeydown = function (event) {
  var e = event || window.event || arguments.callee.caller.arguments[0];
  if (e && e.keyCode == 13) { // enter 键
    emit();
  }
};
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-11-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 安装依赖
  • 客户端
    • 默认超时时间
      • GET请求
        • 基本请求
        • 获取状态码
        • 文件下载
      • POST请求
        • 基本用法
        • Form-encoded
        • Multipart-Encoded File
        • Streaming uploads
      • 其它请求
        • WebSocket
        • 服务端
          • WEB服务器
            • 文本
            • 注解
            • JSON
            • From参数获取
            • 文件上传服务
          • WebSockets
            • 服务端
            • 客户端测试页面
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档