微信公眾開發——實現功能

來源:互聯網
上載者:User

承啟

因為之前設計了要實現一個機器人,以向使用者響應響應的文章,這個app十分地簡單,並不需要特別深入的設計,而且我的想法是:拿來主義, 反正github上面那麼多的用python寫的部落格系統,我只需要實現響應的部分,也就是從資料庫中擷取文章的資料,然後將文章的標題,url,圖片等資訊打包成xml格式返回給伺服器,伺服器再返回給使用者。而且我發現,有菜單的會好很多,就像一個完整的app,可以直接點擊察看某篇文章,而不是硬邦邦的回複。我是用別人寫的一個部落格系統進行改造——saepy-log.而這個部落格系統又是基於tornado架構的,本來不打算染指tornado的,但是不得不硬著頭皮鑽研。其中遇到了很多困難,在sql語句的like寫法,察看文檔方面有了比較大的收穫。


部署與開發

事先說明,由於我是各種折騰,所以可能照本篇文章做是做不成的。下載了saepy-log的源碼後,按照這裡的操作進行上傳後,就可以將部落格系統安裝在sae平台上了,然後用svn把代碼同步下來到本地工作目錄,一切準備就緒。我們會看到我們的部落格已經可以訪問了,而且我們本地還有部落格的所有代碼:

650) this.width=650;" src="http://www.bkjia.com/uploads/allimg/131228/1143303626-0.png" />


我們要修改的是blog.py是部落格的核心功能所在,還有modle.py是資料模型的關鍵所在,我們將要擴充資料模型功能,使之完成我們的功能。


在blog.py裡面添加我們的功能類 weixin.py(由於是用tornado架構,所以方法與在django裡面略有不同):

匯入需要用到的包

# weixin used packageimport xml.etree.ElementTree as ETimport urllib,urllib2,time,hashlib                                              import tornado.wsgiimport tornado.escape

主要是xml的解析和一些處理字串的包,接下來我們定義weixin類的主體:

# 添加推送帳號class WeiXinPoster(BaseHandler):    #-----------------------------------------------------------------------    # 處理get方法 對應check_signature    def get(self):        global TOKEN        signature = self.get_argument("signature")        timestamp = self.get_argument("timestamp")        nonce = self.get_argument("nonce")        echoStr = self.get_argument("echostr")        token = TOKEN        tmpList = [token,timestamp,nonce]        tmpList.sort()        tmpstr = "%s%s%s" % tuple(tmpList)        tmpstr = hashlib.sha1(tmpstr).hexdigest()                                                     if tmpstr == signature:            self.write(echoStr)            #return echoStr        else:            self.write(None);            #return None                                                             # 處理post方法,對應response_msg    def post(self):        global SORRY        # 從request中擷取請求文本        rawStr = self.request.body        # 將文本進行解析,得到請求的資料        msg = self.parse_request_xml(ET.fromstring(rawStr))        # 根據請求訊息來處理內容返回        query_str = msg.get("Content")        query_str = tornado.escape.utf8(query_str)        # TODO 使用者發來的資料類型可能多樣,所以需要判別        response_msg = ""        return_data = ""        # 使用簡單的處理邏輯,有待擴充        if query_str[0] == "h":     # send help menu to user            response_msg = self.get_help_menu()     # 返回訊息            # 包括post_msg,和對應的 response_msg            if response_msg:                return_data = self.pack_text_xml(msg, response_msg)            else:                response_msg = SORRY                return_data = self.pack_text_xml(msg, response_msg)            self.write(return_data)        # 分類        elif query_str[0] =="c":            category = query_str[1:]            response_msg = self.get_category_articles(category)            if response_msg:                return_data = self.pack_news_xml(msg, response_msg)            else:                response_msg = SORRY                return_data = self.pack_text_xml(msg, response_msg)            self.write(return_data)        # 列出文章列表         elif query_str[0] =="l":            response_msg = self.get_article_list()            if response_msg:                return_data = self.pack_text_xml(msg, response_msg)            else:                response_msg = SORRY                return_data = self.pack_text_xml(msg, response_msg)            self.write(return_data)        # 直接擷取某篇文章        elif query_str[0] == "a":            # 直接擷取文章的id,然後在資料庫中查詢            article_id = int(query_str[1:])            # 進行操作            response_msg = self.get_response_article_by_id(article_id)            if response_msg:                return_data = self.pack_news_xml(msg, response_msg)            else:                response_msg = SORRY                return_data = self.pack_text_xml(msg, response_msg)            self.write(return_data)                                                                 # 還要考慮其他        elif query_str[0] == "s":            keyword = str(query_str[1:])            # 搜尋關鍵詞,返回相關文章            response_msg = self.get_response_article(keyword)            # 返回圖文資訊            if response_msg:                return_data = self.pack_news_xml(msg, response_msg)            else:                response_msg = SORRY                return_data = self.pack_text_xml(msg, response_msg)            self.write(return_data)                                                                 elif query_str[0] == "n":            response_msg = self.get_latest_articles()            # 返回圖文資訊            if response_msg:                return_data = self.pack_news_xml(msg, response_msg)            else:                response_msg = SORRY                return_data = self.pack_text_xml(msg, response_msg)            self.write(return_data)        # 如果找不到,返回協助資訊        else:            response_msg = get_help_menu()            if response_msg:                return_data = response_msg            else:                return_data = SORRY            self.write(return_data)    # n for 擷取最新的文章    def get_latest_articles(self):        global MAX_ARTICLE        global PIC_URL        article_list = Article.get_articles_by_latest()        article_list_length = len(article_list)        count = (article_list_length < MAX_ARTICLE) and article_list_length or MAX_ARTICLE        if article_list:            # 構造圖文訊息            articles_msg = {'articles':[]}            for i in range(0,count):                article = {                        'title': article_list[i].slug,                        'description':article_list[i].description,                        'picUrl':PIC_URL,                        'url':article_list[i].absolute_url                    }                # 插入文章                articles_msg['articles'].append(article)                article = {}            # 返迴文章            return articles_msg                                                     #-----------------------------------------------------------------------    # 解析請求,拆解到一個字典裡         def parse_request_xml(self,root_elem):        msg = {}        if root_elem.tag == 'xml':            for child in root_elem:                msg[child.tag] = child.text  # 獲得內容            return msg                                                 #-----------------------------------------------------------------------    def get_help_menu(self):        menu_msg = '''歡迎關注南苑隨筆,在這裡你能獲得關於校園的資訊和故事。回複如下按鍵則可以完成得到相應的回應        h :協助(help)        l :文章列表(article list)        f : 獲得分類列表        n : 擷取最新文章        a + 數字 :察看某篇文章 a2 察看第2篇文章        s + 關鍵字 : 搜尋相關文章 s科研 察看科研相關        c + 分類名 : 擷取分類文章 c校園生活 察看校園生活分類        其他 : 功能有待豐富'''        return menu_msg    #-----------------------------------------------------------------------    # 擷取文章列表    def get_article_list(self):        # 查詢資料庫擷取文章列表        article_list = Article.get_all_article_list()        article_list_str = "最新文章列表供您點閱,回複a+數字即可閱讀: \n"        for i in range(len(article_list)):            art_id = str(article_list[i].id)            art_id = tornado.escape.native_str(art_id)                                                                     art_title = article_list[i].title            art_title = tornado.escape.native_str(art_title)                                                                     art_category = article_list[i].category            art_category = tornado.escape.native_str(art_category)                                                                                                                              article_list_str +=  art_id + ' ' + art_title + ' ' + art_category + '\n'        return article_list_str                                                         # 按照分類尋找    def get_category_articles(self, category):        global MAX_ARTICLE        global PIC_URL        article_list = Article.get_articles_by_category(category)        article_list_length = len(article_list)        count = (article_list_length < MAX_ARTICLE) and article_list_length or MAX_ARTICLE        if article_list:            # 構造圖文訊息            articles_msg = {'articles':[]}            for i in range(0,count):                article = {                        'title': article_list[i].slug,                        'description':article_list[i].description,                        'picUrl':PIC_URL,                        'url':article_list[i].absolute_url                    }                # 插入文章                articles_msg['articles'].append(article)                article = {}            # 返迴文章            return articles_msg    #-----------------------------------------------------------------------    # 擷取用於返回的msg    def get_response_article(self, keyword):        global PIC_URL        keyword = str(keyword)        # 從資料庫查詢得到若干文章        article = Article.get_article_by_keyword(keyword)        # 這裡先用測試資料        if article:            title = article.slug            description = article.description            picUrl = PIC_URL            url = article.absolute_url            count = 1            # 也有可能是若干篇            # 這裡實現相關邏輯,從資料庫中擷取內容                                                                     # 構造圖文訊息            articles_msg = {'articles':[]}            for i in range(0,count):                article = {                        'title':title,                        'description':description,                        'picUrl':picUrl,                        'url':url                    }                # 插入文章                articles_msg['articles'].append(article)                article = {}            # 返迴文章            return articles_msg        else:            return                                                     def get_response_article_by_id(self, post_id):        global PIC_URL        # 從資料庫查詢得到若干文章        article = Article.get_article_by_id_detail(post_id)        # postId為文章id        if article:            title = article.slug            description = article.description            picUrl = PIC_URL            url = article.absolute_url            count = 1            # 這裡實現相關邏輯,從資料庫中擷取內容                                                                     # 構造圖文訊息            articles_msg = {'articles':[]}            for i in range(0,count):                article = {                        'title':title,                        'description':description,                        'picUrl':picUrl,                        'url':url                    }                # 插入文章                articles_msg['articles'].append(article)                article = {}            # 返迴文章            return articles_msg        else:            return


可見app的難度並不大,還是和上次使用API裡面的和接近,需要用到的幾個全域變數,需要自己定義,如token,如PIC_URL等。程式原理其實就是解析使用者請求,h開頭,則提供協助菜單,a開頭加數位,就提供某篇文章,等等,然後提供相應的函數進行處理,這裡面說明起來比較複雜,就以擷取分類文章來講吧:

需要用到分析使用者請求字串:

# 分類        elif query_str[0] =="c":            category = query_str[1:]            response_msg = self.get_category_articles(category)            if response_msg:                return_data = self.pack_news_xml(msg, response_msg)            else:                response_msg = SORRY                return_data = self.pack_text_xml(msg, response_msg)            self.write(return_data)

這裡要提供get_category_articles(category)的功能,所以需要在weixin類裡面實現一個這樣的函數:

# 按照分類尋找    def get_category_articles(self, category):        global MAX_ARTICLE        global PIC_URL        article_list = Article.get_articles_by_category(category)        article_list_length = len(article_list)        count = (article_list_length < MAX_ARTICLE) and article_list_length or MAX_ARTICLE        if article_list:            # 構造圖文訊息            articles_msg = {'articles':[]}            for i in range(0,count):                article = {                        'title': article_list[i].slug,                        'description':article_list[i].description,                        'picUrl':PIC_URL,                        'url':article_list[i].absolute_url                    }                # 插入文章                articles_msg['articles'].append(article)                article = {}            # 返迴文章            return articles_msg

很顯然,這裡需要和資料庫模型Article打交道,看看Article是否實現了該功能,很遺憾沒有,那麼我們只好捲起袖子自己幹——擴充Article,所以我們轉戰到modle.py檔案裡面了,寫下了如下的代碼:

# 返回一個包含若干篇文章的數組 limit 5    def get_articles_by_category(self, category):        sdb._ensure_connected()        article_list = sdb.query('SELECT * FROM `sp_posts` WHERE `category` = %s LIMIT 5', str(category))        for i in range(len(article_list)):            article_list[i] = post_detail_formate(article_list[i])        return article_list

這裡進行了資料庫查詢,將category參數傳遞進去,選擇category為參數的5篇文章,打包返回。post_detail_formate是部落格系統已經寫好的,我們只要拿來用就行了。在python裡面,寫sql語句要很小心,遇到要傳入參數的,最好是用逗號分割開,而不是使用%來填充參數。特別是使用like的時候,我們往往需要寫這樣的sql語句:

SELECT * FROM `sp_posts` WHERE `category` LIKE '%study%'

但是python裡面又是用%s做參數預留位置的,故而會引起很多不必要的錯誤,比如這裡。總之要安全使用,最好是作為參數傳遞,python會為他們與原字串裡的%等分開。


上傳發布

完成功能後就發行就緒了,雖然還有很多不足的地方,但是測試發現,還是可以使用的。本項目將開源到github中,歡迎使用需要修改部分的參數,例如部落格名稱等),或者交流研究原始碼。實現後的功能是這樣字的:

650) this.width=650;" src="http://www.bkjia.com/uploads/allimg/131228/1143306437-1.jpg" />650) this.width=650;" src="http://www.bkjia.com/uploads/allimg/131228/1143306028-2.jpg" />

比如回複一個a9,將返回第9篇文章;回複l,列出所有文章。吐槽一下我們宿舍的網路,差到離譜,我是擷取資訊之後才截屏的。


該篇並不是要將實現的所有細節披露出來,披露細節最好的方法就是公開原始碼,接下來會發布到github上面去,只需稍微配置,你就可以擁有一個可以推送到的部落格了我不會告訴你wordpress早就出外掛程式了)。經過這一番折騰,可以得出一個第三方開發的經驗,那就是初步制定好自己的app需要提供什麼功能,然後接著設計一個類,處理各種的請求,將各種處理部分封裝起來,例如按照分類擷取文章,或者擷取文章列表等,然後在這裡面調用資料庫模型裡面的功能來實現。這是MVC的一種便利之處。此外,還要勇於閱讀原始碼和英文文檔,我之前google了很多字串轉換編碼的方法,沒有成功,但是閱讀了tornado的文檔之後,我恍然大悟。例如之前在django裡面寫這個應用的時候,我們擷取的還是request.raw_post_data,現在改成了self.request.body,不看文檔,想破頭也想不出來。功能實現雖然告一段落,但是這個項目還有很多需要重寫一次,使得它的表現更好,安全性更高。這裡不得不提一下,伺服器中採用tornado真的不是一般快~


END

by bibodeng 2013-05-08




本文出自 “ugeek bibodeng” 部落格,請務必保留此出處http://bibodeng.blog.51cto.com/2646046/1195719

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.