官方文档 (由于本人公众号权限的原因,这一节的大部分例子都没有测试)

注意:

1、对于认证订阅号,群发接口每天可成功调用1次,此次群发可选择发送给全部用户或某个标签;
2、对于认证服务号虽然开发者使用高级群发接口的每日调用限制为100次,但是用户每月只能接收4条,无论在公众平台网站上,还是使用接口群发,用户每月只能接收4条群发消息,多于4条的群发将对该用户发送失败;
3、开发者可以使用预览接口校对消息样式和排版,通过预览接口可发送编辑好的消息给指定用户校验效果;
4、群发过程中,微信后台会自动进行图文消息原创校验,请提前设置好相关参数(send_ignore等);
5、开发者可以主动设置 clientmsgid 来避免重复推送。
6、群发接口每分钟限制请求60次,超过限制的请求会被拒绝。
7、图文消息正文中插入自己帐号和其他公众号已群发文章链接的能力

关于群发时使用is_to_all为true使其进入公众号在微信客户端的历史消息列表:
1、使用is_to_all为true且成功群发,会使得此次群发进入历史消息列表。
2、为防止异常,认证订阅号在一天内,只能使用is_to_all为true进行群发一次,或者在公众平台官网群发(不管本次群发是对全体还是对某个分组)一次。
以避免一天内有2条群发进入历史消息列表。
3、类似地,服务号在一个月内,使用is_to_all为true群发的次数,加上公众平台官网群发(不管本次群发是对全体还是对某个分组)的次数,最多只能是4次。
4、设置is_to_all为false时是可以多次群发的,但每个用户只会收到最多4条,且这些群发不会进入历史消息列表。

另外,请开发者注意,本接口中所有使用到media_id的地方,现在都可以使用素材管理中的永久素材media_id了。请但注意,使用同一个素材群发出去的链接是一样的,
这意味着,删除某一次群发,会导致整个链接失效。

群发图文消息:
  1、首先,预先将图文消息中需要用到的图片,使用上传图文消息内图片接口,上传成功并获得图片 URL;
def upload_newsurl(self,access_token,picfile):
        upload_url = 'https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=%s'% access_token
        postData ={
            'media':open(picfile,'rb')
        }
        response = requests.post(upload_url,files=postData)
        return json.loads(response)['url']

  2.上传图文消息素材,需要用到图片时,请使用上一步获取的图片 URL;

如果需要在群发图文中插入小程序,则在调用上传图文消息素材接口时,需在content字段中添加小程序跳转链接,有以下三种样式的可供选择。

小程序卡片跳转小程序,代码示例:


<mp-miniprogram data-miniprogram-appid="wx123123123" data-miniprogram-path="pages/index/index" data-miniprogram-title="小程序示例" data-progarm-imageurl="http://mmbizqbic.cn/demo.jpg"></mp-miniprogram>


文字跳转小程序,代码示例:


<p><a data-miniprogram-appid="wx123123123" data-miniprogram-path="pages/index" href="">点击文字跳转小程序</a></p>


图片跳转小程序,代码示例:


<p><a data-miniprogram-appid="wx123123123" data-miniprogram-path="pages/index" href=""><img src="http://mmbiz.qpic.cn/mmbiz_jpg/demo/0?wx_fmt=jpg" alt="" data-width="null" data-ratio="NaN"></a></p>


3. 根据标签进行群发:
def send_massmsg(access_token,postData):
    send_url ='https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=%s'% access_token
    response = requests.post(send_url,json.dumps(postData))
    return json.loads(response.text)

mpnews

{
   "filter":{
      "is_to_all":false,
      "tag_id":2
   },
   "mpnews":{
      "media_id":"123dsdajkasd231jhksad"
   },
    "msgtype":"mpnews",
    "send_ignore_reprint":0
}

text

{
   "filter":{
      "is_to_all":false,
      "tag_id":2
   },
   "text":{
      "content":"CONTENT"
   },
    "msgtype":"text"
}

voice

{
   "filter":{
      "is_to_all":false,
      "tag_id":2
   },
   "voice":{
      "media_id":"123dsdajkasd231jhksad"
   },
    "msgtype":"voice"
}

image

{
   "filter":{
      "is_to_all":false,
      "tag_id":2
   },
   "image":{
      "media_id":"123dsdajkasd231jhksad"
   },
    "msgtype":"image"
}

mpvideo

{
   "filter":{
      "is_to_all":false,
      "tag_id":2
   },
   "mpvideo":{
      "media_id":"IhdaAQXuvJtGzwwc0abfXnzeezfO0NgPK6AQYShD8RQYMTtfzbLdBIQkQziv2XJc"
   },
    "msgtype":"mpvideo"
}

卡券消息

{
   "filter":{
    "is_to_all":false,
      "tag_id":"2"
   },
  "wxcard":{              
    "card_id":"123dsdajkasd231jhksad"         
    },
   "msgtype":"wxcard"
}
视频消息的media_id需要通过 先上传视频,再从素材中获取得到的media_id为准:
def get_uploadedvideo(self,access_token,media_id,video_title='',video_intro=''):
        '''为群发视频消息使用
        https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Batch_Sends_and_Originality_Checks.html
        '''
        upload_url = 'https://api.weixin.qq.com/cgi-bin/media/uploadvideo?access_token=%s'% access_token
        postData ={
            'media_id':media_id,
            "title": video_title,
            "description": video_intro
        }
        result = requests.post(upload_url,json.dumps(postData,ensure_ascii=False).encode('utf-8'))
        return json.loads(result.text)

返回数据示例(正确时的JSON返回结果):

{
   "errcode":0,
   "errmsg":"send job submission success",
   "msg_id":34182, 
   "msg_data_id": 206227730 #图文消息专有
}
预览消息:
POST数据说明

POST数据示例如下:

图文消息(其中media_id与根据分组群发中的media_id相同):

{
   "touser":"OPENID", 
   "mpnews":{              
     "media_id":"123dsdajkasd231jhksad"               
    },
   "msgtype":"mpnews" 
}
文本:

{     
    "touser":"OPENID",
    "text":{           
      "content":"CONTENT"            
    },     
    "msgtype":"text"
}
语音(其中media_id与根据分组群发中的media_id相同):

{
    "touser":"OPENID",
    "voice":{              
     "media_id":"123dsdajkasd231jhksad"
    },
    "msgtype":"voice" 
}
图片(其中media_id与根据分组群发中的media_id相同):

{
    "touser":"OPENID",
    "image":{      
     "media_id":"123dsdajkasd231jhksad"
    },
    "msgtype":"image" 
}
视频(其中media_id与根据分组群发中的media_id相同):

{
    "touser":"OPENID",
    "mpvideo":{  "media_id":"IhdaAQXuvJtGzwwc0abfXnzeezfO0NgPK6AQYShD8RQYMTtfzbLdBIQkQziv2XJc",   
   },
    "msgtype":"mpvideo" 
}
卡券:

{ "touser":"OPENID", 
  "wxcard":{              
           "card_id":"123dsdajkasd231jhksad",
            "card_ext": "{"code":"","openid":"","timestamp":"1402057159","signature":"017bb17407c8e0058a66d72dcc61632b70f511ad"}"               
            }, 
  "msgtype":"wxcard" 
}
请注意,上述JSON数据中的touser字段都可以改为towxname,这样就可以针对微信号进行预览(而非openID),towxname和touser同时赋值时,以towxname优先。修改后JSON数据如下(以图文消息为例): 图文消息:

{
   "towxname":"示例的微信号", 
   "mpnews":{              
            "media_id":"123dsdajkasd231jhksad"               
             },
   "msgtype":"mpnews" 
}
def preview_massmsg(access_token,postData):
    '''预览接口'''
    send_url = 'https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=%s' % access_token
    response = requests.post(send_url, json.dumps(postData))
    return json.loads(response.text)

 

删除消息:
def del_massmsg(access_token,msg_id, article_idx=0):
    '''只能删除图文和视频消息.收到消息的用户点击时会显示已被删除'''
    del_url = 'https://api.weixin.qq.com/cgi-bin/message/mass/delete?access_token=%s'% access_token
    postData={
        "msg_id": msg_id,
        "article_idx": article_idx
    }
    response = requests.post(del_url, json.dumps(postData))
    return json.loads(response.text)

查询群发消息发送状态

def get_massmsg_status(access_token,msg_id):
    '''查看消息状态'''
    send_url = 'https://api.weixin.qq.com/cgi-bin/message/mass/get?access_token=%s' % access_token
    postData = {
   "msg_id": msg_id
}
    response = requests.post(send_url, json.dumps(postData))
    return json.loads(response.text)

 

事件推送群发结果

由于群发任务提交后,群发任务可能在一定时间后才完成,因此,群发接口调用时,仅会给出群发任务是否提交成功的提示,若群发任务提交成功,则在群发任务结束时,会向开发者在公众平台填写的开发者URL(callback URL)推送事件。

需要注意,由于群发任务彻底完成需要较长时间,将会在群发任务即将完成的时候,就推送群发结果,此时的推送人数数据将会与实际情形存在一定误差

推送的XML结构如下(发送成功时),已增加原创校验结果和群发图文的url:

<xml> 
  <ToUserName><![CDATA[gh_4d00ed8d6399]]></ToUserName>  
  <FromUserName><![CDATA[oV5CrjpxgaGXNHIQigzNlgLTnwic]]></FromUserName>  
  <CreateTime>1481013459</CreateTime>  
  <MsgType><![CDATA[event]]></MsgType>  
  <Event><![CDATA[MASSSENDJOBFINISH]]></Event>  
  <MsgID>1000001625</MsgID>  
  <Status><![CDATA[err(30003)]]></Status>  
  <TotalCount>0</TotalCount>  
  <FilterCount>0</FilterCount>  
  <SentCount>0</SentCount>  
  <ErrorCount>0</ErrorCount>  
  <CopyrightCheckResult> 
    <Count>2</Count>  
    <ResultList> 
      <item> 
        <ArticleIdx>1</ArticleIdx>  
        <UserDeclareState>0</UserDeclareState>  
        <AuditState>2</AuditState>  
        <OriginalArticleUrl><![CDATA[Url_1]]></OriginalArticleUrl>  
        <OriginalArticleType>1</OriginalArticleType>  
        <CanReprint>1</CanReprint>  
        <NeedReplaceContent>1</NeedReplaceContent>  
        <NeedShowReprintSource>1</NeedShowReprintSource> 
      </item>  
      <item> 
        <ArticleIdx>2</ArticleIdx>  
        <UserDeclareState>0</UserDeclareState>  
        <AuditState>2</AuditState>  
        <OriginalArticleUrl><![CDATA[Url_2]]></OriginalArticleUrl>  
        <OriginalArticleType>1</OriginalArticleType>  
        <CanReprint>1</CanReprint>  
        <NeedReplaceContent>1