鱼C论坛

 找回密码
 立即注册
查看: 2354|回复: 1

[技术交流] Python 模拟微信后台 第一弹

[复制链接]
发表于 2017-1-5 23:29:54 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 富友郑鹏展 于 2017-1-5 23:50 编辑

好久没有在鱼C更新技术心得了,最近太懒了! 还是下定决心把自己最近写的项目心得分享出来吧。由于最近公司需要写一个模拟微信后台与客户端来测试公众号在多并发下的性能情况。项目用的是flask来写的。我把它分成3部分,微信后台,微信客户端,当然还有进行测试的公众号端。当然这篇文章不会把它们一次性全部讲出来。所以我打算把它按功能来展开讲,主要也只是讲微信后台。当然我这个项目只是实现了微信的一部分功能而已,想把这个项目的心得写下来也是为了做一次整理。我也不会把整个项目讲出来,会挑里面几个功能来讲。整个项目写下来后,对微信的业务流程有了一定的理解,当然对flask也有了更深入的了解。用flask来写web其实还是蛮爽的。期待小甲鱼的教程。。。。
  好了,开始进入主题吧!那要从哪里开始讲呢? 那就从客户端发送消息给公众号,公众号回复消息给客户端这个功能说起吧!
  其实无论是从客户端发送消息给公众号,还是公众号回复消息给客户端,都会经过微信后台,后台也会做相应的记录,怎么做呢?当然就需要数据库来记录了,这里我用的数据库是python自带的sqlite。为了让我们的程序更加优雅,更加有利于业务逻辑的编写,所以我采用了ORM框架sqlalchemy来作为数据库对象关系映射引擎,什么是ORM呢?简单点说其实就是用比较优雅的API来写数据库语句,它的底层还是SQL语句,其实就是加了一层语法糖。好了,接下来我需要编写数据库表结构,来存放消息数据。这里需要两个文件,一个database.py,一个models.py。models.py文件就是我们设计的表结构文件,database.py文件是数据库初始化文件。


  下图是models.py文件
  1. from sqlalchemy import Column, Integer, String
  2. from database import Base
  3. # 消息表
  4. class Msg(Base):
  5.     __tablename__ = 'message'
  6.     MsgId = Column(String(10000), primary_key=True)
  7.     ToUserName = Column(String(10000), unique=False, nullable=False, index=True)
  8.     FromUserName = Column(String(10000), unique=False, nullable=False, index=True)
  9.     CreateTime = Column(Integer, unique=False, nullable=False, index=True)
  10.     Content = Column(String(10000), unique=False, nullable=False, index=True)
  11.     MsgType = Column(String(10000), unique=False, nullable=False, index=True)

  12.     def __init__(self, ToUserName=None, FromUserName=None, CreateTime=None, Content=None, MsgId=None, MsgType=None):
  13.         self.ToUserName = ToUserName
  14.         self.FromUserName = FromUserName
  15.         self.CreateTime = CreateTime
  16.         self.Content = Content
  17.         self.MsgId = MsgId
  18.         self.MsgType = MsgType

  19.     def __repr__(self):
  20.         return "{'ToUserName': %s, 'FromUserName': %s, 'CreateTime': %s, 'Content': %s, 'MsgId': %s, 'MsgType': %s}" % (self.ToUserName, self.FromUserName, self.CreateTime, self.Content, self.MsgId, self.MsgType)
复制代码

  下图是database.py文件
  1. # coding=utf-8

  2. from sqlalchemy import create_engine
  3. from sqlalchemy.orm import scoped_session, sessionmaker
  4. from sqlalchemy.ext.declarative import declarative_base

  5. engine = create_engine(r'sqlite:///test.db', convert_unicode=True)
  6. db_session = scoped_session(sessionmaker(autocommit=False,
  7.                                          autoflush=True,
  8.                                          bind=engine))
  9. Base = declarative_base()
  10. Base.query = db_session.query_property()

  11. def init_db():
  12.     # 在这里导入定义模型所需要的所有模块,这样它们就会正确的注册在
  13.     # 元数据上。否则你就必须在调用 init_db() 之前导入它们。
  14.     import models
  15.     Base.metadata.create_all(bind=engine)
复制代码

  有了数据库的表结构,下面我们就可以写业务逻辑了。先来看后台的代码
  1. # coding=utf-8

  2. import time
  3. import requests
  4. import random
  5. import hashlib
  6. import re
  7. from lxml import etree
  8. from flask import Flask, request, jsonify, send_file, send_from_directory, render_template
  9. from database import init_db, db_session
  10. from models import *
  11. from sqlalchemy import and_
  12. from datetime import datetime
  13. from refresh import Reflesh_DataBase
  14. import urlparse
  15. import ConfigParser
  16. import qrcode
  17. import uuid
  18. import os
  19. import sys
  20. import json
  21. from gevent.wsgi import WSGIServer
  22. from gevent import monkey
  23. monkey.patch_all()
  24. reload(sys)
  25. sys.setdefaultencoding('utf-8')

  26. app = Flask(__name__)
  27. work_path = os.getcwd()
  28. f = ConfigParser.ConfigParser()
  29. f.readfp(open(work_path + '\\TestModle.ini'))
  30. Mode = f.get('Default', 'OAuthTestMode')
  31. ip = socket.gethostbyname(socket.gethostname())
  32. # 微信后台类
  33. class WechatServer:

  34.     # 初始化
  35.     def __init__(self, url='http://localhost:90'):
  36.         self.url = url
  37.     # 发送相应请求到指定服务器
  38.     def send_data(self, data=None, method=None):
  39.         if method == "GET":
  40.             return requests.get(self.url, params=data)
  41.         elif method == "POST":
  42.             return requests.post(self.url, data=data)
  43.         else:
  44.             raise Exception(u'Error: the method must be given')

  45. # 接收客户端的文本消息,再将消息推送到公众号
  46. @app.route('/receive/message', methods=['POST'])
  47. def receive_message():
  48.     # 接收POST请求的表单数据
  49.     data = request.form
  50.     if not data:
  51.         return jsonify({"errcode": 43002, "errmsg": "require POST request"})
  52.     ToUserName = data.get('ToUserName', '')
  53.     FromUserName = data.get('FromUserName', '')
  54.     CreateTime = int(data.get('CreateTime', ''))
  55.     Content = data.get('Content', '')
  56.     MsgId = data.get('MsgId', '')
  57.     MsgType = data.get('MsgType', '')
  58.     m = Msg(ToUserName=ToUserName, FromUserName=FromUserName, CreateTime=CreateTime, Content=Content, MsgId=MsgId, MsgType=MsgType)
  59.     # 把数据插入到消息表中
  60.     init_db()
  61.     db_session.add(m)
  62.     db_session.commit()
  63.     # 获取公众号url
  64.     url = db_session.query(Public_Num).filter_by(appnum=ToUserName).first().appurl
  65.     xml = '<xml>\n<ToUserName><![CDATA[{a}]]></ToUserName>\n<FromUserName><![CDATA[{b}]]></FromUserName> \n<CreateTime>{c}</CreateTime>\n<Content><![CDATA[{d}]]></Content>\n<MsgId><![CDATA[{e}]]></MsgId>\n<MsgType>{f}</MsgType>\n</xml>'.format(a=ToUserName, b=FromUserName, c=CreateTime, d=Content, e=MsgId, f=MsgType)
  66.    # 向公众号推送XML数据
  67.     WechatServer(url=url).send_data(data=xml, method='POST')
  68.     return jsonify({'success': True})

  69. # 接收公众号服务器发来的xml数据并把数据发送到相应的用户
  70. @app.route('/send/message', methods=['POST', 'GET'])
  71. def send_message():
  72.     ToUserName, FromUserName, CreateTime, Content, MsgType = '', '', '', '', ''
  73.     if request.method == 'POST':
  74.         data = request.form
  75.         if not data:
  76.             return jsonify({"errcode": 43002, "errmsg": "require POST request"})
  77.         ToUserName = data.get('ToUserName', '')
  78.         FromUserName = data.get('FromUserName', '')
  79.         CreateTime = int(data.get('CreateTime', ''))
  80.         Content = data.get('Content', '')
  81.         MsgType = data.get('MsgType', '')
  82.     elif request.method == 'GET':
  83.         qs = request.query_string
  84.         if not qs:
  85.             return jsonify({"errcode": 43001, "errmsg": "require GET request"})
  86.         data = urlparse.parse_qs(qs)
  87.         ToUserName = data.get('ToUserName', '')[0]
  88.         FromUserName = data.get('FromUserName', '')[0]
  89.         CreateTime = int(data.get('CreateTime', '')[0])
  90.         Content = data.get('Content', '')[0]
  91.         MsgType = data.get('MsgType', '')[0]
  92.     m = Msg(ToUserName=ToUserName, FromUserName=FromUserName, CreateTime=CreateTime, Content=Content, MsgType=MsgType)
  93.     # 把数据插入到消息表中
  94.     init_db()
  95.     db_session.add(m)
  96.     db_session.commit()
  97.    # 把公众号回复的消息发送给客户端
  98.     WechatServer(url='http://localhost:93/receivepublicinfo').send_data(data={'Content': Content, 'CreateTime': CreateTime, 'ToUserName': ToUserName, 'FromUserName': FromUserName, 'MsgType': MsgType}, method='POST')
  99.     return jsonify({'success': True})
  100. if __name__ == '__main__':
  101.     http_server = WSGIServer((ip, 90), app)
  102.     http_server.serve_forever()
复制代码


  好了,今天先讲这些吧。如果有什么错误,请指出来!谢谢。有什么问题也可以提出来我们一起探讨!
  
  
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2017-1-6 12:42:43 | 显示全部楼层
谢谢大神分享!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-26 20:08

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表