Вконтакте запустил Streaming API, инструмент для получения публичных данных из ВКонтакте по заданным ключевым словам.

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

Давайте напишем бота для телеграмма с оповещением о записях в ВК.

Получаем новости


Для начала нужно создать приложение в ВК и взять из настроек сервисный ключ



Для передачи данных используется протокол WebSocket. Используем библиотеку «websocket».

import websocket

Перед соединением используйте метод streaming.getServerUrl. В качестве результата метод возвращает URL для дальнейших запросов в поле endpoint (string) и ключ доступа в поле key (string).

Получаем урл и ключ

def get_streaming_server_key(token):
    request_url = "https://api.vk.com/method/streaming.getServerUrl?access_token={}&v=5.64".format(token)
    r = requests.get(request_url)
    data = r.json()
    return {"server":data["response"]["endpoint"],"key":data["response"]["key"]}

Слушаем сервер

def listen_stream():
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("wss://{}/stream?key={} ".format(stream["server"], stream["key"]),
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close)
    ws.on_open = on_open
    ws.run_forever()

def on_message(ws, message):
    print(">>>> receive message:", message)

def on_error(ws, error):
    print(">>>> error thead:",error)

def on_close(ws):
    print(">>>> close thead")

def on_open(ws):
    print(">>>> open thead")


stream = get_streaming_server_key(my_servise_token)
listen_stream()

После запуска в консоли должно появиться примерно следующее
— request header — GET /stream?key=e6290ba04916a314c398c331f60224d012fabeb1 HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: streaming.vk.com
Origin: streaming.vk.com
Sec-WebSocket-Key: vG40A5bwbPaVBS+DLBOyog==
Sec-WebSocket-Version: 13
— — response header — HTTP/1.1 101 Switching Protocols
Server: Apache
Date: Thu, 20 Jul 2017 22:06:55 GMT
Connection: upgrade
Upgrade: websocket
Sec-WebSocket-Accept: QRJNqD8K7vWNMcQesYKo64q8MfA=
— >>>> open thead
send: b"\x8a\x80d')\x90"
send: b'\x8a\x80\xfcmp\xe6'
send: b'\x8a\x80s\x9f6{'
send: b'\x8a\x80\xc9\xfa.\xd4'
send: b'\x8a\x80fU<\xed'
send: b'\x8a\x80\xc6Ji\x19'
send: b'\x8a\x80\xd2D\x08!'

Работа с правилами


Добавление

Метод HTTP POST
Content Type application/json
URL запроса https://{endpoint}/rules?key={key}


def set_my_rules(value):
    rule_params = {"rule":{"value":value,"tag":'tag_'+str(random.randint(11111, 99999))}}
    headers = {'content-type': 'application/json'}
    r = requests.post("https://{}/rules?key={}".format(stream["server"], stream["key"]), data=json.dumps(rule_params), headers=headers)
    data = r.json()

    return data['code'] == 200

Получение

Метод HTTP GET
URL запроса https://{endpoint}/rules?key={key}



def get_my_rules():
    r = requests.get("https://{}/rules?key={}".format(stream["server"], stream["key"]))
    data = r.json()
    if data['code'] != 200:
        return False

    return data['rules']

Удаление

Метод HTTP DELETE
Content Type application/json
URL запроса https://{endpoint}/rules?key={key}


def del_my_rules(tag):
    headers = {'content-type': 'application/json'}
    rule_params = {"tag":tag}
    r = requests.delete("https://{}/rules?key={}".format(stream["server"], stream["key"]), data=json.dumps(rule_params), headers=headers)
    data = r.json()

    return data['code'] == 200

Пробуем получить новости

stream = get_streaming_server_key(my_servise_token)
set_my_rules('кот')
listen_stream()

В консоли должно появиться примерно следующее
send: b'\x8a\x80\xc9\xfa.\xd4'
send: b'\x8a\x80fU<\xed'
send: b'\x8a\x80\xc6Ji\x19'
send: b'\x8a\x80\xd2D\x08!'
{«code»:100,«event»:{«event_type»:«post»,«event_id»:{«post_owner_id»:-35708825,«post_id»:4560},«event_url»:«vk.com/wall-35708825_4560»,«text»:«vk.com/small.dolly
Пропал кот. В г.Электросталь на улице Загонова в районе 16 школы. Молодой мальчик, 2 года кастрирован. Обычного серого окраса с полосками. На подбородке и грудке белое пятно. Кот крупный около 7 кг. Пропал 27 июня. Если кто-то его видел пожалуйста сообщите 89250506596 или 89251527466. Мы его очень любим и хотим чтобы он вернулся домой»,«creation_time»:1498942995,«tags»:[«test_cats»],«author»:{«id»:-35708825}}} — наша новсть

Телеграм


Используем библиотеку «telebot».


import telebot

TELEGRAM_API_TOKEN = '3277332...' # токен выдаваемый при создании бота
bot = telebot.TeleBot(TELEGRAM_API_TOKEN)

Старт бота


def _send(msg):
    markup = types.ReplyKeyboardMarkup(row_width=1, resize_keyboard=True)
    markup.add('Мои интересы', 'Очистить список интересов', 'Добавить')

    msg = bot.send_message(chatID, msg, reply_markup=markup) # шлем текст с вариантами ответа
    bot.register_next_step_handler(msg, process_step) # устанавливаем обработчик для наших ответов

# обработчик для 'help', 'start'
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
    global chatID
    chatID = message.chat.id
    hello_test = 'Привет, %s! Я бот использующий VK Streaming API!' % message.from_user.first_name
    _send(hello_test)

# обработчик для наших ответов
def process_step(message):
    if message.text == 'Мои интересы':
        _send(get_rules_list())

    if message.text == 'Очистить список интересов':
        _send(clear_rules_list())

    if message.text == 'Добавить':
        msg = bot.send_message(chatID, "Что добавить?")
        bot.register_next_step_handler(msg, add_rule_handler)

Добавить




if message.text == 'Добавить':
        msg = bot.send_message(chatID, "Что добавить?")
        bot.register_next_step_handler(msg, add_rule_handler)
.....
def add_rule_handler(message):
    new_rule = set_my_rules(message.text)
    if new_rule:
        _send("Successful")
    else:
        logging.debug("Error add rules")
        _send("Error")

Мои интересы

def get_rules_list():
    rules = get_my_rules()
    if rules:
        return "\n".join([str(rule['value']) for rule in rules])
    else:
        logging.debug("Error get rules list")
        return 'Error'

Очистить список интересов

def clear_rules_list():
    rules = get_my_rules()
    if rules:
        for rule in rules:
            del_my_rules(rule['tag'])

        return "Successful"

    else:
        logging.debug("Error clear rules list")
        return 'Error'

Отправка новостей

def on_message(ws, message):
    print(">>>> receive message:", message)
    message = json.loads(message)

    if not message['code']:
        return

    if not message['event']['event_type'] or message['event']['event_type'] != 'post':
        return # выходим, если запись не отдельный пост

    post = message['event']['event_type'] +"\n"+message['event']['text'].replace("<br>", "\n") +"\n\n"+ message['event']['event_url']

    _send_post(post)

Запускаем все вместе


Для корректной работы сокеты нужно запустить в отдельном треде.

def listen_stream():
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("wss://{}/stream?key={} ".format(stream["server"], stream["key"]),
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close)
    ws.on_open = on_open
    #ws.run_forever()

    wst = threading.Thread(target=ws.run_forever)
    wst.daemon = True
    wst.start()

Запускаем

if __name__ == '__main__':
    try:
        chatID = 0
        stream = get_streaming_server_key(my_servise_token)
        listen_stream()

        bot.polling(none_stop=True)
    except Exception as e:
        logging.exception("error start")





На этом все, спасибо.

> Полный код скрипта
> Streaming API docs
> Telegram API docs
> Python websocket-client
> Python TelegramBotAPI
Поделиться с друзьями
-->

Комментарии (6)


  1. MonkAlex
    23.07.2017 23:17
    +6

    Это не ваш личный блог, будьте добры, именуйте статьи адекватно.


    1. WSN3
      24.07.2017 06:35
      +3

      Поправил


      1. AC130
        24.07.2017 15:46

        На всякий случай: старое название было «Будь в курсе»?
        ВК упорно предлагает мне именно эту строку при попытке кинуть ссылку на стену.


        1. WSN3
          24.07.2017 16:17

          Да


  1. PeterSamokhin
    24.07.2017 07:04
    +3

    Сперва, когда я увидел, что бесплатно оповещения приходят только об 1% событий, я подумал, что это, в принципе, не так уж и страшно. Но на деле оказалось, что практической пользы от Streaming API нет вообще. На конкретном примере: я с разных аккаунтов, с парочки тестовых сообществ, написал несколько десятков постов и комментариев с нужными мне ключевыми словами. И с хештегами пробовал. О скольких упоминаниях нужных мне ключевых слов пришли уведомления? Нетрудно догадаться, что я не получил ни единого уведомления.

    Итог: какая может быть практическая польза от этого? Никакой. Проще использовать поиск по новостям. 25000 запросов к этому методу в сутки (с одним ключом доступа) достаточно для 1 запроса каждые 3 секунды. Естественно, это ещё порежут, но и юзать Streaming API, фактически, смысла нет. Даже если указать самую упоминаемую тему, уведомления будут приходить крайне редко. Максимум, для чего это может пригодиться — фановая рассылочка о некоторых интересных темах, которую можно куда-то отправлять, например в ТГ, как автор и показал. И то, чтобы уведомления приходили более-менее часто, нужно выбрать самые упоминаемые темы, и их по максимуму.


  1. denistu10
    24.07.2017 18:35

    Отлично, как раз только вчера интересовался как же блин пользоваться этим нововведением в виде Streaming API. А тут такой подгон да еще и на Python