Python如何爬取实时变化的WebSocket数据
作者 | 韦世东
来源 | 进击的Coder(公众号id:FightingCode)
一、前言
作为一名爬虫工程师,在工作中常常会遇到爬取实时数据的需求,比如体育赛事实时数据、股市实时数据或币圈实时变化的数据。如下图:
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":"40078","timestamp":1551941701,"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.')
运行后的结果输出为:
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 表示服务端向客户端推送的消息
五、编码获取数据
回到这一次的爬取需求,目标网站是莱特币官网:
从刚才的网络请求记录中,我们得知目标网站的 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
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 读取服务端推送的消息
(本文为 AI科技大本营转载文章,转载请联系作者)
4 月13日-4 月14日,CSDN 将在北京主办“Python 开发者日( 2019 )”,汇聚十余位来自阿里巴巴、IBM、英伟达等国内外一线科技公司的Python技术专家,还有数百位来自各行业领域的Python开发者。目前购票通道已开启,早鸟票限量发售中,3 月15日之前可享受优惠价 299 元(售完即止)。
推荐阅读:
让数百万台手机训练同一个模型?Google把这套框架开源了
她们,在”图形“科研的征途里洒下坚定信仰
2018中国开发者真实现状:40岁不做开发,算法工程师最稀缺!
人生苦短,Python之父要解开这个困惑
从起源、变体到评价指标,一文解读NLP的注意力机制
女子偷师男子学校,变身区块链开发工程师,却说: “这次女人不会再缺席了!”
云漫圈 | 如何给女朋友解释什么是HTTP
从程序媛到全球研发副总裁,技术女神进击史!
杨超越杯编程大赛上热搜:不懂技术真不敢追星女子偷师男子学校,变身区块链开发工程师,却说: “这次女人不会再缺席了!”
❤点击“阅读原文”,查看历史精彩文章。
相关文章:
【Qt】Qt样式表总结(四):CSS盒子模型
官网:http://doc.qt.io/qt-5/stylesheet-customizing.html#box-model 【Qt】Qt样式表总结(一):选择器 【Qt】Qt样式表总结(二):冲突和命名空间 【Qt】Qt样式表总结(三):QObject 属性 盒子模型 先来张图片,引自Qt官网 使用样式表时, 每个小部件都被视为具有四个同…
1.试述大数据对思维方式的重要影响。 2.详细阐述大数据、云计算、物联网之间的区别与联系。 3.简述你对大数据应用与发展的看法,以及你在这次大数据浪潮中想扮演什么角色。...
1.大数称巨量资料,指的是需要新处理模式才能具有更强的决策力、洞察力和流程优化能力的海量、高增长率和多样化的信息资产。所以利用大数据的人们思维更加的敏锐,也会对人们的思维方式产生扩大化,通过大量的数据进行分析,从而形成…
有关GetPrivateProfileString的使用方法
函数返回值为string的长度(long型),而从ini文件获得的字符串则保留在目的缓冲器中 DWORD GetPrivateProfileString( LPCTSTR lpAppName, //配置文件的section名 LPCTSTR lpKeyName, //配置文件的key名 LPCTSTR lpDefault, LPTSTR lpReturnedString, DWORD nSize, LPC…
【Qt】QDebug和log4cplus的联合使用
问题描述 项目开始时,只使用QDebug将调试信息打印到终端上。后期添加了日志管理系统,比如log4cplus。如何在不修改打印语句,比如还使用qDebug,就能将日志打印到文件中。 解决方法 使用qInstallMessageHandler将调试消息重定向功…
75道常见AI面试题,看看你的知识盲点在哪?(附解析)
整理 | AI科技大本营出品 | AI科技大本营(公众号id:rgznai100)【导语】正值求职、跳槽季,无论你是换工作还是找实习,没有真本事都是万万不行的,可是如何高效率复习呢?之前我们给大家推荐了一份 …
Flex画流程图
<?xml version"1.0" encoding"utf-8"?><mx:Application xmlns:mx"http://www.adobe.com/2006/mxml" layout"absolute" creationComplete"initApp()"> <mx:Canvas id"paper" x"30" y&q…
【Qt】Qt信号与槽使用不当,使程序崩溃
问题描述 跨线程使用Qt信号和槽,信号发送时间间隔小于槽函数处理时间时,造成程序崩溃。 原因分析 跨线程使用Qt信号和槽时,connect默认是QueuedConnection,队列连接方式。 信号传递给槽函数的参数,分配内存后放入队…
70亿美金!英伟达欲竞购这家以色列芯片公司!
整理 | 琥珀出品 | AI科技大本营(公众号id:rgznai100)近日,据国外财经媒体 Calcalist 报道,英伟达已给出报价,竞购以色列芯片设计公司迈络思(MellanoxTechnologies)。实际上…
Mysql安全配置
zhangsan 2014/06/14 11:550x01 前言很多文章中会说,数据库的权限按最小权限为原则,这句话本身没有错,但是却是一句空话。因为最小权限,这个东西太抽象,很多时候你并弄不清楚具体他需要哪些权限。 现在很多mysql用着r…
【C++】Google C++编码规范(一):作用域
1、文件作用域: 在.cpp文件中,C使用匿名名字空间来表示文件作用域,C使用static来表示; 2、局部变量 局部变量在声明的同时,进行显示初始化;比起隐式初始化再赋值要高效; 局部变量的作用域要尽…
华为 | 人生苦短,码短情长,有场大Party等你来Pick!
上学时,书上说C语言是上帝的语言。我同屋的兄弟不服,他说PHP才是最好的语言。毕业之后,我们Team的老大却坚信:Life is short,只用Python……现在,作为一个真正的开发者,我发现用什么语言一点点都…
设置进程优先级
//取得本进程id HANDLE hProcess GetCurrentProcess(); //设置本进程的优先级 int stat SetPriorityClass(hProcess, NORMAL_PRIORITY_CLASS);
Deep Reading | 从0到1再读注意力机制,此文必收藏!
译者 | forencegan编辑 | 琥珀出品 | AI科技大本营(ID: rgznai100)【AI科技大本营导语】注意力机制(Attention)已经成为深度学习必学内容之一,无论是计算机视觉还是自然语言处理都可以看到各种各样注意力机制的方法。之…
【C++】Google C++编码规范(二):类
1、构造函数 不要在构造函数中进行复杂的初始化 (尤其是那些有可能失败或者需要调用虚函数的初始化). 构造函数不得调用虚函数, 或尝试报告一个非致命错误. 如果对象需要进行有意义的 初始化, 考虑使用明确的 Init() 方法或使用工厂模式. 2、初始化 第一种方法:构…
Flask与Django对比
Flask与Django对比 Django vs FlaskFlask框架之间的差别 Django功能大而全,Flask只包含基本的配置 Django的一站式解决的思路,能让开发者不用在开发之前就在选择应用的基础设施上花费大量时间。Django有模板,表单,路由,…
windows下apache配置虚拟主机的两个方法
windows下apache配置虚拟主机方法一: 对httpd.conf进行设置:1.注释以下三行#ServerAdmin#ServerName#DocumentRoot2.去掉mod_proxy.so和mod_proxy_ajp.so的注释3.#Virtual hosts#Include conf/extra/httpd-vhosts.conf ࿰…
大疆、港科大联手!双目3D目标检测实验效果大放送 | CVPR 2019
作者 | heryms责编 | JaneCVPR 2019的文章出来了,今天聊聊双目的 3D object detection。这是一篇来自 DJI (大疆)与港科大合作的文章《Stereo R-CNN based 3D Object Detection for Autonomous Driving》,作者分别是 Peiliang Li&…
【C++】Google C++编码规范(三):智能指针
【C】Google C编码规范(一):作用域 【C】Google C编码规范(二):类 std::unique_ptr std::unique_ptr是C11标准里新推出的智能指针,用来表示动态分配出的对象的「独一无二」所有权;…
Django restful-framework初步学习
urls.py from django.conf.urls import include, url from django.contrib import admin from rest_framework import routers # 导入api路由 from app01 import apirouter routers.DefaultRouter() # 获取api路由对象 router.register(rusers, api.UserViewSet) # 注册路由到…
植树节,程序员要爬哪些“树”?
作者 | 程序猿小吴、进击的Hello_World转载自五分钟学算法(ID: CXYxiaowu)公历 3 月 12 日是一年一度的植树节。旨在宣传保护森林,并动员群众参加植树造林活动。说到树,程序猿们肯定不陌生,趁着这个植树节,…
把JS 脚本嵌入CS运行
下面这段视频,是让您知道怎样把播放器的javascript放入C#类别中。在调用这个类别时,只传入相关的参数,即可运行。一时类别封装了,在前台xxx.aspx或xxx.aspx.cs看不到播放器的代码。 另一个就是在CS内怎样运行Javascript脚本。此工…
【C++】Google C++编码规范(四):其他C++
引用参数 所有按引用传递的参数必须加上const; 这在Google Code上是一个硬性约定:输入参数是值参或const的引用参数,输出参数为指针,输入参数可以是const指针,但决不能是非const的引用参数,除非用于交换,比…
使用Ceph集群作为Kubernetes的动态分配持久化存储
2019独角兽企业重金招聘Python工程师标准>>> 使用Docker快速部署Ceph集群 , 然后使用这个Ceph集群作为Kubernetes的动态分配持久化存储。 Kubernetes集群要使用Ceph集群需要在每个Kubernetes节点上安装ceph-common 1. 为kubernetes创建一个存储池 1 2 #…
Cosmos的基石:IL2CPU编译器--.net/C#开源操作系统学习系列三
本文的代码包以cosmos-12304.zip为例(从这个包开始,COSMOS的内核算是有了个基本的雏形,就像是一颗大树在出芽前会先长出庞大的根系,现在就要破土长出第一颗芽了) IL2CPU之于COSMOS就相当与GCC之于LINUX,查看…
【面试 多线程】【第九篇】多线程的问题
1.多线程有什么用 发挥多核CPU优势,防止阻塞,更快的处理数据 2.多线程的实现方式有哪几种,分别的特点优势是什么样的 1》继承Thread类,重写run方法,start启动多线程 2》实现Runnable接口,重写run方法&…
那个大战AlphaGo的柯洁,将免试入读清华大学工商管理专业
日前,柯洁将免试入读清华大学的消息经媒体曝光了出来。《2019 年优秀运动员免试入学推荐名单》3 月 10 日开始公示,围棋世界冠军柯洁的名字出现在名单上,其中表明他将就读清华大学工商管理类专业。据了解,柯洁预计今年下半年入学清…
【Qt】设置背景
1、使用样式表qss设置背景 QDialog 设置背景图片: dlg->setStyleSheet("QDialog{border-image: url(://test.png);}"); 设置背景颜色: dlg->setStyleSheet("QDialog{background-color: red;}"); QWidget 设置背景图片: wgt->setStyleSheet…
基于Hadoop的MapReduce框架研究报告
http://www.doc88.com/p-19830708273.html
【Qt】设置窗口透明度
1、使用setWindowOpacity设置透明度 setWindowOpacity(0.5); 设置完成会使窗体、标题栏、子控件都透明 2、使用样式表qss设置窗体透明 dlg->setStyleSheet("QDialog{background-color: rgba(255, 0, 0, 0.5);}");wgt->setStyleSheet("QWidget{backgrou…
7行Python代码,搭建可以识花的机器学习App|视频教程
你想学Python,却不知如何着手,那你需要一种更加有趣的学习方式。Siraj Raval是一位人工智能领域的编程高手,毕业于哥伦比亚大学,曾任职于 Twilio 和 Meetup,他通过制作教程类短视频的方式在Youtube上积累了大量的粉丝&…