摘要:最近借用了女朋友的公號(hào),感覺如果只是用來(lái)發(fā)文章,太浪費(fèi)微信給提供的這些功能了。想了想,先從最簡(jiǎn)單的開始,做一個(gè)聊天機(jī)器人吧。是一款接口的,基于一系列規(guī)則和機(jī)器學(xué)習(xí)算法完成的聊天機(jī)器人。
最近借用了女朋友的公號(hào),感覺如果只是用來(lái)發(fā)文章,太浪費(fèi)微信給提供的這些功能了。想了想,先從最簡(jiǎn)單的開始,做一個(gè)聊天機(jī)器人吧。
使用Python實(shí)現(xiàn)聊天機(jī)器人的方案有多種:AIML、chatterBot以及圖靈聊天機(jī)器人和微軟小冰等。
考慮到以后可能會(huì)做一些定制化的需求,這里我選擇了chatterBot(github 項(xiàng)目地址:https://github.com/gunthercox/ChatterBot)。
chatterbot是一款python接口的,基于一系列規(guī)則和機(jī)器學(xué)習(xí)算法完成的聊天機(jī)器人。具有結(jié)構(gòu)清晰,可擴(kuò)展性好,簡(jiǎn)單實(shí)用的特點(diǎn)。
chatterBot 的工作流程如圖:
輸入模塊(input adapter)從終端或者API等輸入源獲取數(shù)據(jù)
輸入源會(huì)被指定的邏輯處理模塊(logic Adapter)分別處理,邏輯處理模塊會(huì)匹配訓(xùn)練集中已知的最接近輸入數(shù)據(jù)句子A,然后根據(jù)句子A去找到相關(guān)度最高的結(jié)果B,如果有多個(gè)邏輯處理模塊返回了不同的結(jié)果,會(huì)返回一個(gè)相關(guān)度最高的結(jié)果。
輸出模塊(output adapter)將匹配到的結(jié)果返回給終端或者API。
值得一說(shuō)的是chatterBot 是一個(gè)模塊化的項(xiàng)目,分為 input Adapter、logic Adapter、storage Adapter、output Adapter以及Trainer 模塊。
logic Adapter是一個(gè)插件式設(shè)計(jì),主進(jìn)程在啟動(dòng)時(shí)會(huì)將用戶定義的所有邏輯處理插件添加到logic context中,然后交MultiLogicAdapter 進(jìn)行處理,MultiLogicAdapter 依次調(diào)用每個(gè) logic Adapter,logic Adapter 被調(diào)用時(shí)先執(zhí)行can_process 方式判斷輸入是否可以命中這個(gè)邏輯處理插件。比如”今天天氣怎么樣“這樣的問題顯然需要命中天氣邏輯處理插件,這時(shí)時(shí)間邏輯處理插件的can_process 則會(huì)返回False。在命中后logic Adapter 負(fù)責(zé)計(jì)算出對(duì)應(yīng)的回答(Statement對(duì)象)以及可信度(confidence),MultiLogicAdapter會(huì)取可信度最高的回答,并進(jìn)入下一步。
下面我們來(lái)看下 chatterBot 如何使用
chatterBot 安裝&使用 安裝chatterBot 是使用Python編寫的,可以使用 pip 安裝:
pip install chatterbot
測(cè)試chatterBot 的中文對(duì)話要求Python3 以上版本,建議在Python3.x 環(huán)境下開發(fā)
打開iPython,輸入測(cè)試一下
In [1]: from chatterbot import ChatBot # import ChatBot In [2]: momo = ChatBot("Momo", trainer="chatterbot.trainers.ChatterBotCorpusTrainer") /Users/gs/.virtualenvs/py3/lib/python3.6/site-packages/chatterbot/storage/jsonfile.py:26: UnsuitableForProductionWarning: The JsonFileStorageAdapter is not recommended for production environments. self.UnsuitableForProductionWarning # 這里storage adapter 默認(rèn)使用的是 json 格式存儲(chǔ)數(shù)據(jù)的,如果想在服務(wù)端部署,應(yīng)該避免使用這種格式,因?yàn)閷?shí)在是太慢了 In [3]: momo.train("chatterbot.corpus.chinese") # 指定訓(xùn)練集,這里我們使用中文 # 下邊是對(duì)話結(jié)果 In [4]: momo.get_response("你好") Out[4]:In [5]: momo.get_response("怎么了") Out[5]: In [6]: momo.get_response("你知道它的所有內(nèi)容嗎?") Out[6]: In [7]: momo.get_response("你是一個(gè)程序員嗎?") Out[7]: In [8]: momo.get_response("你使用什么語(yǔ)言呢?") Out[8]:
這時(shí)你已經(jīng)可以和機(jī)器人對(duì)話了,不過(guò)現(xiàn)在由于訓(xùn)練數(shù)據(jù)太少,機(jī)器人只能返回簡(jiǎn)單的對(duì)話。
這里是默認(rèn)的中文對(duì)話訓(xùn)練數(shù)據(jù) 中文訓(xùn)練數(shù)據(jù)地址:https://github.com/gunthercox/chatterbot-corpus/tree/master/chatterbot_corpus/data/chinese。
那么我們?cè)趺刺砑佑?xùn)練數(shù)據(jù)呢?
訓(xùn)練機(jī)器人chatterBot 內(nèi)置了training class,自帶的方法有兩種,一種是使用通過(guò)輸入list 來(lái)訓(xùn)練,比如 ["你好", "我不好"],后者是前者的回答,另一種是通過(guò)導(dǎo)入Corpus 格式的文件來(lái)訓(xùn)練。也支持自定義的訓(xùn)練模塊,不過(guò)最終都是轉(zhuǎn)為上述兩種類型。
chatterBot 通過(guò)調(diào)用 train() 函數(shù)訓(xùn)練,不過(guò)在這之前要先用 set_trainer() 來(lái)進(jìn)行設(shè)置。例如:
In [12]: from chatterbot.trainers import ListTrainer # 導(dǎo)入訓(xùn)練模塊的 ListTrainer 類 In [13]: momo.get_response("你叫什么?") # 現(xiàn)在是答非所問,因?yàn)樵谶@之前我們并沒有訓(xùn)練過(guò) Out[13]:In [14]: momo.set_trainer(ListTrainer) # 指定訓(xùn)練方式 In [15]: momo.train(["你叫什么?", "我叫魔魔!"]) # 訓(xùn)練 In [16]: momo.get_response("你叫什么?") # 現(xiàn)在機(jī)器人已經(jīng)可以回答了 Out[16]:
訓(xùn)練好的數(shù)據(jù)默認(rèn)存在 ./database.db,這里使用的是 jsondb。
對(duì) chatterBot 的介紹先到這里,具體用法可以參考文檔:ChatterBot Tutorial:http://chatterbot.readthedocs.io/en/stable/tutorial.html
接下來(lái),介紹如何在項(xiàng)目中使用 chatterBot。
使用 Sanic 創(chuàng)建項(xiàng)目Sanic 是一個(gè)和類Flask 的基于Python3.5+的web框架,它編寫的代碼速度特別快。
除了像Flask 以外,Sanic 還支持以異步請(qǐng)求的方式處理請(qǐng)求。這意味著你可以使用新的 async/await 語(yǔ)法,編寫非阻塞的快速的代碼。
對(duì) Sanic 不了解的可以參考我之前的一篇文章: python web 框架 Sanci 快速入門,可以在公號(hào)輸入 【sanic】獲取文章地址。
這里之所以使用 Sanic 是因?yàn)樗虵lask 非常像,之前我一直使用Flask,并且它也是專門為Python3.5 寫的,使用到了協(xié)程。
首先建個(gè)項(xiàng)目,這里項(xiàng)目我已經(jīng)建好了,項(xiàng)目結(jié)構(gòu)如下:
. ├── LICENSE ├── README.md ├── manage.py # 運(yùn)行文件 啟動(dòng)項(xiàng)目 使用 python manage.py 命令 ├── momo │ ├── __init__.py │ ├── app.py # 創(chuàng)建app 模塊 │ ├── helper.py │ ├── settings.py # 應(yīng)用配置 │ └── views │ ├── __init__.py │ ├── hello.py # 測(cè)試模塊 │ └── mweixin.py # 微信消息處理模塊 ├── requirements.txt └── supervisord.conf
源碼我已經(jīng)上傳到github,有興趣的可以看一下,也可以直接拉下來(lái)測(cè)試。
項(xiàng)目代碼地址
我們先重點(diǎn)看下 hello.py 文件 和 helper.py 。
# hello.py # -*- coding: utf-8 -*- from sanic import Sanic, Blueprint from sanic.views import HTTPMethodView from sanic.response import text from momo.helper import get_momo_answer # 導(dǎo)入獲取機(jī)器人回答獲取函數(shù) blueprint = Blueprint("index", url_prefix="/") class ChatBot(HTTPMethodView): # 聊天機(jī)器人 http 請(qǐng)求處理邏輯 async def get(self, request): ask = request.args.get("ask") # 先獲取url 參數(shù)值 如果沒有值,返回 "你說(shuō)啥" if ask: answer = get_momo_answer(ask) return text(answer) return text("你說(shuō)啥?") blueprint.add_route(ChatBot.as_view(), "/momo")
# helper.py from chatterbot import ChatBot momo_chat = ChatBot( "Momo", # 指定存儲(chǔ)方式 使用mongodb 存儲(chǔ)數(shù)據(jù) storage_adapter="chatterbot.storage.MongoDatabaseAdapter", # 指定 logic adpater 這里我們指定三個(gè) logic_adapters=[ "chatterbot.logic.BestMatch", "chatterbot.logic.MathematicalEvaluation", # 數(shù)學(xué)模塊 "chatterbot.logic.TimeLogicAdapter", # 時(shí)間模塊 ], input_adapter="chatterbot.input.VariableInputTypeAdapter", output_adapter="chatterbot.output.OutputAdapter", database="chatterbot", read_only=True ) def get_momo_answer(content): # 獲取機(jī)器人返回結(jié)果函數(shù) response = momo_chat.get_response(content) if isinstance(response, str): return response return response.text
運(yùn)行命令 python manage.py 啟動(dòng)項(xiàng)目。
在瀏覽器訪問url: http://0.0.0.0:8000/momo?ask=你是程序員嗎
到這里,我們已經(jīng)啟動(dòng)了一個(gè)web 項(xiàng)目,可以通過(guò)訪問url 的方式和機(jī)器人對(duì)話,是時(shí)候接入微信公號(hào)了!
接入微信公眾號(hào) 前提擁有一個(gè)可以使用的微信公眾號(hào)(訂閱號(hào)服務(wù)號(hào)都可以,如果沒有,可以使用微信提供的測(cè)試賬號(hào))
擁有一個(gè)外網(wǎng)可以訪問的服務(wù)器(vps 或公有云都可以 aws 新用戶免費(fèi)使用一年,可以試試)
服務(wù)器配置了python3 環(huán)境,(建議使用 virtualenvwrapper 配置虛擬環(huán)境)
微信設(shè)置登錄微信公眾號(hào): https://mp.weixin.qq.com
打開:開發(fā)>基本配置查看公號(hào)開發(fā)信息:
開啟服務(wù)器配置:設(shè)置請(qǐng)求url,這里是你配置的url(需要外網(wǎng)可訪問,只能是80或443端口)
填寫token和EncodingAESKey,這里我選擇的是兼容模式,既有明文方便調(diào)試,又有信息加密。
詳細(xì)配置可以參考官方文檔:接入指南
如果你的 服務(wù)器地址 已經(jīng)配置完成,現(xiàn)在點(diǎn)擊提交應(yīng)該就成功了。如果沒有成功我們接下來(lái)看怎么配置服務(wù)器地址。
代碼示例先看下 微信請(qǐng)求的視圖代碼:
# -*- coding: utf-8 -*- from __future__ import unicode_literals from six import StringIO import re import xmltodict from chatterbot.trainers import ListTrainer from sanic import Blueprint from sanic.views import HTTPMethodView from sanic.response import text from sanic.exceptions import ServerError from weixin import WeixinMpAPI from weixin.lib.WXBizMsgCrypt import WXBizMsgCrypt from momo.settings import Config blueprint = Blueprint("weixin", url_prefix="/weixin") class WXRequestView(HTTPMethodView): def _get_args(self, request): # 獲取微信請(qǐng)求參數(shù),加上token 拼接為完整的請(qǐng)求參數(shù) params = request.raw_args if not params: raise ServerError("invalid params", status_code=400) args = { "mp_token": Config.WEIXINMP_TOKEN, "signature": params.get("signature"), "timestamp": params.get("timestamp"), "echostr": params.get("echostr"), "nonce": params.get("nonce"), } return args def get(self, request): # 微信驗(yàn)證服務(wù)器這一步是get 請(qǐng)求,參數(shù)可以使用 request.raw_args 獲取 args = self._get_args(request) weixin = WeixinMpAPI(**args) # 這里我使用了 第三方包 python-weixin 可以直接實(shí)例化一個(gè)WeixinMpAPI對(duì)象 if weixin.validate_signature(): # 驗(yàn)證參數(shù)合法性 # 如果參數(shù)爭(zhēng)取,我們將微信發(fā)過(guò)來(lái)的echostr參數(shù)再返回給微信,否則返回 fail return text(args.get("echostr") or "fail") return text("fail") blueprint.add_route(WXRequestView.as_view(), "/request")
這里處理微信請(qǐng)求我使用的是 我用python 寫的 微信SDK python-weixin,可以使用 pip 安裝:
pip install python-weixin
這個(gè)包最新版本對(duì)Python3 加密解密有點(diǎn)問題,可以直接從github 安裝:
pip install git+https://github.com/zongxiao/python-weixin.git@py3
然后更新 app.py 文件:
# -*- coding: utf-8 -*- from sanic import Sanic from momo.settings import Config def create_app(register_bp=True, test=False): # 創(chuàng)建app app = Sanic(__name__) if test: app.config["TESTING"] = True # 從object 導(dǎo)入配置 app.config.from_object(Config) register_blueprints(app) return app def register_blueprints(app): from momo.views.hello import blueprint as hello_bp from momo.views.mweixin import blueprint as wx_bp app.register_blueprint(hello_bp) # 注冊(cè) wx_bp app.register_blueprint(wx_bp)
詳細(xì)代碼參考github: 微信聊天機(jī)器人 momo
接入聊天機(jī)器人現(xiàn)在我們公號(hào)已經(jīng)接入了自己的服務(wù),是時(shí)候接入微信聊天機(jī)器人。
微信聊天機(jī)器人的工作流程如下:
看我們消息邏輯處理代碼:
# -*- coding: utf-8 -*- from __future__ import unicode_literals from six import StringIO import re import xmltodict from chatterbot.trainers import ListTrainer from sanic import Blueprint from sanic.views import HTTPMethodView from sanic.response import text from sanic.exceptions import ServerError from weixin import WeixinMpAPI from weixin.reply import TextReply from weixin.response import WXResponse as _WXResponse from weixin.lib.WXBizMsgCrypt import WXBizMsgCrypt from momo.settings import Config from momo.helper import validate_xml, smart_str, get_momo_answer from momo.media import media_fetch blueprint = Blueprint("weixin", url_prefix="/weixin") appid = smart_str(Config.WEIXINMP_APPID) token = smart_str(Config.WEIXINMP_TOKEN) encoding_aeskey = smart_str(Config.WEIXINMP_ENCODINGAESKEY) # 關(guān)注后自動(dòng)返回的文案 AUTO_REPLY_CONTENT = """ Hi,朋友! 這是我媽四月的公號(hào),我是魔魔,我可以陪你聊天呦! 我還能"記賬",輸入"記賬"會(huì)有驚喜呦! 歷史記錄 """ class ReplyContent(object): _source = "value" def __init__(self, event, keyword, content=None, momo=True): self.momo = momo self.event = event self.content = content self.keyword = keyword if self.event == "scan": pass @property def value(self): if self.momo: answer = get_momo_answer(self.content) return answer return "" class WXResponse(_WXResponse): auto_reply_content = AUTO_REPLY_CONTENT def _subscribe_event_handler(self): # 關(guān)注公號(hào)后的處理邏輯 self.reply_params["content"] = self.auto_reply_content self.reply = TextReply(**self.reply_params).render() def _unsubscribe_event_handler(self): # 取關(guān)后的處理邏輯,取關(guān)我估計(jì)會(huì)哭吧 pass def _text_msg_handler(self): # 文字消息處理邏輯 聊天機(jī)器人的主要邏輯 event_key = "text" content = self.data.get("Content") reply_content = ReplyContent("text", event_key, content) self.reply_params["content"] = reply_content.value self.reply = TextReply(**self.reply_params).render() class WXRequestView(HTTPMethodView): def _get_args(self, request): params = request.raw_args if not params: raise ServerError("invalid params", status_code=400) args = { "mp_token": Config.WEIXINMP_TOKEN, "signature": params.get("signature"), "timestamp": params.get("timestamp"), "echostr": params.get("echostr"), "nonce": params.get("nonce"), } return args def get(self, request): args = self._get_args(request) weixin = WeixinMpAPI(**args) if weixin.validate_signature(): return text(args.get("echostr") or "fail") return text("fail") def _get_xml(self, data): post_str = smart_str(data) # 驗(yàn)證xml 格式是否正確 validate_xml(StringIO(post_str)) return post_str def _decrypt_xml(self, params, crypt, xml_str): # 解密消息 nonce = params.get("nonce") msg_sign = params.get("msg_signature") timestamp = params.get("timestamp") ret, decryp_xml = crypt.DecryptMsg(xml_str, msg_sign, timestamp, nonce) return decryp_xml, nonce def _encryp_xml(self, crypt, to_xml, nonce): # 加密消息 to_xml = smart_str(to_xml) ret, encrypt_xml = crypt.EncryptMsg(to_xml, nonce) return encrypt_xml def post(self, request): # 獲取微信服務(wù)器發(fā)送的請(qǐng)求參數(shù) args = self._get_args(request) weixin = WeixinMpAPI(**args) if not weixin.validate_signature(): # 驗(yàn)證參數(shù)合法性 raise AttributeError("Invalid weixin signature") xml_str = self._get_xml(request.body) # 獲取form data crypt = WXBizMsgCrypt(token, encoding_aeskey, appid) decryp_xml, nonce = self._decrypt_xml(request.raw_args, crypt, xml_str) # 解密 xml_dict = xmltodict.parse(decryp_xml) xml = WXResponse(xml_dict)() or "success" # 使用WXResponse 根據(jù)消息獲取機(jī)器人返回值 encryp_xml = self._encryp_xml(crypt, xml, nonce) # 加密消息 return text(encryp_xml or xml) # 回應(yīng)微信請(qǐng)求 blueprint.add_route(WXRequestView.as_view(), "/request")
可以看到,我處理微信請(qǐng)求返回結(jié)果比較簡(jiǎn)單,也是使用的 python-weixin 包封裝的接口,
主要的處理邏輯是 WXResponse。
這里需要注意的是,如果服務(wù)器在5秒內(nèi)沒有響應(yīng)微信服務(wù)器會(huì)重試。為了加快響應(yīng)速度,不要在服務(wù)器 將 chatterBot 的 storage adapter 設(shè)置為使用 jsondb。
上邊這些就是,微信聊天機(jī)器人的主要處理邏輯,我們運(yùn)行服務(wù),示例如下:
可以看到這里聊天機(jī)器人也可以做簡(jiǎn)單的數(shù)學(xué)運(yùn)算和報(bào)時(shí),是因?yàn)槲以谏线呏付ㄌ幚磉壿嫷臅r(shí)候添加了數(shù)學(xué)模塊和時(shí)間模塊:
momo_chat = ChatBot( "Momo", # 指定存儲(chǔ)方式 使用mongodb 存儲(chǔ)數(shù)據(jù) storage_adapter="chatterbot.storage.MongoDatabaseAdapter", # 指定 logic adpater 這里我們指定三個(gè) logic_adapters=[ "chatterbot.logic.BestMatch", "chatterbot.logic.MathematicalEvaluation", # 數(shù)學(xué)模塊 "chatterbot.logic.TimeLogicAdapter", # 時(shí)間模塊 ], input_adapter="chatterbot.input.VariableInputTypeAdapter", output_adapter="chatterbot.output.OutputAdapter", database="chatterbot", read_only=True )
到這里,微信機(jī)器人的搭建就完成了,詳細(xì)代碼已經(jīng)長(zhǎng)傳到了 github: https://github.com/gusibi/momo/tree/chatterbot,感興趣的可以參考一下。
參考鏈接ChatterBot 項(xiàng)目地址:https://github.com/gunthercox/ChatterBot
ChatterBot Tutorial:http://chatterbot.readthedocs.io/en/stable/tutorial.html
用Python快速實(shí)現(xiàn)一個(gè)聊天機(jī)器人:http://www.jianshu.com/p/d1333fde266f
基于Python-ChatterBot搭建不同adapter的聊天機(jī)器人:https://ask.hellobi.com/blog/guodongwei1991/7626
擁有自動(dòng)學(xué)習(xí)的 Python 機(jī)器人 - ChatterBot:https://kantai235.github.io/2017/03/16/ChatterBotTeaching/
使用 ChatterBot構(gòu)建聊天機(jī)器人:https://www.biaodianfu.com/chatterbot.html
python-weixin sdk: https://github.com/gusibi/python-weixin
預(yù)告這里,聊天機(jī)器人還是比較簡(jiǎn)單的只能回復(fù)簡(jiǎn)單的對(duì)話,下一篇將要結(jié)束如何在公號(hào)訓(xùn)練機(jī)器人以及一個(gè)更實(shí)用的功能,如何讓公號(hào)變成一個(gè)博客寫作助手。
最后,感謝女朋友支持。
歡迎關(guān)注(April_Louisa) | 請(qǐng)我喝芬達(dá) |
---|---|
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/38685.html
摘要:使用圖靈機(jī)器人訓(xùn)練之前在對(duì)比聊天機(jī)器人實(shí)現(xiàn)方案的時(shí)候,我試用過(guò)圖靈機(jī)器人,他們號(hào)稱中文語(yǔ)境下智能渡最高的機(jī)器人大腦。 上一篇 一小時(shí)搭建微信聊天機(jī)器人 介紹了如何搭建一個(gè)可用的聊天機(jī)器人,但是和機(jī)器人聊完你會(huì)發(fā)現(xiàn),聊天機(jī)器人實(shí)在是太傻了,來(lái)回就那么幾句。這是因?yàn)槲覀兘o聊天機(jī)器人的數(shù)據(jù)太少,他只能在我們給的訓(xùn)練集中找它認(rèn)為最合適的。那么,如何導(dǎo)入更多的訓(xùn)練數(shù)據(jù)呢?我能想到最簡(jiǎn)單的方法是找...
摘要:然后又介紹了基于的公號(hào)賬本應(yīng)用的數(shù)據(jù)庫(kù)設(shè)計(jì)。歡迎關(guān)注公號(hào)四月試用。 前兩篇 微信公號(hào)DIY 系列: 微信公號(hào)DIY:一小時(shí)搭建微信聊天機(jī)器人 微信公號(hào)DIY:訓(xùn)練聊天機(jī)器人&公號(hào)變身圖片上傳工具 介紹了如何使用搭建&訓(xùn)練聊天機(jī)器人以及讓公號(hào)支持圖片上傳到七牛,把公號(hào)變成一個(gè)七牛圖片上傳客戶端。這一篇將繼續(xù)開發(fā)公號(hào),讓公號(hào)變成一個(gè)更加實(shí)用的工具賬本(理財(cái)從記賬開始)。 代碼: 項(xiàng)目代...
摘要:然后又介紹了基于的公號(hào)賬本應(yīng)用的數(shù)據(jù)庫(kù)設(shè)計(jì)。歡迎關(guān)注公號(hào)四月試用。 前兩篇 微信公號(hào)DIY 系列: 微信公號(hào)DIY:一小時(shí)搭建微信聊天機(jī)器人 微信公號(hào)DIY:訓(xùn)練聊天機(jī)器人&公號(hào)變身圖片上傳工具 介紹了如何使用搭建&訓(xùn)練聊天機(jī)器人以及讓公號(hào)支持圖片上傳到七牛,把公號(hào)變成一個(gè)七牛圖片上傳客戶端。這一篇將繼續(xù)開發(fā)公號(hào),讓公號(hào)變成一個(gè)更加實(shí)用的工具賬本(理財(cái)從記賬開始)。 代碼: 項(xiàng)目代...
摘要:前端日?qǐng)?bào)精選專題之類型判斷下百度生態(tài)構(gòu)建發(fā)布基于的解決方案將全面支持從綁定,看語(yǔ)言發(fā)展和框架設(shè)計(jì)掘金譯機(jī)器學(xué)習(xí)與一付費(fèi)問答上線,向你心目中的大牛提問吧產(chǎn)品技術(shù)日志中文第期團(tuán)隊(duì)技術(shù)信息流建設(shè)翻譯基于路由的異步組件加載個(gè)必備的裝逼 2017-07-06 前端日?qǐng)?bào) 精選 JavaScript專題之類型判斷(下) · Issue #30 · mqyqingfeng/Blog 百度Web生態(tài)構(gòu)...
閱讀 1083·2023-04-25 15:42
閱讀 3683·2021-11-02 14:38
閱讀 2951·2021-09-30 09:48
閱讀 1530·2021-09-23 11:22
閱讀 3502·2021-09-06 15:02
閱讀 3251·2021-09-04 16:41
閱讀 676·2021-09-02 15:41
閱讀 2089·2021-08-26 14:13