python對(duì)接公眾號(hào)接口

昨晚下班回到家倘核,打算把前不久做的圖片動(dòng)漫化的功能集成到微信公眾號(hào)陕见,效果是秘血,用戶發(fā)送一張圖片到公眾號(hào),那么公眾號(hào)就會(huì)回復(fù)一張動(dòng)漫化的圖片回去评甜。

距離上次開發(fā)公眾號(hào)相關(guān)的接口灰粮,已經(jīng)過去很久了,也有點(diǎn)生疏了忍坷,但是還是記得相關(guān)的一些模糊細(xì)節(jié)粘舟,需要處理微信回調(diào)發(fā)送的信息,涉及到了加解密佩研,還有主動(dòng)調(diào)用微信接口柑肴,需要根據(jù)app id和app secret獲取access token等等,由于打算快速開發(fā)旬薯,所以采用python來做相關(guān)的接口集成開發(fā)晰骑,用java的話沒有python這么敏捷和快速。

于是網(wǎng)上找了一下有沒有相關(guān)的庫(kù)绊序,總不能自己重新對(duì)著微信公眾號(hào)文檔造輪子吧些侍,這樣效率太低了,經(jīng)過一番搜索政模,找到一個(gè)還不錯(cuò)的框架wechatpy

安裝非常簡(jiǎn)單岗宣,使用pip即可

pip install wechatpy
# with cryptography (推薦)
pip install wechatpy[cryptography]# with pycryptodome
pip install wechatpy[pycrypto]

安裝完畢之后,需要選擇一個(gè)web框架淋样,從輕量級(jí)和快速角度考慮耗式,選擇了flask框架來做web開發(fā)
安裝如下:

pip install Flask

一個(gè)簡(jiǎn)單上手flask的示例


from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8000, debug=True)

運(yùn)行之后,打開瀏覽器127.0.0.1:8000即可看到hello world!非常簡(jiǎn)單

那接下來繼續(xù)刊咳,打開公眾號(hào)管理后臺(tái)彪见,開發(fā)->基本配置,服務(wù)器配置欄娱挨,需要填寫微信回調(diào)的url地址
然后設(shè)置令牌和消息加解密模式余指,微信會(huì)回調(diào)這個(gè)url接口,確認(rèn)對(duì)接成功跷坝,那接下來我們就用wechatpy來處理這段邏輯

定義微信的回調(diào)url處理方法酵镜,從請(qǐng)求參數(shù)獲取timestamp和nonce參數(shù),如果是get請(qǐng)求柴钻,則進(jìn)一步獲取echostr和signature參數(shù)淮韭,然后調(diào)用check_signature校驗(yàn)是否是來自微信的請(qǐng)求,如果不是則拒絕贴届,如果是則回復(fù)echostr靠粪,告知微信,這邊已經(jīng)對(duì)接好了

from wechatpy.utils import check_signature
@app.route('/wechat', methods=['GET', 'POST'])
def wechat():
    timestamp = request.args.get("timestamp")
    nonce = request.args.get("nonce")
    if request.method == 'GET':
        # token, signature, timestamp, nonce
        echostr = request.args.get("echostr")
        signature = request.args.get("signature")
        if echostr:
            try:
                check_signature(const.token, signature, timestamp, nonce)
                return echostr
            except InvalidSignatureException:
                logging.error("invalid message from request")

注意到這里只處理了GET請(qǐng)求毫蚓,只有GET請(qǐng)求是不帶任何request body的占键,GET請(qǐng)求是微信發(fā)送的Token驗(yàn)證
還有一種是POST請(qǐng)求,這個(gè)請(qǐng)求微信會(huì)攜帶一些消息體過來元潘,主要是XML畔乙,這些包含了比如圖片消息,文本消息柬批,或者一些事件等等,具體可以查看微信公眾號(hào)的相關(guān)文檔
這里處理的是被動(dòng)請(qǐng)求袖订,也就是微信調(diào)用我們的服務(wù)器氮帐。
我們調(diào)用微信服務(wù)器接口屬于主動(dòng)請(qǐng)求,需要access token

繼續(xù)完善這個(gè)wechat方法

from wechatpy.utils import check_signature
from wechatpy import parse_message
from wechatpy.crypto import WeChatCrypto
from wechatpy.exceptions import InvalidSignatureException, InvalidAppIdException
from wechatpy.replies import ImageReply
#....省略代碼
@app.route('/', methods=['GET', 'POST'])
def wechat():
    timestamp = request.args.get("timestamp")
    nonce = request.args.get("nonce")
    if request.method == 'GET':
      #....省略代碼
    else:
        xml = request.data
        if xml:
            msg_signature = request.args.get("msg_signature")
            crypto = WeChatCrypto(const.token, const.encodeing_aes_key,
                                  const.app_id)
            try:
                decrypted_xml = crypto.decrypt_message(
                    xml,
                    msg_signature,
                    timestamp,
                    nonce
                )
                msg = parse_message(decrypted_xml)
                logging.info("message from wechat %s " % msg)
            except (InvalidAppIdException, InvalidSignatureException):
                logging.error("cannot decrypt message!")
        else:
            logging.error("no xml body, invalid request!")
    return ""

在else分支處理post請(qǐng)求洛姑,注意最上面route我們配置了僅支持get和post請(qǐng)求上沐,那么else分支一定是post方法
通過request.data獲取xml數(shù)據(jù),這里注意判斷一下是否有數(shù)據(jù)楞艾,如果沒有數(shù)據(jù)的情況下参咙,那么直接打印日志no xml body
然后返回即可。

如果有xml數(shù)據(jù)硫眯,那么我們從請(qǐng)求里面獲取msg_signature蕴侧,然后解密消息,這個(gè)取決于你的微信公眾號(hào)的配置两入,如果無(wú)所謂安全净宵,可以直接明文傳輸,那樣就省去了解密消息的步驟。

后面把消息解出來之后择葡,然后打印消息內(nèi)容
放到服務(wù)器上調(diào)試一下紧武,往公眾號(hào)發(fā)送文本消息你好,消息顯示如下:

2021-03-10 04:00:10,543:INFO:message from wechat TextMessage(OrderedDict([('ToUserName', 'gh_292a97797f58'), ('FromUserName', 'oPXMnwgqQBKAz23ovLYhca6KtIMQ'), ('CreateTime', '1615348809'), ('MsgType', 'text'), ('Content', '你好'), ('MsgId', '23126237789150234')]))

文章一開始提到要做的內(nèi)容是當(dāng)用戶發(fā)送一張圖片到公眾號(hào)敏储,那么公眾號(hào)就會(huì)回復(fù)一張動(dòng)漫化的圖片回去阻星。

那么需要處理的消息是圖片類型的消息,而不是文本消息已添,發(fā)送一張圖片到公眾號(hào)試試妥箕,發(fā)現(xiàn)打印消息如下:

2021-03-10 13:08:47,296:INFO:message from wechat ImageMessage(OrderedDict([('ToUserName', 'gh_292a97797f58'), ('FromUserName', 'oPXMnwgkC5M-DKNfigblDIXY3Irw'), ('CreateTime', '1615381726'), ('MsgType', 'image'), ('PicUrl', 'http://mmbiz.qpic.cn/sz_mmbiz_jpg/------'), ('MsgId', '23126702559805937'), ('MediaId', 'UdcKE2XkjLTQcJmRQulERp2GZeTZ5FncAht5XjrKU3Yh1sTucAaQ0E8-------')]))

可以看到圖片類型的消息的type是image類型,再結(jié)合wechatpy的文檔,繼續(xù)編寫代碼

圖片消息
classwechatpy.messages.ImageMessage(message)[源代碼]
圖片消息 詳情請(qǐng)參閱 http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html

ImageMessage 的屬性:

name value
type image
image 圖片的 URL 地址

#.....省去代碼
msg = parse_message(decrypted_xml)
logging.info("message from wechat %s " % msg)
if msg.type == "image":
    logging.info("image type message url %s" % msg.image)
    cartoon_file = utils.cartoon_image(msg.image)
    media_id = utils.upload_image(cartoon_file)
    if media_id is not None:
        reply = ImageReply(message=msg)
        reply.media_id = media_id
        xml = reply.render()
        return xml

當(dāng)msg.type是image的時(shí)候酝碳,打印image的url地址矾踱,然后調(diào)用封裝好的卡通化image的方法 cartoon_image,具體如何把圖片卡通化的方法請(qǐng)參考筆者前面的文章疏哗,這里就不重復(fù)了呛讲。

然后上傳卡通過后的圖片到微信服務(wù)器,獲取media_id,這里構(gòu)建一個(gè)wechatclient返奉,傳入你的appid和appsecret即可

from wechatpy import WeChatClient
from wechatpy.client.api import WeChatMedia

client = WeChatClient(const.app_id, const.app_secret)

def upload_image(file_path):
    res = WeChatMedia(client).upload("image", open(file_path, "rb"))
    if "media_id" in res:
        return res['media_id']
    logging.error("upload image %s" % res)
    return None

拿到media_id之后贝搁,就可以回復(fù)圖片給用戶了。
最后上一個(gè)效果圖


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芽偏,一起剝皮案震驚了整個(gè)濱河市雷逆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌污尉,老刑警劉巖膀哲,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異被碗,居然都是意外死亡某宪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門锐朴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兴喂,“玉大人,你說我怎么就攤上這事焚志∫旅裕” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵酱酬,是天一觀的道長(zhǎng)壶谒。 經(jīng)常有香客問我,道長(zhǎng)膳沽,這世上最難降的妖魔是什么佃迄? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任泼差,我火速辦了婚禮,結(jié)果婚禮上呵俏,老公的妹妹穿的比我還像新娘堆缘。我一直安慰自己,他們只是感情好普碎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布吼肥。 她就那樣靜靜地躺著,像睡著了一般麻车。 火紅的嫁衣襯著肌膚如雪缀皱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天动猬,我揣著相機(jī)與錄音啤斗,去河邊找鬼。 笑死赁咙,一個(gè)胖子當(dāng)著我的面吹牛钮莲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播彼水,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼崔拥,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了凤覆?” 一聲冷哼從身側(cè)響起链瓦,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盯桦,沒想到半個(gè)月后慈俯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拥峦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年贴膘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片事镣。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡步鉴,死狀恐怖揪胃,靈堂內(nèi)的尸體忽然破棺而出璃哟,到底是詐尸還是另有隱情,我是刑警寧澤喊递,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布随闪,位于F島的核電站,受9級(jí)特大地震影響骚勘,放射性物質(zhì)發(fā)生泄漏铐伴。R本人自食惡果不足惜撮奏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望当宴。 院中可真熱鬧畜吊,春花似錦、人聲如沸户矢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)梯浪。三九已至捌年,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間挂洛,已是汗流浹背礼预。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虏劲,地道東北人托酸。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像伙单,于是被迫代替她去往敵國(guó)和親获高。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容