网站改版|WAP网站制作|域名注册|虚拟主机|服务器|海微商|海微信|海微通| 无障碍| 24小时服务电话:13807590485
海南网站建设海南网站制作海口网站建设三亚网站建设儋州网站建设五指山网站建设文昌网站建设琼海网站建设万宁网站建设东方网站建设定安网站建设 网站首页网站首页 网站建设网站建设 微信开发微信开发 网站推广网站推广 海南网站建设公司,海南网站开发制作公司,海南网页设计公司,海南小程序开发公司,海南微信公众号开发公司,海南网络公司,海南世纪华联海南世纪华联 网站超市网站超市 客户案例客户案例 网站模板网站模板 关于我们关于我们
  • 微信开发
  • 持之以恒
  • 网站设计制作
  • 中立五年回报客户
无障碍
微信开发
微信系统开发 微信开发功能 公众号基础教程 开发技术资讯 公众号推广营销 客户案例
联系我们
QQ服务群:28519571 工作时间:86-0898-31568080 传真号码:86-0898-31568085 24小时服务:0138-07590485
 您现在的位置: 首页 >> 微信开发 >> 开发技术资讯 开发技术资讯
微信机器人:有道翻译小助手——Django + SAE + 微信公众帐号自动回复开放接口
世纪华联 | 2018-01-26 23:32:14 | 阅读:12265
Sina App Engine

  首先作为一个屌丝开发者要解决服务器的问题。去SAE的官网上用新浪微博的帐号注册一个帐号,成功后会赠给你500云豆,可供一个开发者试用大概5天。SAE非常优秀,如果以后我们想在上面写点应用什么的,可以去申请实名认证和开发者认证,那样每个月都会给你一定数量的云豆,应该能满足日常需求。由于现在SAE上部署Python还处于公测阶段,因此我们要去申请开通可以在上面部署Python程序的权限,现在很好申请的,不一会就会收到已经为你开放了部署Python应用权限的邮件,网上搜到的啥啥还需要排队都是过去了(不排除当你看到这篇文章时SAE已经可以允许所有开发者部署Python的应用了)。
 

  OK,完成这些后,就可以到SAE的文档中心读文档来照着文档里面的样例创建一个应用了。

  有道API

  然后,去有道API申请一个key,申请的时候网站地址随便填就行。有道API非常简单,直接以GET的形式把要翻译的文本发送到指定的url,然后它会给我们回复翻译结果,我们可以选择xml、json等返回格式,我选得是xml,接着,在浏览器里面按着指定的格式输入url,就可以看到返回结果啦:
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <youdao-fanyi>  
  3.     <errorCode>0</errorCode>  
  4.     <query><![CDATA[这里是有道翻译API]]></query>  
  5.     <!-- 有道翻译 -->  
  6.     <translation>  
  7.             <paragraph><![CDATA[Here is the youdao translation API]]></paragraph>  
  8.         </translation>  
  9. </youdao-fanyi>  
复制代码

  注意,如果是对词进行翻译的话有的词还会返回一些啥网络释义,基本释义啥的,具体对这个xml解析的方法请看下面的代码。

  微信公众帐号

  接着,我们要去微信的公众帐号平台去申请一个公众帐号,不能用现有的已绑定私人微信帐号的QQ号申请,我用的是一个平时不用的QQ号申请的,申请成功后,可以大致看看微信公众帐号的管理平台(现在你知道那些公众帐号,比如王力宏的帐号啥的是怎么运作的了吧),接着去这里仔细阅读微信公众帐号自动回复开放接口的文档,你要从这里学一种如何让用户认证的思想(就是如果用户做了XX,给我返回了XX结果,那么我就能确定,用户是“合法”的),或者认证的方法。大致有一个认识后,赶紧下载他给的样例php源码,也是唯一的可以参考的源码,仔细阅读,如下:
  1. <?php  
  2. /** 
  3.   * wechat php test 
  4.   */  
  5.   
  6. //define your token  
  7. define("TOKEN", "weixin");  
  8. $wechatObj = new wechatCallbackapiTest();  
  9. $wechatObj->valid();  
  10.   
  11. class wechatCallbackapiTest  
  12. {  
  13.     public function valid()  
  14.     {  
  15.         $echoStr = $_GET["echostr"];  
  16.   
  17.         //valid signature , option  
  18.         if($this->checkSignature()){  
  19.             echo $echoStr;  
  20.             exit;  
  21.         }  
  22.     }  
  23.   
  24.     public function responseMsg()  
  25.     {  
  26.         //get post data, May be due to the different environments  
  27.         $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];  
  28.   
  29.         //extract post data  
  30.         if (!empty($postStr)){  
  31.        echoStr           
  32.                 $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);  
  33.                 $fromUsername = $postObj->FromUserName;  
  34.                 $toUsername = $postObj->ToUserName;  
  35.                 $keyword = trim($postObj->Content);  
  36.                 $time = time();  
  37.                 $textTpl = "<xml>  
  38.                             <ToUserName><![CDATA[%s]]></ToUserName>  
  39.                             <FromUserName><![CDATA[%s]]></FromUserName>  
  40.                             <CreateTime>%s</CreateTime>  
  41.                             <MsgType><![CDATA[%s]]></MsgType>  
  42.                             <Content><![CDATA[%s]]></Content>  
  43.                             <FuncFlag>0</FuncFlag>  
  44.                             </xml>";               
  45.                 if(!empty( $keyword ))  
  46.                 {  
  47.                     $msgType = "text";  
  48.                     $contentStr = "Welcome to wechat world!";  
  49.                     $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);  
  50.                     echo $resultStr;  
  51.                 }else{  
  52.                     echo "Input something...";  
  53.                 }  
  54.   
  55.         }else {  
  56.             echo "";  
  57.             exit;  
  58.         }  
  59.     }  
  60.           
  61.     private function checkSignature()  
  62.     {  
  63.         $signature = $_GET["signature"];  
  64.         $timestamp = $_GET["timestamp"];  
  65.         $nonce = $_GET["nonce"];      
  66.                   
  67.         $token = TOKEN;  
  68.         $tmpArr = array($token, $timestamp, $nonce);  
  69.         sort($tmpArr);  
  70.         $tmpStr = implode( $tmpArr );  
  71.         $tmpStr = sha1( $tmpStr );  
  72.           
  73.         if( $tmpStr == $signature ){  
  74.             return true;  
  75.         }else{  
  76.             return false;  
  77.         }  
  78.     }  
  79. }  
  80.   
  81. ?>  
复制代码

  很简单吧,以至于被一些人说写的很水,但是我觉得,这份php源码还是很有含金量的;网上一些哥们还抱怨啥啥的直接部署样例php不能运行啥的,拜托,有点职业精神好不好,连我这个之前完全不会php的人都能看出来要调用里面的responseMsg( )方法才能实现自动回复,样例里面的只是调用了微信接入的认证功能的函数。
上面的代码写的很好,不需要我多解释,相信大家能看出来它是怎么工作的。
设计与实现
  接着就可以实现我们自己的应用了,首先把我们在SAE上创建的应用通过SVN检出到本地,然后切换到检出的目录,用Django的命令创建一个应用,目录结构如下:
 

  其中,index.wsgi和config.yaml是SAE规定的文件,具体请仔细阅读SAE的文档。
  之后就可以编写我们自己的服务端代码了,大致思想就是:用户A向公众帐号发送一条消息,微信平台会按着公众帐号预先的设置,把用户A的消息内容和一些其他信息(如创建时间等)以xml的形式post到我们预先设置好的url上(这个url的服务端就是我们要写的在SAE上的应用),我们要做的就是每当接受到微信post请求,我们解析微信平台post过来的xml,得到用户A的消息内容,把消息内容以get的形式发送到有道API,获取有道API返回的xml(或json等),解析,之后按微信平台规定的格式构造成一个xml,作为微信平台post请求的结果给其返回,微信平台收到结果后,会把消息自动回复给用户,用户就能收到翻译结果了。
  用一个图表示上述过程如下:
 

源码
  下面贴出逻辑处理部分代码如下(Views.py),各函数功能不言而喻:
  1. [python] view plaincopy
  2. # -*- coding: utf-8 -*-  
  3. from django.http import HttpResponse  
  4. from django.template import RequestContext, Template  
  5. from django.views.decorators.csrf import csrf_exempt  
  6. from django.utils.encoding import smart_str, smart_unicode  
  7.   
  8. import xml.etree.ElementTree as ET  
  9. import urllib,urllib2,time,hashlib  
  10.   
  11. TOKEN = "你设置的Token"  
  12.   
  13. YOUDAO_KEY = 你申请到的有道的Key  
  14. YOUDAO_KEY_FROM = "有道的key-from"  
  15. YOUDAO_DOC_TYPE = "xml"  

  16. @csrf_exempt  
  17. def handleRequest(request):  
  18.     if request.method == 'GET':  
  19.         #response = HttpResponse(request.GET['echostr'],content_type="text/plain")  
  20.         response = HttpResponse(checkSignature(request),content_type="text/plain")  
  21.         return response  
  22.     elif request.method == 'POST':  
  23.         #c = RequestContext(request,{'result':responseMsg(request)})  
  24.         #t = Template('{{result}}')  
  25.         #response = HttpResponse(t.render(c),content_type="application/xml")  
  26.         response = HttpResponse(responseMsg(request),content_type="application/xml")  
  27.         return response  
  28.     else:  
  29.         return None  
  30.   
  31. def checkSignature(request):  
  32.     global TOKEN  
  33.     signature = request.GET.get("signature", None)  
  34.     timestamp = request.GET.get("timestamp", None)  
  35.     nonce = request.GET.get("nonce", None)  
  36.     echoStr = request.GET.get("echostr",None)  
  37.   
  38.     token = TOKEN  
  39.     tmpList = [token,timestamp,nonce]  
  40.     tmpList.sort()  
  41.     tmpstr = "%s%s%s" % tuple(tmpList)  
  42.     tmpstr = hashlib.sha1(tmpstr).hexdigest()  
  43.     if tmpstr == signature:  
  44.         return echoStr  
  45.     else:  
  46.         return None  
  47.   
  48. def responseMsg(request):  
  49.     rawStr = smart_str(request.raw_post_data)  
  50.     #rawStr = smart_str(request.POST['XML'])  
  51.     msg = paraseMsgXml(ET.fromstring(rawStr))  
  52.       
  53.     queryStr = msg.get('Content','You have input nothing~')  
  54.   
  55.     raw_youdaoURL = "http://fanyi.youdao.com/openapi.do?keyfrom=%s&key=%s&type=data&doctype=%s&version=1.1&q=" % (YOUDAO_KEY_FROM,YOUDAO_KEY,YOUDAO_DOC_TYPE)     
  56.     youdaoURL = "%s%s" % (raw_youdaoURL,urllib2.quote(queryStr))  
  57.   
  58.     req = urllib2.Request(url=youdaoURL)  
  59.     result = urllib2.urlopen(req).read()  
  60.   
  61.     replyContent = paraseYouDaoXml(ET.fromstring(result))  
  62.   
  63.     return getReplyXml(msg,replyContent)  
  64.   
  65. def paraseMsgXml(rootElem):  
  66.     msg = {}  
  67.     if rootElem.tag == 'xml':  
  68.         for child in rootElem:  
  69.             msg[child.tag] = smart_str(child.text)  
  70.     return msg  
  71.   
  72. def paraseYouDaoXml(rootElem):  
  73.     replyContent = ''  
  74.     if rootElem.tag == 'youdao-fanyi':  
  75.         for child in rootElem:  
  76.             # 错误码  
  77.             if child.tag == 'errorCode':  
  78.                 if child.text == '20':  
  79.                     return 'too long to translate\n'  
  80.                 elif child.text == '30':  
  81.                     return 'can not be able to translate with effect\n'  
  82.                 elif child.text == '40':  
  83.                     return 'can not be able to support this language\n'  
  84.                 elif child.text == '50':  
  85.                     return 'invalid key\n'  
  86.   
  87.             # 查询字符串  
  88.             elif child.tag == 'query':  
  89.                 replyContent = "%s%s\n" % (replyContent, child.text)  
  90.   
  91.             # 有道翻译  
  92.             elif child.tag == 'translation':   
  93.                 replyContent = '%s%s\n%s\n' % (replyContent, '-' * 3 + u'有道翻译' + '-' * 3, child[0].text)  
  94.   
  95.             # 有道词典-基本词典  
  96.             elif child.tag == 'basic':   
  97.                 replyContent = "%s%s\n" % (replyContent, '-' * 3 + u'基本词典' + '-' * 3)  
  98.                 for c in child:  
  99.                     if c.tag == 'phonetic':  
  100.                         replyContent = '%s%s\n' % (replyContent, c.text)  
  101.                     elif c.tag == 'explains':  
  102.                         for ex in c.findall('ex'):  
  103.                             replyContent = '%s%s\n' % (replyContent, ex.text)  
  104.   
  105.             # 有道词典-网络释义  
  106.             elif child.tag == 'web':   
  107.                 replyContent = "%s%s\n" % (replyContent, '-' * 3 + u'网络释义' + '-' * 3)  
  108.                 for explain in child.findall('explain'):  
  109.                     for key in explain.findall('key'):  
  110.                         replyContent = '%s%s\n' % (replyContent, key.text)  
  111.                     for value in explain.findall('value'):  
  112.                         for ex in value.findall('ex'):  
  113.                             replyContent = '%s%s\n' % (replyContent, ex.text)  
  114.                     replyContent = '%s%s\n' % (replyContent,'--')  
  115.     return replyContent  
  116.   
  117. def getReplyXml(msg,replyContent):  
  118.     extTpl = "<xml><ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName><CreateTime>%s</CreateTime><MsgType><![CDATA[%s]]></MsgType><Content><![CDATA[%s]]></Content><FuncFlag>0</FuncFlag></xml>";  
  119.     extTpl = extTpl % (msg['FromUserName'],msg['ToUserName'],str(int(time.time())),'text',replyContent)  
  120.     return extTpl  
复制代码

  之后通过SVN把项目部署到SAE上,就OK啦~
遇到的问题
  现在网上这种参考的代码还很少,在SAE上部署调试也非常困难,无奈下我自己写了个脚本,模仿微信平台给自己部署在SAE上的服务端POST消息,看返回的结果。如果出现错误,Django都会产生一个优美的错误页面,获取这个错误页面把它写到本地的一个html里面,用浏览器打开就可以知道是什么错误了。
写的过程中还是遇到不少问题的:
1.Django的CSRF错误:
我用的Django 1.4,我尝试了大家说的很多解决办法都会出现403错误,无奈下只能暂时通过修饰符把Django的CSRF暂时禁掉,这个还要以后学Django的深入调研一下;
2.Django的编码错误:
我也尝试了很多方法,但是都不行,主要是中文处理上,遇到了很多麻烦,最终在这里找到了完美的解决方案,用可爱的Django自带的可爱的方法:smart_str、smart_unicode,就能完美处理中文了。
海南网站建设|海南网站制作|海口网站建设|三亚网站建设|儋州网站建设|五指山网站建设|文昌网站建设|琼海网站建设|万宁网站建设|东方网站建设|定安网站建设|屯昌网站建设|澄迈网站建设|临高网站建设|白沙网站建设|昌江网站建设|乐东网站建设|陵水网站建设|保亭网站建设|琼中网站建设|海口精英网|三亚精英网|文昌精英网|琼海精英网|陵水精英网|儋州精英网|万宁精英网|澄迈精英网|海微通
合作伙伴 企业发展 企业文化 联系我们 在线订购 网站地图 返回首页手机版
海口世纪华联科技有限公司2024版权所有 24小时服务热线:13807590485   欢迎来电咨询
地址:海南省.海口市.海甸二东路环惠大厦6楼(南宝路明都大厦107#) 公司电话:0898-31568080 31568060 QQ:85398489
全国合作联盟分布:海南海口 四川成都 湖北武汉 湖南长沙 安徽合肥 广东深圳 山西太原 西藏拉萨
技术服务: E_mail:server@hnzlweb.com 服务订购:E_mail:server@hnzlweb.com 在线客服邀请 琼ICP备10201086号-15
推广关键字:海南网站建设公司,海南网站制作公司,海南网站开发公司,海南微信开发公司,海南微信公众号开发公司,海南微信公众号代运营公司,海南模板网站制作公司,海南网页设计公司,海南网络公司