python 之 微信订阅号群发接口发图文和视频

#python 之 微信订阅号群发接口发图文和视频

####说明:
######微信订阅号群发没有上传视频接口。想要实现群发视频就需要用腾讯视频连接(微信支持腾讯视频),群发接口支持图文(也就是图片和文字)和HTML 。封面图需要长传返回media_id、插图需要上传图片获取url放入html中,再构建图文,最后群发。
视频:视频也需要构建标签插入HTML中就可显示。
ps: 视频必须使用腾讯视频,微信只支持腾讯视频。腾讯视频上传由于难度比较大没有开发。
视频标签结构:这里写图片描述

<iframe class="video_iframe" data-vidtype="2" allowfullscreen="" frameborder="0" style="position:relative;z-index:1;" data-ratio="1.7647058823529411" data-w="480" data-src="video_url"></iframe><br>

视频连接也许需要做处理:如下

原连接:https://v.qq.com/x/page/l0778mjb4lp.html

处理后:
https://v.qq.com/iframe/preview.html?vid=l0778mjb4lp&width=500&height=375&auto=0

如不处理将无法显示。

######微信官方接口连接:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1481187827_i0l21

####一下是代码实现:
释:代码并非相当完善,如有不足请指点,只希望能帮到。error"] = dicts.get(“errcode”)

# -*- coding: utf-8 -*-
"""
-------------------------------------------------
    File Name   :   update_wechat_post.py
    date        :   18-12-26
    Author      :    Hebel
-------------------------------------------------
    Description:
    note: 
-------------------------------------------------
"""

from urllib.request import urlretrieve
from html.parser import HTMLParser
import requests
import json
import time
import random
import re
import hashlib
import logging
import os




class UploadNewsWechat(object):
    """微信公众号发图文插入视频"""

    def __init__(self, content, cookies=None, proxy_info=None, type="image"):

        self.account_info = content.get("password")
        self.appId = self.account_info.split("|")[0]
        self.secret = self.account_info.split("|")[1]
        self.contentAll = content.get("wechat_content")
        self.imageUrlName = []
        self.thumbUrlName = []
        self.errorlist = []
        self.tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}"
        self.startUpload = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token={token}&type=%s"%type
        self.ImageUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token={token}"
        self.newsIdUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadnews?access_token={token}"
        self.sendUrl = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token={token}"
        self.logging = logging.basicConfig(filename="logging.log", level=logging.DEBUG,format='[%(asctime)s]%(name)s:%(levelname)s:%(message)s')
        self.filePath = ""
        self.uid = None
        self.test_id = 0
        self.jpg_image_file_name = "jpg_Image.jpg"
        self.gif_image_file_name = "gif_Image.jpg"


    def __del__(self):
        """删除下载图片文件"""
        try:
            os.remove(self.jpg_image_file_name)
            os.remove(self.gif_image_file_name)
        except:
            pass

    def getImageId(self, token, image_url, define_type="id"):
        """上传封面图片获得media_id"""
        try:

            file_name = self.gif_image_file_name if "gif" in image_url else self.jpg_image_file_name
            urlretrieve(url=image_url, filename=file_name)
        except:
            if "url" in define_type:
                return None
            raise IOError("下载图片失败,请检查图片link")
        try:
            if "id" in define_type:

                url = self.startUpload.format(token=token)
                files = {'data': open(file_name, "rb")}
                resp = requests.post(url=url, files=files)
                if not resp.status_code is 200:
                    raise IOError("上传图片失败")
                dicts_data = resp.json()

                media_id = dicts_data.get("media_id", None)
                if not media_id:
                    raise IOError("上传图片失败")
                return media_id
            else:
                url = self.ImageUrl.format(token=token)
                files = {'data': open(file_name, "rb")}
                resp = requests.post(url=url, files=files)
                if not resp.status_code is 200:
                    return None
                dicts_data = resp.json()
                img_url = dicts_data.get("url", None)
                return img_url
        except Exception as err:
            if "url" in define_type:
                return None
            raise err

    def html_decode(self, html):
        """替换掉那些没有成功转码的html标签"""
        htmlCodes = (("'", '&#39;'), ('"', '&quot;'), ('>', '&gt;'), ('<', '&lt;'), ('&', '&amp;'))
        for code in htmlCodes:
            html = html.replace(code[1], code[0])
        return html

    def buildContent(self, token, html, define_type="image"):
        """ 对图片和视频链接进行处理"""
        #处理文章内的图片
        if "image" in define_type:
            pattern = '<img src="(.*?)"[^>]*/>'
            re_image = re.compile(pattern)
            img_list = re_image.findall(html)
            if not img_list:
                return html
            new_html = re_image.sub("%s", html)
            img_url_all = []
            for url in img_list:
                img_url = self.getImageId(token, url, define_type="url")
                img_html = '<img src="%s" title="" alt=""/>'%img_url
                img_url_all.append(img_html)
            return new_html%tuple(img_url_all)
        # 处理文章内的视频
        else:
            pattern = '<img name="video\|https://v\.qq.com/x/page/(.*?)\..*?"[^>]*/>'
            re_image = re.compile(pattern)
            new_html = re_image.sub("%s", html)
            video_url_list = re_image.findall(html)
            if not video_url_list:
                return html
            video_iframe_all = []
            for video_id in video_url_list:
                video_url = "https://v.qq.com/iframe/preview.html?vid={video_id}&width=500&height=375&auto=0".format(video_id=video_id)
                video_html = '<iframe class="video_iframe" data-vidtype="2" allowfullscreen="" frameborder="0" style="position:relative;z-index:1;" data-ratio="1.7647058823529411" data-w="480" data-src="{video_url}"></iframe><br>'.format(video_url=video_url)
                video_iframe_all.append(video_html)
            return new_html%tuple(video_iframe_all)


    def getToken(self, appId, secret):
        """使用id和秘钥请求得到token,用于数据上传"""
        get_token_Url = self.tokenUrl.format(appId, secret)
        resp = requests.get(url=get_token_Url, timeout=5)
        if resp.status_code is 200:
            dict_data = resp.json()
            token = dict_data.get("access_token", None)
            if not token is None:
                return token
            else:
                errmsg = dict_data.get("errmsg")
                if "ip" in errmsg:
                    ip_list = re.findall("\d+\.\d+\.\d+\.\d+", errmsg)
                    if ip_list:
                        ip = ip_list[0]
                        result = "此ip:{ip}不在当前微信公众号白名单内, 如需群发请手动加入".format(ip=ip)
                        return Exception(result)
                    return Exception(errmsg)
                return Exception(dict_data)
        return Exception("获取token请求失败,status_code:%s"%resp.status_code)


    def buildNews(self, token, content_list):
        """重构html ,所有url替换为有效的url"""
        if not isinstance(content_list, list):
            raise ValueError("数据类型错误")
        data_list = []
        for dicts_data in content_list:
            articles = {} #重新创建数据字典
            cover_image_url = dicts_data.get("thumb_media_id", None)
            if cover_image_url is None:
                raise ValueError("封面图片是不可缺的")
            #上传封面图片获取封面图片di
            image_id = self.getImageId(token=token, image_url=cover_image_url)
            articles["thumb_media_id"] = image_id
            articles["author"] = dicts_data.get("author","")
            title = dicts_data.get("title", None)
            if title is None:
                raise ValueError("标题不能为空")
            articles['title'] = title
            content = dicts_data.get("content",None)
            if content is None:
                raise ValueError("未获取到内容数据html")
            #解析文章内容,html转码,替换图片,替换视频链接
            html_parser = HTMLParser()
            html = html_parser.unescape(content)
            html = self.html_decode(html)
            #处理图片数据
            new_html = self.buildContent(token, html)
            new_html = self.buildContent(token, new_html, define_type="video")
            # 组建单个文章数据
            articles['content'] = new_html
            articles['content_source_url'] = dicts_data.get("content_source_url","http://")
            articles['digest'] = dicts_data.get("digest", "")
            articles['show_cover_pic'] = dicts_data.get("show_cover_pic", "0")
            data_list.append(articles)
        return {"articles":data_list}


    def uploadNews(self, token, content):
        """上传图文内容获得新的media_id """
        content = json.dumps(content, ensure_ascii=False).encode()
        url = self.newsIdUrl.format(token=token)
        headers = {'content-type': 'charset=utf-8'}
        resp = requests.post(url=url, data=content, headers=headers, timeout=5)
        if not resp.status_code is 200:
            raise IOError("上传图文失败,状态码:%s"%resp.status_code)
        dicts_data = resp.json()
        media_id = dicts_data.get("media_id",None)
        if media_id is None:
            raise ValueError("上传图文,获取media_id失败,%s"%dicts_data)
        return media_id


    def buildSendData(self, media_id):
        """构建群发数据"""
        sendData = {}
        filter = {}
        mpnews = {}
        filter["is_to_all"] = True
        mpnews["media_id"] = media_id
        sendData["filter"] = filter
        sendData["mpnews"] = mpnews
        sendData["msgtype"] = "mpnews"
        sendData["send_ignore_reprint"] = 0
        return sendData

    def sendNews(self, token, sendData):
        """群发请求"""
        sendData = json.dumps(sendData)
        url = self.sendUrl.format(token=token)
        try:
            resp = requests.post(url=url, data=sendData, timeout=5)
        except Exception as err:
            raise IOError("请求群发失败, %s"%err)
        dicts_data = resp.json()
        errcode = dicts_data.get("errcode")
        if errcode is 0:
            return {"state": 0, "status": "群发成功:%s" % dicts_data.get("errmsg"),"url": ""}
        else:
            return {"state": 1, "status": "%s" % dicts_data.get("errmsg"), "url": ""}


    def run(self):
        try:
            if self.appId and self.secret:
                #获取token
                token = self.getToken(appId=self.appId, secret=self.secret)
                if isinstance(token, Exception):
                    raise token
                if isinstance(self.contentAll, list):
                    raise TypeError("wechat_content数据类型错误,必须是列表")
                #更新更换图文中的图片link和视频link
                news_data = self.buildNews(token=token, content_list=self.contentAll)
                # 上传图文内容得到media_id
                media_id = self.uploadNews(token=token,content=news_data)
                #构建群发数据
                data_dicts = self.buildSendData(media_id=media_id)
                #开始群发
                self.sendNews(token=token, sendData=data_dicts)
            else:
                raise ValueError("password数据缺失")
        except Exception as err:
            return  {"state": 1, "status": "%s"%str(err), "url": ""}
# if __name__ == '__main__':


#     wechat = UploadNewsWechat(content=data)
#     result = wechat.run()
#     print(result)

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_JackSparrow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值