Как Python сканирует данные WebSocket, которые меняются в режиме реального времени

Python рептилия

Введение

Как инженер-краулер, я часто сталкиваюсь с необходимостью сканировать данные в реальном времени в своей работе, такие как данные о спортивных событиях в реальном времени, данные о фондовом рынке в реальном времени или изменения в валютном круге в реальном времени. Как показано ниже:

В веб-домене существует два метода реализации обновления данных в режиме реального времени, а именно опрос и WebSocket. Опрос означает, что клиент обращается к интерфейсу сервера через определенный интервал времени (например, 1 секунду), чтобы добиться эффекта «реального времени».Хотя кажется, что данные обновляются в реальном времени, на самом деле они имеют определенный временной интервал и не очень живые обновления. Опрос обычно использует режим извлечения, когда клиент активно извлекает данные с сервера.

WebSocket использует режим push, и сервер активно отправляет данные клиенту, что является реальным обновлением в реальном времени.

2. Что такое веб-сокет

WebSocket — это протокол для полнодуплексной связи по одному TCP-соединению. Это упрощает обмен данными между клиентом и сервером, позволяя серверу активно передавать данные клиенту. В API WebSocket браузеру и серверу нужно выполнить только одно рукопожатие, и между ними может быть создано постоянное соединение, и может выполняться двусторонняя передача данных.

Преимущества веб-сокета

  • Меньше накладных расходов на управление: требуется только одно рукопожатие и требуется только одна информация заголовка запроса, а затем передаются только данные.По сравнению с HTTP, который переносит заголовки запроса в каждом запросе, WebSocket очень экономит ресурсы.
  • Сильнее в режиме реального времени: поскольку сервер может активно отправлять сообщения, что делает задержку незначительной по сравнению с временным интервалом опроса HTTP, WebSocket может выполнять несколько передач за одно и то же время.
  • Двоичная поддержка: WebSocket поддерживает двоичные кадры, что означает меньшую передачу.
  • ...

Сканеры сталкиваются с HTTP и WebSocket

В Python есть много библиотек сетевых запросов, Requests — одна из наиболее часто используемых библиотек запросов, которая может имитировать отправку сетевых запросов. Но все эти запросы основаны на протоколе HTTP. Перед лицом WebSocket запросы играют неожиданную роль, и необходимо использовать библиотеку, которая может подключаться к WebSocket.

Три, ползающие идеи

Вот пример данных в реальном времени с официального сайта Litecoin http://www.laiteb.com/. Рукопожатие WebSocket происходит только один раз, поэтому, если вам нужно наблюдать за сетевым запросом с помощью инструментов разработчика браузера, вам нужно открыть инструменты разработчика браузера, перейти на вкладку NewWork и войти или обновить текущую страницу при открытии страницы. Наблюдайте за запросом рукопожатия WebSocket и передачей данных. Вот пример браузера Chrome:

Фильтрация предоставляется в инструментах разработчика, где параметр WS представляет только сетевые запросы для соединений WebSocket.

В это время вы можете видеть, что в списке записей запросов есть запись с именем realTime. После нажатия левой кнопки мыши инструменты разработчика будут разделены на две колонки, в правой части указана подробная информация об этой записи запроса. :

В отличие от HTTP-запросов, адреса подключения WebSocket начинаются с ws или wss. Код состояния успешного подключения не 200, а 101.

На вкладке «Заголовки» записывается информация о запросе и ответе, а на вкладке «Кадры» записываются данные, передаваемые двумя сторонами, которые также являются данными, которые нам необходимо сканировать:

Данные с зеленой стрелкой вверх на диаграмме кадров — это данные, отправленные клиентом на сервер, а данные с оранжевой стрелкой вниз — это данные, переданные сервером клиенту.

Как видно из последовательности данных, клиент отправляет первым:

{"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 представляет сообщение, отправленное клиентом на сервер

получить представляет сообщение, переданное сервером клиенту

5. Кодирование для получения данных

Возвращаясь к требованиям сканирования на этот раз, целевым веб-сайтом является официальный веб-сайт Litecoin:

Из записи сетевого запроса мы знаем, что адрес 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 автоматически отвечает на пинг-понг с сервером.
  • Наконец, aiowebsocket читает сообщение, отправленное сервером.

[Куинн:] Если вы думаете, что aiowebsocket поможет вам, перейдите на Github.https://github.com/asyncins/aiowebsocketДайте ему Звезду. Если вы обнаружите проблемы в использовании или хотите предложить aiowebsocket, вы также можете отправить их на Github. Пока вы вносите предложения, это определенно поможет aiowebsocket стать лучше, и aiowebsocket может продолжать служить вам.