Python如何爬取实时变化的WebSocket数据的方法


Posted in Python onMarch 09, 2019

一、前言

作为一名爬虫工程师,在工作中常常会遇到爬取实时数据的需求,比如体育赛事实时数据、股市实时数据或币圈实时变化的数据。如下图:

Python如何爬取实时变化的WebSocket数据的方法

Python如何爬取实时变化的WebSocket数据的方法

Python如何爬取实时变化的WebSocket数据的方法

Web 领域中,用于实现数据'实时'更新的手段有轮询和 WebSocket 这两种。轮询指的是客户端按照一定时间间隔(如 1 秒)访问服务端接口,从而达到 '实时' 的效果,虽然看起来数据像是实时更新的,但实际上它有一定的时间间隔,并不是真正的实时更新。轮询通常采用 拉 模式,由客户端主动从服务端拉取数据。

WebSocket 采用的是 推 模式,由服务端主动将数据推送给客户端,这种方式是真正的实时更新。

二、什么是 WebSocket

WebSocket是一种在单个TCP连接上进行全双工通信的协议。它使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket 优点

  • 较少的控制开销:只需要进行一次握手,携带一次请求头信息即可,后续只传输数据即可,相比 HTTP 每次请求都携带请求头,WebSocket 非常省资源。
  • 更强的实时性:由于服务器可以主动推送消息,这使得延迟变得可以忽略不计,相比 HTTP 轮询的时间间隔,WebSocket 可以在相同的时间内进行多次传输。
  • 二进制支持:WebSocket 支持二进制帧,这意味着传输更节省。
  • ……

爬虫面对 HTTP 和 WebSocket

Python 中的网络请求库非常多,Requests 是最常用的请求库之一,它可以模拟发送网络请求。但是这些请求都是基于 HTTP 协议的。在面对 WebSocket 的时候 Requests 就发挥不料作用了,必须使用能够连接 WebSocket 的库。

三、爬取思路

这里以莱特币官网 http://www.laiteb.com/ 实时数据为例。WebSocket 的握手只发生一次,所以如果需要通过浏览器开发者工具观察网络请求,则需要在打开页面的情况下,打开浏览器开发者工具,定位到 NewWork 选项卡,并输入或刷新当前页面,才能观察到 WebSocket 的握手请求和数据传输情况。这里以 Chrome 浏览器为例:

Python如何爬取实时变化的WebSocket数据的方法

在开发者工具中提供了筛选功能,其中 WS 选项代表只显示 WebSocket 连接的网络请求。

这时候可以看到请求记录列表中有一条名为 realTime 的记录,鼠标左键点击它后,开发者工具会分为左右两栏,右侧列出本条请求记录的详细信息:

Python如何爬取实时变化的WebSocket数据的方法

与 HTTP 请求不同的是,WebSocket 连接地址以 ws 或 wss 开头。连接成功的状态码不是 200,而是 101。

Headers 标签页记录的是 Request 和 Response 信息,而 Frames 标签页中记录的则是双方互传的数据,也是我们需要爬取的数据内容:

Python如何爬取实时变化的WebSocket数据的方法

Frames 图中绿色箭头向上的数据是客户端发送给服务端的数据,橙色箭头向下的数据是服务端推送给客户端的数据。

从数据顺序中可以看到,客户端先发送:

{"action":"subscribe","args":["QuoteBin5m:14"]}

然后服务端才会推送信息(一直推送):

{"group":"QuoteBin5m:14","data":[{"low":"55.42","high":"55.63","open":"55.42","close":"55.59","last_price":"55.59","avg_price":"55.5111587372932781077","volume":"40078","timestamp":1551941701,"rise_fall_rate":"0.0030674846625766871","rise_fall_value":"0.17","base_coin_volume":"400.78","quote_coin_volume":"22247.7621987324"}]}

所以,从发起握手到获得数据的整个流程为:

Python如何爬取实时变化的WebSocket数据的方法

那么,现在问题来了:

  • 握手怎么弄?
  • 连接保持怎么弄?
  • 消息发送和接收怎么弄?
  • 有什么库可以轻松实现吗?

四、aiowebsocket

Python 库中用于连接 WebSocket 的有很多,但是易用、稳定的有 websocket-client(非异步)、websockets(异步)、aiowebsocket(异步)。

可以根据项目需求选择三者之一,今天介绍的是异步 WebSocket 连接客户端 aiowebsocket。其 Github 地址为: https://github.com/asyncins/aiowebsocket

ReadMe中介绍到: AioWebSocket是一个遵循 WebSocket 规范的 异步 WebSocket 客户端,相对于其他库它更轻、更快。

它的安装和其他库一样简单,使用 pip install aiowebsocket 即可。安装好后,我们可以根据 ReadMe 中提供的示例代码来测试:

import asyncio
import logging
from datetime import datetime
from aiowebsocket.converses import AioWebSocket


async def startup(uri):
 async with AioWebSocket(uri) as aws:
  converse = aws.manipulator
  message = b'AioWebSocket - Async WebSocket Client'
  while True:
   await converse.send(message)
   print('{time}-Client send: {message}'
     .format(time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'), message=message))
   mes = await converse.receive()
   print('{time}-Client receive: {rec}'
     .format(time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'), rec=mes))


if __name__ == '__main__':
 remote = 'ws://echo.websocket.org'
 try:
  asyncio.get_event_loop().run_until_complete(startup(remote))
 except KeyboardInterrupt as exc:
  logging.info('Quit.')

运行后的结果输出为:

2019-03-07 15:43:55-Client send: b'AioWebSocket - Async WebSocket Client'
2019-03-07 15:43:55-Client receive: b'AioWebSocket - Async WebSocket Client'
2019-03-07 15:43:55-Client send: b'AioWebSocket - Async WebSocket Client'
2019-03-07 15:43:56-Client receive: b'AioWebSocket - Async WebSocket Client'
2019-03-07 15:43:56-Client send: b'AioWebSocket - Async WebSocket Client'
……

send 表示客户端向服务端发送的消息

recive 表示服务端向客户端推送的消息

五、编码获取数据

回到这一次的爬取需求,目标网站是莱特币官网:

Python如何爬取实时变化的WebSocket数据的方法

从刚才的网络请求记录中,我们得知目标网站的 WebSocket 地址为: wss://api.bbxapp.vip/v1/ifcontract/realTime ,从地址中可以看出目标网站使用的是 wss,也就是 ws 的安全版,它们的关系跟 HTTP/HTTPS 一样。aiowebsocket 会自动处理并识别 ssl,所以我们并不需要作额外的操作,只需要将目标地址赋值给连接 uri 即可:

import asyncio
import logging
from datetime import datetime
from aiowebsocket.converses import AioWebSocket


async def startup(uri):
 async with AioWebSocket(uri) as aws:
  converse = aws.manipulator
  while True:
   mes = await converse.receive()
   print('{time}-Client receive: {rec}'
     .format(time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'), rec=mes))


if __name__ == '__main__':
 remote = 'wss://api.bbxapp.vip/v1/ifcontract/realTime'
 try:
  asyncio.get_event_loop().run_until_complete(startup(remote))
 except KeyboardInterrupt as exc:
  logging.info('Quit.')

运行代码后观察输出,你会发现什么都没有发生。既没有内容输出,也没有断开连接,程序一直在运行,但是什么都没有:

Python如何爬取实时变化的WebSocket数据的方法

这是为什么呢?

是对方不接受我方的请求吗?

还是有什么反爬虫限制呢?

实际上,刚才的流程图可以解释这个问题:

Python如何爬取实时变化的WebSocket数据的方法

整个流程中有一步是需要客户端给服务端发送指定的消息,服务端验证后才会不停推送数据。所以,应该在消息读取前、握手连接后加上消息发送的代码:

import asyncio
import logging
from datetime import datetime
from aiowebsocket.converses import AioWebSocket


async def startup(uri):
 async with AioWebSocket(uri) as aws:
  converse = aws.manipulator
  # 客户端给服务端发送消息
  await converse.send('{"action":"subscribe","args":["QuoteBin5m:14"]}')
  while True:
   mes = await converse.receive()
   print('{time}-Client receive: {rec}'
     .format(time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'), rec=mes))


if __name__ == '__main__':
 remote = 'wss://api.bbxapp.vip/v1/ifcontract/realTime'
 try:
  asyncio.get_event_loop().run_until_complete(startup(remote))
 except KeyboardInterrupt as exc:
  logging.info('Quit.')

保存后运行,就会看到数据源源不断的推送过来:

Python如何爬取实时变化的WebSocket数据的方法

到这里,爬虫就能够获取到想要的数据了。

aiowebsocket 做了什么

代码不长,使用的时候只需要将目标网站 WebSocket 地址填入,然后按照流程发送数据即可,那么 aiowebsocket 在这个过程中做了什么呢?

  • 首先,aiowebsocket 根据 WebSocket 地址,向指定的服务端发送握手请求,并校验握手结果。
  • 然后,在确认握手成功后,将数据发送给服务端。
  • 整个过程中为了保持连接不断开,aiowebsocket 会自动与服务端响应 ping pong。
  • 最后,aiowebsocket 读取服务端推送的消息

【奎因:】如果你认为 aiowebsocket 帮助了你,那么请你到 Github https://github.com/asyncins/aiowebsocket 上给一个 Star。如果在使用当中发现问题或者希望给 aiowebsocket 提建议,那么也可以到 Github 上提出。只要你提出建议,就一定能够帮助 aiowebsocket 变的更好,而 aiowebsocket 也能够继续为你服务。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python中pygame的mouse鼠标事件用法实例
Nov 11 Python
python3之微信文章爬虫实例讲解
Jul 12 Python
对python中return和print的一些理解
Aug 18 Python
Python3处理HTTP请求的实例
May 10 Python
Python基于pandas实现json格式转换成dataframe的方法
Jun 22 Python
解决Python selenium get页面很慢时的问题
Jan 30 Python
Django如何自定义model创建数据库索引的顺序
Jun 20 Python
Python 异常处理Ⅳ过程图解
Oct 18 Python
Pyorch之numpy与torch之间相互转换方式
Dec 31 Python
Python sklearn库实现PCA教程(以鸢尾花分类为例)
Feb 24 Python
keras .h5转移动端的.tflite文件实现方式
May 25 Python
pytorch Dataset,DataLoader产生自定义的训练数据案例
Mar 03 Python
浅谈python的深浅拷贝以及fromkeys的用法
Mar 08 #Python
Python高级特性与几种函数的讲解
Mar 08 #Python
Python I/O与进程的详细讲解
Mar 08 #Python
举例讲解Python常用模块
Mar 08 #Python
python re库的正则表达式入门学习教程
Mar 08 #Python
opencv与numpy的图像基本操作
Mar 08 #Python
Python脚本修改阿里云的访问控制列表的方法
Mar 08 #Python
You might like
php Memcache 中实现消息队列
2009/11/24 PHP
ThinkPHP基本的增删查改操作实例教程
2014/08/22 PHP
PHP常用工具类大全附全部代码下载
2015/12/07 PHP
怎么用javascript进行拖拽
2006/07/20 Javascript
java script编程起步(第三课)
2007/01/10 Javascript
Colortip基于jquery的信息提示框插件在IE6下面的显示问题修正方法
2010/12/06 Javascript
JavaScript创建对象的写法
2013/08/29 Javascript
jquery实现滑屏大图定时收缩为小banner图片的广告代码
2015/09/02 Javascript
Java Mybatis框架入门基础教程
2015/09/21 Javascript
Jquery1.9.1源码分析系列(六)延时对象应用之jQuery.ready
2015/11/24 Javascript
轻松学习jQuery插件EasyUI EasyUI表单验证
2015/12/01 Javascript
AngularJS中的过滤器filter用法完全解析
2016/04/22 Javascript
js实现纯前端的图片预览
2016/04/27 Javascript
实用又漂亮的BootstrapValidator表单验证插件
2016/05/30 Javascript
JS实现自动阅读单词(有道单词本添加功能)
2016/11/14 Javascript
jQuery中的100个技巧汇总
2016/12/15 Javascript
12306 刷票脚本及稳固刷票脚本(防挂)
2017/01/04 Javascript
jQuery zTree树插件动态加载实例代码
2017/05/11 jQuery
JavaScript求一组数的最小公倍数和最大公约数常用算法详解【面向对象,回归迭代和循环】
2018/05/07 Javascript
三分钟教你用Node做一个微信哄女友(基友)神器(面向小白)
2019/06/21 Javascript
基于Vue SEO的四种方案(小结)
2019/07/01 Javascript
node.js开发辅助工具nodemon安装与配置详解
2020/02/06 Javascript
python中关于for循环的碎碎念
2017/06/30 Python
python使用matplotlib画柱状图、散点图
2019/03/18 Python
PyTorch中的Variable变量详解
2020/01/07 Python
HTML5 Canvas的常用线条属性值总结
2016/03/17 HTML / CSS
哈萨克斯坦移动和数字技术在线商店:SatelOnline.kz
2020/09/04 全球购物
华为慧通笔试题
2016/04/22 面试题
ORACLE十问
2015/04/20 面试题
园林技术个人的自我评价
2014/01/08 职场文书
《雾凇》教学反思
2014/02/17 职场文书
单位接收证明格式
2015/06/18 职场文书
重阳节主题班会
2015/08/17 职场文书
JS新手入门数组处理的实用方法汇总
2021/04/07 Javascript
Golang 入门 之url 包
2022/05/04 Golang
java实现web实时消息推送的七种方案
2022/07/23 Java/Android