boss直聘聊天機器人

想轉崗到爬蟲工程師,由于面試機會太少,而boss直聘又無法海投,決定做一個boss直聘機器人來幫我找工作。

一、基本需求

  1. 一個爬蟲程序爬取需要的工作信息,存入數據庫。
  2. 聊天機器人每天上午10點向未聊天的boss打招呼。
  3. 如果有boss回復了,聊天機器人向boss發送簡歷(因為boss直聘設定必須雙方說話過后才能發簡歷)。

二、涉及知識點

  1. protobuf
  2. MQTT
  3. websocket

三、聊天機器人實現過程

1. 查看boss直聘網頁版即時通訊實現方式。

查看聊天界面ws下有無建立websocket。


image.png

發現無websocket建立,考慮難道是ajax輪訓不會這么low吧?
但是在與別人聊天時候,下面并無新增ajax請求。
打開抓包工具charles進行抓包查看。


image.png

發現是有ws建立的。改用chrome瀏覽器,
image.png

可以看到是有ws建立。(第一個坑,以后默認使用chrome瀏覽器)。

2.websocket連接建立方式及參數

查看連接建立的調用棧


image.png

將關聯的js都下載到本地,使用webstorm格式化代碼。再使用charles的map local功能將這三個文件代理。這樣boss直聘使用的js就是本地格式化后的代碼了(否則在使用查看調用時,所有代碼都堆在2行,不可讀)。
仔細查看調用堆棧確認是app.js的connection處傳入的連接參數。


image.png

搜索關鍵詞paho-mqtt了解mqtt的功能。所以boss直聘是使用的paho.mqtt這個第三方庫。
在本地使用paho.mqtt第三方庫做個連接的demo。參數通過修改app.js,在建立連接前打印所有的參數獲得。

代碼:

        var hostname = 'ws.zhipin.com', //'192.168.1.2',
            port = 443,
            ssl = true,
            topic = '/chatws';
        client = new Paho.MQTT.Client(hostname, port, topic, "ws-CD090DC8307DE0AC");
        //建立客戶端實例
        var options = {

            password: "Xxxxxx",
            userName: "Xxxxxxxx",
            useSSL: ssl,
            onSuccess: onConnect,
            onFailure: function (e) {
                console.log(e);
            }
        };
        client.connect(options);
        //發送消息  
        message = new Paho.MQTT.Message("hello");  
        message.destinationName = topic;  
        client.send(message);

運行后:


image.png

連接已經可以建立。但是在發送信息后,會報錯并斷開連接。因為數據格式有誤。

3、通信數據格式

websocket連接已經可以建立,接下來就是查看boss直聘的通信數據格式了。
通過charles抓包,取到當我對一個boss發送“你好”時的數據:

3357 0004 6368 6174 0001 0801 1A4B 0A02 0800 1220 0800 121C 3531 6465 3762 6632 6234 3732 6139 3566 3148 525F 3039 5739 4556 6F7E 1801 20EE FBC8 FB92 2D28 B8B7 8EF2 922D 320C 0801 1001 1A06 E4BD A0E5 A5BD 58EE FBC8 FB92 2D

簡單的16進制轉字符串后獲得:

3W?chat???K
?? ??51de7bf2b472a95f1HR_09W9EVo~?? ?????-(????-2?????你好X?????-

這不是一個簡單的json數據,查看數據發送時的調用堆棧


image.png

發現protobuf.js關鍵字,搜索并學習了protobuf。通過打印發送的數據。


image.png

獲得數據:
        [8, 1, 26, 75, 10, 2, 8, 0, 18, 32, 8, 0, 18, 28, 53, 49, 100, 101, 55, 98, 102, 50, 98, 52, 55, 50, 97, 57, 53, 102, 49, 72, 82, 95, 48, 57, 87, 57, 69, 86, 111, 126, 24, 1, 32, 233, 193, 149, 232, 145, 45, 40, 179, 253, 218, 222, 145, 45, 50, 12, 8, 1, 16, 1, 26, 6, 228, 189, 160, 229, 165, 189, 88, 233, 193, 149, 232, 145, 45]

寫一個使用protobuf轉化的demo。

var protobuf = require("protobufjs");

protobuf.load("proto.proto")
    .then(function (root) {
        var Protocol = root.lookupType("TechwolfChatProtocol");
        // var data = Buffer.from([8,2,34,87,8,1,16,182,196,186,9,26,76,10,0,18,0,26,0,34,0,42,18,49,53,53,48,56,49,55,53,57,51,55,49,49,49,52,53,50,53,50,12,53,57,46,49,48,57,46,55,55,46,57,52,56,187,70,66,3,119,101,98,74,2,45,49,82,0,90,0,97,0,0,0,0,0,0,0,0,105,0,0,0,0,0,0,0,0,40,0]);
        var data = Buffer.from([8, 1, 26, 75, 10, 2, 8, 0, 18, 32, 8, 0, 18, 28, 53, 49, 100, 101, 55, 98, 102, 50, 98, 52, 55, 50, 97, 57, 53, 102, 49, 72, 82, 95, 48, 57, 87, 57, 69, 86, 111, 126, 24, 1, 32, 233, 193, 149, 232, 145, 45, 40, 179, 253, 218, 222, 145, 45, 50, 12, 8, 1, 16, 1, 26, 6, 228, 189, 160, 229, 165, 189, 88, 233, 193, 149, 232, 145, 45]);
        // Decode an Uint8Array (browser) or Buffer (node) to a message
        var message = Protocol.decode(data);
        console.log(message);

    });
打印數據為:
TechwolfChatProtocol {
  messages:
   [ TechwolfMessage {
       from: [TechwolfUser],
       to: [TechwolfUser],
       type: 1,
       mid: [Long],
       time: [Long],
       body: [TechwolfMessageBody],
       cmid: [Long] } ],
  messageSync: [],
  messageRead: [],
  type: 1 }

可以解析數據。這樣數據格式就找到了。

4、發送一條消息。

考慮node運行聊天機器人,但是 paho-mqtt.js不支持node方式,僅支持瀏覽器。而python同時支持paho-mqtt和protobuf。
所以使用python來結合完成。
但是在python中建立socket連接時總會報錯:


image.png

給出了報錯,但是沒有具體信息。以為是tls版本太低,最后在mqtt源碼報錯處添加打印,獲得信息

b"bytearray(b'http/1.1 403 forbidden\\r\\n')"
b"bytearray(b'date: wed, 27 feb 2019 10:03:06 gmt\\r\\n')"
b"bytearray(b'transfer-encoding: chunked\\r\\n')"
b"bytearray(b'connection: keep-alive\\r\\n')"

發現是在websocket握手時,http請求直接403了,考慮是否與cookies有關,在源碼找到header設置的地方,添加cookies參數。
運行后可以正常連接。
結合protobuf后,最終代碼:

chat = {
    'type': 1,
    'messages': [
        {
            'from': {'uid': 0},
            'to': {'uid': 0, 'name': 'xxxxxxxxx~'},#name為boss的id
            'type': 1,
            'mid': 1550970085609,
            'time': 1550950252211,
            'body': {'type': 1, 'templateId': 1, 'text': '你好'},
            'cmid': 1550970085609
        }
    ]
}
chat_protocol = protobuf_json.json2pb(TechwolfChatProtocol(), chat)

hostname = 'ws.zhipin.com'
port = 443
clientId = '19833398'
timeout = 60
keepAlive = 100
topic = '/chatws'

# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))

    # Subscribing in on_connect() means that if we lose the connection and
    # reconnect then subscriptions will be renewed.
    client.subscribe(topic)
    client.publish(topic,payload=chat_protocol.SerializeToString(),qos=0)


def on_disconnect(client, userdata, rc):
    if rc != 0:
        print("Unexpected disconnection.")

# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
    print('received message -----'+client)
    protocol = TechwolfChatProtocol()
    protocol.ParseFromString(msg.payload)
    print(protocol)

client = mqtt.Client(client_id="ws-CD090DC8307DE0AC", clean_session=True,
                     transport="websockets")
client.username_pw_set("xxxxxxx", "xxxxx")  # 參數分別boss為用戶生的mqtt賬號密碼。
client.ws_set_options(path=topic)
client.tls_set()
client.on_connect = on_connect
client.on_message = on_message
client.on_disconnect = on_disconnect

client.connect(hostname, port, 60)
client.loop_forever()

運行后,手機端以及可以看到給boss發送了“你好”。

5.總結

接下來只需要結合爬蟲,就可以完成最初的設想啦。
完成這個聊天機器學到了protobuf、mqtt、websocket相關的知識。
中間有卡殼的地方也很多。
但是一定要堅信,web端所有的內容,都是可爬的。完成的所有功能,都是可以模擬的。
ps:
文章寫一半,就接到電話,有位大佬給推薦下工作了。
再次感謝這位大佬。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 網絡編程 1. 概論 建立連接:通過IP或者域名來連接兩臺設備,通過端口號找到對應的通信程序 通信協議:要傳輸的數...
    陵無山閱讀 5,197評論 0 12
  • 點擊查看原文 Web SDK 開發手冊 SDK 概述 網易云信 SDK 為 Web 應用提供一個完善的 IM 系統...
    layjoy閱讀 13,908評論 0 15
  • 有種片,不看沒事,一看上癮。 A級……自然歷史紀錄片。 自然風光、人文科學盡收眼底。 最值得羨慕的,還是其中的英雄...
    Sir電影閱讀 1,703評論 3 31
  • 晚,我與父親行于街道之上,暢聊文學之事。 街道,昏暗,腳步走在砂石之上吱吱做響,寂冷,寒風而過瑟瑟發抖,凄靜,惟獨...
    又人同學閱讀 575評論 0 3
  • 繼續練習鋼筆字第158彈,今天介紹含有“心”字的字——想。 都無色可并,不奈此香何?,幭藳鲈O,金羈落晚過?;佤罒?..
    胡義華閱讀 147評論 0 0