018:websocket实时动态数据爬取

我们先看一下斗鱼直播的弹幕:
在这里插入图片描述
大家可以发现右下角在一直不断变化。

轮询和WebSocket:

Web 领域中,用于实现数据’实时’更新的手段有轮询和 WebSocket 这两种。

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

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

WebSocket:

WebSocket是一种在单个TCP连接上进行全双工通信的协议。

它使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。

在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket 优点:

较少的控制开销:只需要进行一次握手,携带一次请求头信息即可,后续只传输数据即可,相比 HTTP 每次请求都携带请求头,WebSocket 非常省资源。

更强的实时性:由于服务器可以主动推送消息,这使得延迟变得可以忽略不计,相比 HTTP 轮询的时间间隔,WebSocket 可以在相同的时间内进行多次传输。

二进制支持:WebSocket 支持二进制帧,这意味着传输更节省。

案例分析:

先以莱特币官网 http://www.laiteb.com/ 实时数据为例

WebSocket 的握手只发生一次,所以如果需要通过浏览器开发者工具观察网络请求,则需要在打开页面的情况下,打开浏览器开发者工具,定位到 NewWork 选项卡,并输入或刷新当前页面,才能观察到 WebSocket 的握手请求和数据传输情况。这里以 Chrome 浏览器为例:
在这里插入图片描述
在开发者工具中提供了筛选功能,其中 WS 选项代表只显示 WebSocket 连接的网络请求。

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

与 HTTP 请求不同的是,WebSocket 连接地址以 ws 或 wss 开头。连接成功的状态码不是 200,而是 101。
在这里插入图片描述
Headers 标签页记录的是 Request 和 Response 信息,而 Frames 标签页中记录的则是双方互传的数据,也是我们需要爬取的数据内容:在这里插入图片描述
Frames 图中绿色箭头向上的数据是客户端发送给服务端的数据,橙色箭头向下的数据是服务端推送给客户端的数据。

从数据顺序中可以看到,客户端先发送:
{"action":"ping"}
然后服务端才会推送信息(一直推送):
{"action":"subscribe","group":"QuoteBin5m:14","success":true,"request":{"action":"subscribe","args":["QuoteBin5m:14"]}}

所以,从发起握手到获得数据的整个流程为:
在这里插入图片描述

使用aiowebsocket库爬取莱特网数据:

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

可以根据项目需求选择三者之一,这里介绍的是异步 WebSocket 连接客户端 aiowebsocket。

AioWebSocket是一个遵循 WebSocket 规范的 异步 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.')

运行后: (可以看到数据已经不停的过来了)
在这里插入图片描述

我们再可以看下这个网站:(金十数据中心) https://datacenter.jin10.com/price
在这里插入图片描述

他的headers中Request Url是wss://开头的
右边正边疯狂的刷新数据,而使用的协议,正是 websocket

在这里插入图片描述
对于python下如何连接websocket ,网上有很多的文章,我这里就不用细说了,通常我们拿到这样的接口,都会本能去尝试直连看看,在进一步尝试之后,该端口的api会根据真实的请求变化 ,而且进一步的请求的cookie和key都会变化 ,看来直连的方式是行不通了,那没办法,只能走渲染的路了,selenium可以是可以,不过我们要尝试一下新的路线和方法,那就直接上chrome-headless

Headless Chrome指在headless模式下运行谷歌浏览器(以程序模式运行,没有界面),自从这玩意儿出来之后,
phantomjs的作者就宣布不维护了。。。

直接使用docker 来安装chrome headless

docker run -d -p 9222:9222 --cap-add=SYS_ADMIN justinribeiro/chrome-headless

这样我们已经启用了一个chrome headless的服务,那如何使用呢,我们使用websocket 和chrome header less进行交互,不多说了,直接上代码吧

import json
import time
import requests
import websocket
 
request_id = 0
target_url = 'https://datacenter.jin10.com/price'

def get_websocket_connection():
    r = requests.get('http://10.10.2.42:9222/json') #这是开启chrome headless的机器地址
    if r.status_code != 200:
        raise ValueError("can not get the api ,please check if docker is ready")
 
    conn_api = r.json()[0].get('webSocketDebuggerUrl')
 
    return websocket.create_connection(conn_api)
 
def run_command(conn, method, **kwargs):
    global request_id
    request_id += 1
    command = {'method': method,
               'id': request_id,
               'params': kwargs}
    conn.send(json.dumps(command))
    #while True:
    msg = json.loads(conn.recv())
    if msg.get('id') == request_id:
        return msg
 
def get_element():
    conn = get_websocket_connection()
    msg = run_command(conn, 'Page.navigate', url=target_url)
    time.sleep(5)
    js = "var p = document.querySelector('.jin-pricewall_list-item_b').innerText ; p ;"
 
    for _ in range(20):
        time.sleep(1)
        msg = run_command(conn, 'Runtime.evaluate', expression=js)
        print(msg.get('result')['result']['value'])
 
if __name__ == '__main__':
    get_element()

在这里插入图片描述

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

考古学家lx(李玺)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值