寫一個自動回覆的聊天機器人

要寫一個自動聊天的機器人,底層一定離不開socket, TCP 是一個 穩定、可靠的傳輸協議,經常使用於對數據進行準確無誤的傳輸,socket裏面有對它的封裝。


TCP 的概念

TCP 的英文全拼(Transmission Control Protocol)簡稱傳輸控制協議,它是一種面向鏈接的、可靠的、基於字節流的傳輸層通訊協議python

socket 的概念

socket (簡稱 套接字) 是進程之間通訊一個工具進程之間想要進行網絡通訊須要基於這個 socket。它負責進程之間的網絡數據傳輸,比如數據的搬運工。不誇張的說,只要跟網絡相關的應用程序或者軟件都使用到了 socketweb

客戶端

下面是一個開發TCP應用程序客戶端的通常流程,後面見具體代碼:json

  1. 建立客戶端套接字對象api

  2. 和服務端套接字創建鏈接瀏覽器

  3. 發送數據bash

  4. 接收數據服務器

  5. 關閉客戶端套接字網絡

from socket import *
class Cilent_Socket:
    def __init__(self):
        self.tcp_client_socket = socket(AF_INET, SOCK_STREAM) # AF_INET指ipv4地址,SOCK_STREAM指TCP協議
        self.tcp_client_socket.connect(('192.168.137.1',8989))  #鏈接服務端,指定服務器ip和端口def run(self):
        while True:
            # 用戶輸入數據
            send_data = input("我:")
            if len(send_data)==0:
                 print('已斷開鏈接!')
                 break
            if send_data == "quit" or send_data == "exit" or send_data =='Bye'or send_data =='bye':
                self.tcp_client_socket.send(send_data.encode("gbk"))
                recv_data = self.tcp_client_socket.recv(4096).decode('gbk')
                print('小美:', recv_data)
                self.tcp_client_socket.close()
                break
            self.tcp_client_socket.send(send_data.encode("gbk"))​            # 接收對方發送過來的數據,最大接收4096個字節
            recv_data = self.tcp_client_socket.recv(4096).decode('gbk')
            print('小美:', recv_data)
        # 關閉套接字
        self.tcp_client_socket.close()
def main():
    client = Cilent_Socket()
    client.run()
​if __name__ == '__main__':
    main()複製代碼

上面代碼中的__init__方法初始化了一個客戶端套接字,並與服務器創建一個長鏈接,run()方法中用於和後臺機器人發送消息和接收機器人給你返回的消息。多線程

服務端

建立一個服務端程序的基本步驟是:app

  1. 建立服務端端套接字對象

  2. 綁定端口號

  3. 設置監聽

  4. 等待接受客戶端的鏈接請求

  5. 接收數據

  6. 發送數據

  7. 關閉套接字

要建立一個能自動回覆的機器人,只要循環接收用戶輸入的信息,將其輸入的關鍵詞進行判斷,能夠後臺預先給定它對應的關鍵詞對應給用戶回覆的信息便可,或者調用已知已經作好的API接口。下面兩種狀況會分別進行介紹。

1.自定義消息關鍵詞回覆

from socket import *
import time
import random​
class Server_Socket:
    def __init__(self):
        tcp_server_socket = socket(AF_INET, SOCK_STREAM)
        tcp_server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, True) #設置端口複用
        tcp_server_socket.bind(('', 8989))  # 綁定服務器端口
        tcp_server_socket.listen(128)
        self.tcp_server_socket = tcp_server_socket
        self.chat_list = ['今每天氣真好,和朋友出去玩一下吧','今天你學習了嗎','又不知道吃什麼','藍瘦香菇','好嗨喲','去看電影吧','去吃好吃的'] # 定義初始化消息回覆列表​
    def start(self):​
        client_socket, client_addr = self.tcp_server_socket.accept()
        while True:
            # 接收對方發送過來的數據
            recv_data = client_socket.recv(4096).decode('gbk')  # 接收4096個字節if len(recv_data) == 0:
                print("程序結束")
                break            # 下面一串是對用戶的輸入邏輯進行判斷
            elif recv_data =="quit" or recv_data =="exit" or recv_data =='Bye' or recv_data =='bye' or recv_data =='再見':
                client_socket.send('再見'.encode('gbk'))
                break
            elif "你好" in recv_data or "hello" in recv_data:
                client_socket.send("你好".encode('gbk'))
            elif "sb" in recv_data or "SB" in recv_data or "傻" in recv_data or "二貨" in recv_data :
                client_socket.send("你才傻,你全家都傻!!!".encode('gbk'))
            elif "賤" in recv_data or "蠢" in recv_data :
                client_socket.send("你個蠢貨!".encode('gbk'))
            elif "吃" in recv_data or "hello" in recv_data:
                client_socket.send("紅燒肉、東坡肘子...".encode('gbk'))
            elif "玩" in recv_data or "hello" in recv_data:
                client_socket.send("雲南麗江不錯!".encode('gbk'))
            elif "名字" in recv_data or "name" in recv_data:
                client_socket.send("我叫小美,編號9527,哈哈...".encode('gbk'))
            elif "時間" in recv_data or "time" in recv_data:
                client_socket.send(('如今時間是:'+time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))).encode('gbk')) # 返回當前時間
            else:
                self.chat_list.append(recv_data)  # 收集用戶輸入信息,豐富詞彙
                rand_idx = random.randint(0, len(self.chat_list) - 1)                # 經過隨機下標獲取一條信息
                send_data = self.chat_list[rand_idx]                 # 將信息發送給客戶端
                client_socket.send(send_data.encode('gbk'))        # 關閉爲這個客戶端服務的套接字,只要關閉了,就意味着爲不能再爲這個客戶端服務了,若是還須要服務,只能再次從新鏈接
        client_socket.close()​
def main():
    server = Server_Socket()
    server.start()
​if __name__ == '__main__':
    main()複製代碼

上面的代碼是聊天機器人服務端代碼,可和用戶進行通常的閒聊,返回當前時間等,代碼邏輯不復雜,優勢是能夠自行定製。

2.調用圖靈機器人API實現自動回覆

圖靈機器人的接口能夠實現的功能有:中文聊天,情感引擎等。要使用圖靈的API,首先要去它官網進行註冊,而後建立機器人,獲取一個APIkey,而後才能使用它的API接口。下面是網址入口:

www.turingapi.com/

下面對它的API文檔有用的一部分進行摘抄:

編碼方式

UTF-8(調用圖靈API的各個環節的編碼方式均爲UTF-8)

接口地址

openapi.tuling123.com/openapi/api…

請求方式

HTTP POST

請求參數

請求參數格式爲 json 請求示例:

{    "reqType":0,    "perception": {        "inputText": {            "text": "附近的酒店"        },        "inputImage": {            "url": "imageUrl"        },        "selfInfo": {            "location": {                "city": "北京",                "province": "北京",                "street": "信息路"            }        }    },    "userInfo": {        "apiKey": "",        "userId": ""    }}複製代碼

參數說明

參數 類型 是否必須 取值範圍 說明
reqType int N - 輸入類型:0-文本(默認)、1-圖片、2-音頻
perception - Y - 輸入信息
userInfo - Y - 用戶參數

perception

參數 類型 是否必須 取值範圍 說明
inputText - N - 文本信息
inputImage - N - 圖片信息
inputMedia - N - 音頻信息
selfInfo - N - 客戶端屬性

注意:輸入參數必須包含inputText或inputImage或inputMedia!

inputText

參數 類型 是否必須 取值範圍 說明
text String Y 1-128字符 直接輸入文本

inputImage

參數 類型 是否必須 取值範圍 說明
url String Y 圖片地址

inputMedia

參數 類型 是否必須 取值範圍 說明
url String Y 音頻地址

selfInfo

參數 類型 是否必須 取值範圍 說明
location - N - 地理位置信息

location

參數 類型 是否必須 取值範圍 說明
city String Y - 所在城市
province String N - 省份
street String N - 街道

userInfo

參數 類型 是否必須 取值範圍 說明
apiKey String Y 32位 機器人標識
userId String Y 長度小於等於32位 用戶惟一標識
groupId String N 長度小於等於64位 羣聊惟一標識
userIdName String N 長度小於等於64位 羣內用戶暱稱

輸出參數

輸出示例:

{    "intent": {        "code": 10005,        "intentName": "",        "actionName": "",        "parameters": {            "nearby_place": "酒店"        }    },    "results": [        {            "groupType": 1,            "resultType": "url",            "values": {                "url": "http://m.elong.com/hotel/0101/nlist/#indate=2016-12-10&outdate=2016-12-11&keywords=%E4%BF%A1%E6%81%AF%E8%B7%AF"            }        },        {            "groupType": 1,            "resultType": "text",            "values": {                "text": "親,已幫你找到相關酒店信息"            }        }    ]}複製代碼

參數說明

參數 類型 是否必須 取值範圍 說明
intent - Y - 請求意圖
results - N - 輸出結果集

intent

參數 類型 是否包含 取值範圍 說明
code int Y - 輸出功能code
intentName String N - 意圖名稱
actionName String N - 意圖動做名稱
parameters Map N - 功能相關參數

results

參數 類型 是否包含 取值範圍 說明
resultType String Y 文本(text);鏈接(url);音頻(voice);視頻(video);圖片(image);圖文(news) 輸出類型
values - Y - 輸出值
groupType int Y - ‘組’編號:0爲獨立輸出,大於0時可能包含同組相關內容 (如:音頻與文本爲一組時說明內容一致)

下面是針對文檔來封裝實現輸入關鍵詞來返回用戶輸入信息的函數代碼:

import requests
import json
def get_response(msg):
    api = 'http://openapi.tuling123.com/openapi/api/v2' # 接口地址
    data = {        "perception": {            "inputText": {                "text": msg            },            "inputImage": {                "url": "imageUrl"            },            "selfInfo": {                "location": {                    "city": "成都",  # 參數必須指定地點 "province": "四川", # 參數必須 "street": "蜀西路" } } }, "userInfo": { "apiKey": '', # 參數必須此處填入網站申請的key "userId": "" } }
    data = json.dumps(data)  # 將字典格式轉化爲json格式,另外loads函數是將json轉化爲python中的字典
    print(data)
    print('=================================================================================')
    r = requests.post(api, data=data).json()  # 將post請求的結果轉爲json
    print(r)
    return r['results'][0]['values']['text'] # 返回的數據
​mes = get_response('天氣') # 輸入關鍵詞複製代碼

上面用到了python內置的request和json庫,調用了幾回發現有時返回的結果不太滿意,不知道是否是沒有買它套餐的緣由。上一個版本的機器人服務端只實現了單用戶,下面實現能夠多用戶聊天的版本:

import socket
import threading
import requests
import json​​
# 建立web服務器的類
class HttpWebServer:
    """初始化套接字對象"""def __init__(self, port):
        tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        tcp_server_socket.bind(('', port))
        tcp_server_socket.listen(128)
        self.tcp_server_socket = tcp_server_socket
​    @staticmethod
    def get_response(msg):
        # 調用圖靈機器人API
        api = 'http://openapi.tuling123.com/openapi/api/v2'
        data = {            "perception": {                "inputText": {                    "text": msg                },                "inputImage": {                    "url": "imageUrl"                },                "selfInfo": {                    "location": {                        "city": "成都",                        "province": "四川",                        "street": ""                    }                }            },            "userInfo": {                "apiKey": '',# 填入申請的key "userId": "" } }
        data = json.dumps(data)  # 將字典格式轉化爲json格式,另外loads函數是將json轉化爲python中的字典
        print(data)
        # print('=================================================================================')
        r = requests.post(api, data=data).json()  # 將post請求的結果轉爲json
        print(r)
        return r['results'][0]['values']['text']  # 返回的數據
​    @staticmethod
    def client(new_socket):
        """新套接-請求-響應"""
        # 接受客戶端消息
        while True:
            recv_data = (new_socket.recv(4096))
            recv_decode = recv_data.decode('utf-8')
            # 判斷請求內容長度,若爲0,則瀏覽器斷開鏈接
            if len(recv_data) == 0:
                print('offline')
                new_socket.close()
                return​
            print('帥哥:' + recv_decode)
            response = HttpWebServer.get_response(recv_decode)
            new_socket.send(response.encode('utf-8'))
​​    def start(self):
        """開啓服務器的方法"""
        while True:
            # 循環接受請求,並建立相應的套接字
            new_socket, ip_port = self.tcp_server_socket.accept()
            # 運用多線程實現多個客戶端訪問,並設置主線程守護
            sub_threading = threading.Thread(target=self.client, args=(new_socket,), daemon=True)
​            # 子線程開啓
            sub_threading.start()​​
def main():
    """程序入口"""
    web_server = HttpWebServer(8989)
    web_server.start()
​if __name__ == '__main__':
    main()複製代碼

上面採用了threading實現了可多用戶聊天,並使用了守護主線程,防止了在主線程接受數據阻塞引發服務器崩潰的狀況。

相關文章
相關標籤/搜索