一、前言

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

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 浏览器为例:

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

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

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

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

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":"","timestamp":,"rise_fall_rate":"0.0030674846625766871","rise_fall_value":"0.17","base_coin_volume":"400.78","quote_coin_volume":"22247.7621987324"}]}
   

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

那么,现在问题来了:

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

四、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.')

运行后的结果输出为:

 -- ::-Client send: b'AioWebSocket - Async WebSocket Client'
-- ::-Client receive: b'AioWebSocket - Async WebSocket Client'
-- ::-Client send: b'AioWebSocket - Async WebSocket Client'
-- ::-Client receive: b'AioWebSocket - Async WebSocket Client'
-- ::-Client send: b'AioWebSocket - Async WebSocket Client'
……

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

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

五、编码获取数据

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

从刚才的网络请求记录中,我们得知目标网站的 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.')

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

这是为什么呢?

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

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

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

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

 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.')

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

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

aiowebsocket 做了什么

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

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

作者:夜幕奎因

HDC.Cloud 华为开发者大会2020 即将于2020年2月11日-12日在深圳举办,是一线开发者学习实践鲲鹏通用计算、昇腾AI计算、数据库、区块链、云原生、5G等ICT开放能力的最佳舞台。

欢迎报名参会

Python如何爬取实时变化的WebSocket数据【华为云技术分享】的更多相关文章

  1. Python如何爬取实时变化的WebSocket数据

    一.前言 作为一名爬虫工程师,在工作中常常会遇到爬取实时数据的需求,比如体育赛事实时数据.股市实时数据或币圈实时变化的数据.如下图: Web 领域中,用于实现数据'实时'更新的手段有轮询和 WebSo ...

  2. 爬取实时变化的 WebSocket 数据(转载)

    本文转自:https://mp.weixin.qq.com/s/fuS3uDvAWOQBQNetLqzO-g 一.前言 作为一名爬虫工程师,在工作中常常会遇到爬取实时数据的需求,比如体育赛事实时数据. ...

  3. Python爬虫实战之爬取糗事百科段子【华为云技术分享】

    首先,糗事百科大家都听说过吧?糗友们发的搞笑的段子一抓一大把,这次我们尝试一下用爬虫把他们抓取下来. 友情提示 糗事百科在前一段时间进行了改版,导致之前的代码没法用了,会导致无法输出和CPU占用过高的 ...

  4. 用python读取word文件里的表格信息【华为云技术分享】

    在企查查查询企业信息的时候,得到了一些word文件,里面有些控股企业的数据放在表格里,需要我们将其提取出来. word文件看起来很复杂,不方便进行结构化.实际上,一个word文档中大概有这么几种类型的 ...

  5. 使用Python为中秋节绘制一块美味的月饼【华为云技术分享】

    每逢佳节… 对于在外的游子,每逢佳节倍思亲.而对于996ICU的苦逼程序猿们,最期待的莫过于各种节假日能把自己丢在床上好好休息一下了.这几天各公司都陆续开始发中秋礼品了.朋友圈各种秀高颜值的月饼,所以 ...

  6. python爬虫爬取get请求的页面数据代码样例

    废话不多说,上代码 #!/usr/bin/env python # -*- coding:utf-8 -*- # 导包 import urllib.request import urllib.pars ...

  7. Python scrapy爬取带验证码的列表数据

    首先所需要的环境:(我用的是Python2的,可以选择python3,具体遇到的问题自行解决,目前我这边几百万的数据量爬取) 环境: Python 2.7.10 Scrapy Scrapy 1.5.0 ...

  8. Python爬虫爬取异步加载的数据

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:努力努力再努力 爬取qq音乐歌手数据接口数据 https://y.qq ...

  9. 【Python3网络爬虫开发实战】6.4-分析Ajax爬取今日头条街拍美图【华为云技术分享】

    [摘要] 本节中,我们以今日头条为例来尝试通过分析Ajax请求来抓取网页数据的方法.这次要抓取的目标是今日头条的街拍美图,抓取完成之后,将每组图片分文件夹下载到本地并保存下来. 1. 准备工作 在本节 ...

随机推荐

  1. 「刷题」xor

    说实话这道题没有A掉,不过所有的思路都是我自己想的,我觉得这个思路真的很棒很棒很棒的. 首先这个题的题面描述告诉我这种运算有封闭性,满足结合律和交换率,那么其实这个东西是个群运算了,而且这个群有单位元 ...

  2. 「卡常」我T死了我好桑心,我不会卡常。

    我死了我死了我死了 puts(“Lrefrain==T”)

  3. 差异---虐爆了yxs的 后缀数组裸题 板子题 单调栈的简单应用 字符串的基础理解考察题

    先玩柿子,发现可以拆开,前半部分可以瞬间求出,于是只求后半部分 然后抄板子就好了,完结撒花! 下边是个人口胡,因为已经被虐爆头脑不清醒了 定义:LCP(a,b)为排名为a,b两个后缀的最长公共前缀 证 ...

  4. python学习之【第八篇】:Python中的函数基础

    1.前言 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段.函数能提高应用的模块性,和代码的重复利用率. 2.函数的定义 定义函数时需要遵守以下规则: 函数代码块以 def 关键词开头 ...

  5. Java实现不遍历数组求和

    package com.jts.t1; /** * 不遍历数组求和 * 方法省略异常检查 */ public class Demo1 { public static void main(String[ ...

  6. [javascript] 编写一个计算器,实现加减法

    1.代码 <script> function sum(){ //加法 var value1 = document.getElementById("num1").valu ...

  7. DAGScheduler stage 划分算法

    DAGScheduler stage 划分算法 stage划分算法很重要,对于spark开发人员来说,必须对stage划分算法很清晰,知道自己编写的spark Application被划分成了几个jo ...

  8. Zabbix-(五)监控Docker容器与自定义jvm监控项

    Zabbix-(五)监控Docker容器与自定义jvm监控项 一.前言 前文中讲述了Zabbix对服务器硬件方面的监控功能,本文将讲述利用Zabbix监控Docker容器中的Java Web服务,并通 ...

  9. PHP 向数组头部插入数据

    PHP 向数组头部插入数据 函数: array_unshift() 示例: $s = array('a' => 0, 'b' => 3); array_unshift($s, '5'); ...

  10. nyoj 29-求转置矩阵问题 (行,列位置调换)

    29-求转置矩阵问题 内存限制:64MB 时间限制:3000ms Special Judge: No accepted:8 submit:18 题目描述: 求一个三行三列的转置矩阵. 输入描述: 第一 ...