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:
文章写一半,就接到电话,有位大佬给推荐下工作了。
再次感谢这位大佬。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,454评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,553评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,921评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,648评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,770评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,950评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,090评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,817评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,275评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,592评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,724评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,409评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,052评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,815评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,043评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,503评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,627评论 2 350

推荐阅读更多精彩内容