B站动态转图片

B站动态转图片

前言

因为重写QQ机器人,空间动态转发动态的功能需要阉割,所以我考虑用其他的方式来进行一定程度补救。

想到的办法就是将内容转发到QQ群中,但要尽可能小的减少对正常群聊的影响,于是便打算将B站动态转为图片发送到群聊中。


环境准备

  • Python 3.10 (您可以使用自己的环境)

  • pip install pillow
    pip install numpy
    pip install opencv-python
    pip install requests 
    

实现思路

大致过程如下:

  • 获取B站动态并解析对应内容
  • 计算高度动态创建底层图片
  • 填充文字
  • 填充图片

首先就是要获取B站动态,可以通过api请求后解析获得。

https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history?host_uid=uid

将整个图片的宽度设置为 1200 像素,高度需要进行响应计算获得。

在解析动态时,可以获取对应的图片列表,但我当然不想将图片下载下来在进行计算。

于是使用 numpy 将内存中的二进制图片转为数组,再转为image

通过 image.shape就可以获得对应的宽高参数

在填充文字的时候,cv2.inputText不能填充中文,所以需要将其转为PIL使用。

OpenCVPIL

def OpenCVtoPIL(opencvImage):
    return Image.fromarray(cv2.cvtColor(opencvImage, cv2.COLOR_BGR2RGB))

PILOpenCV

def PILtoOpenCV(pilImage):
    return cv2.cvtColor(numpy.asarray(pilImage), cv2.COLOR_RGB2BGR)

填充文字时,还需要考虑字符问题,以便实现换行。

使用正则表达式,[^\x00-\xff]筛选出所有双字节字符。

最后使用image.paste添加所有图片。

全部代码

import json
import os
import re
import time

import cv2
import numpy
import requests
from PIL import Image, ImageDraw, ImageFont

reg = "[^\x00-\xff]"


class Blog:
    def __init__(self, uid):
        self.blog = getBlog(uid)
        self.blogTitle = self.blog["data"]["name"] + self.blog["data"]["type"]
        self.blogTime = self.blog["data"]["time"]
        self.blogContent = self.blog["data"]["content"].replace("\r", "")
        self.blogImage = self.blog["data"]["image"]
        self.lineCount = 0
        self.content = ""
        self.imageList = []
        self.getContent()
        if not len(self.blogImage) == 0:
            self.getImageList()

    def getContent(self):
        contentList = self.blogContent.split("\n")
        self.content = ""
        for i in contentList:
            lineWidth = 0
            for j in i:
                if lineWidth >= 1075:
                    self.content += "\n"
                    self.lineCount += 1
                    lineWidth = 0
                if isChinese(j):
                    lineWidth += 50
                    self.content += j
                else:
                    lineWidth += 25
                    self.content += j
            self.content += "\n"
            self.lineCount += 1

    def getImageList(self):
        for i in self.blogImage:
            # 获取图片
            data = requests.get(i)
            # 将图片转为矩阵
            blogImage = cv2.imdecode(numpy.asarray(bytearray(data.content), dtype="uint8"), cv2.IMREAD_COLOR)
            # 获取原图片宽高
            height, width = blogImage.shape[:2]
            # 重新计算比例
            newHeight = 1100 * height // width
            # 修改图片宽高
            blogImage = cv2.resize(blogImage, (1100, newHeight))
            print(blogImage.shape)
            self.imageList.append(blogImage)


def getBlog(uid):
    url = 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history?host_uid=' + str(uid)
    result = requests.get(url).json()
    data = result['data']['cards'][0]
    blogName = data['desc']['user_profile']['info']['uname']
    blogType = data['desc']['type']
    blogTime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data['desc']['timestamp']))
    if blogType == 1 or blogType == 4:
        blogTypeName = '[转发]'
        blogContent = json.loads(data['card'])['item']['content']
        blogUrl = 'https://t.bilibili.com/' + data['desc']['dynamic_id_str']
        blogImage = []
        try:
            imageUrl = json.loads(json.loads(data['card'])['origin'])['pic']
            blogImage.append(imageUrl)
        except:
            pass
        ret = {
            'name': blogName,
            'type': blogTypeName,
            'time': blogTime,
            'content': blogContent,
            'url': blogUrl,
            'image': blogImage
        }
        retJson = {
            'code': 200,
            'msg': '成功',
            'data': ret
        }
        return retJson
    elif blogType == 2:
        blogTypeName = '[动态]'
        blogContent = json.loads(data['card'])['item']['description']
        blogUrl = 'https://t.bilibili.com/' + data['desc']['dynamic_id_str']
        blogImage = []
        try:
            imageList = json.loads(data['card'])['item']['pictures']
            for i in imageList:
                blogImage.append(i['img_src'])
        except:
            pass
        ret = {
            'name': blogName,
            'type': blogTypeName,
            'time': blogTime,
            'content': blogContent,
            'url': blogUrl,
            'image': blogImage
        }
        retJson = {
            'code': 200,
            'msg': '成功',
            'data': ret
        }
        return retJson
    elif blogType == 8:
        blogTypeName = '[视频]'
        blogContent = json.loads(data['card'])['title'] + '\n\n' + json.loads(data['card'])['dynamic'] + '\n' + \
                      json.loads(data['card'])['desc']
        blogImage = [json.loads(data['card'])['pic']]
        blogUrl = json.loads(data['card'])['short_link']
        ret = {
            'name': blogName,
            'type': blogTypeName,
            'time': blogTime,
            'content': blogContent,
            'url': blogUrl,
            'image': blogImage
        }
        retJson = {
            'code': 200,
            'msg': '成功',
            'data': ret
        }
        return retJson


def isChinese(text):
    chLen = re.findall(reg, text)
    if len(chLen) == 0:
        return False
    else:
        return True


def PILtoOpenCV(pilImage):
    return cv2.cvtColor(numpy.asarray(pilImage), cv2.COLOR_RGB2BGR)


def OpenCVtoPIL(opencvImage):
    return Image.fromarray(cv2.cvtColor(opencvImage, cv2.COLOR_BGR2RGB))


def setTitle():
    titleFont = ImageFont.truetype(os.path.join('', 'simkai.ttf'), 100)
    draw.text((50, 50), blog.blogTitle, font=titleFont, fill="#000000")


def setContent():
    contentFont = ImageFont.truetype(os.path.join('', 'simkai.ttf'), 50)
    draw.text((50, 170), blog.content, font=contentFont, fill="#000000")


def getHeight():
    getTextHeight = 170 + blog.lineCount * 50 + 20
    getAllHeight = getTextHeight
    for i in blog.imageList:
        height, width = i.shape[:2]
        getAllHeight += height + 20
    getAllHeight += 50
    return getTextHeight, getAllHeight


def setImage():
    imageHeight = 0
    for i in blog.imageList:
        height, width = i.shape[:2]
        img = OpenCVtoPIL(i)
        image.paste(img, (50, imageHeight + textHeight))
        imageHeight += height + 20


if __name__ == "__main__":
    blog = Blog(36081646)
    textHeight, allHeight = getHeight()
    image = numpy.zeros((allHeight, 1200, 3), numpy.uint8)
    image.fill(255)
    image = OpenCVtoPIL(image)
    draw = ImageDraw.Draw(image)
    setTitle()
    setContent()
    setImage()
    image = PILtoOpenCV(image)
    cv2.imwrite("image.jpg", image)

效果

效果

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值