视频主要介绍了今天股市详情,包括三大股指、主要板块和北向资金等维度信息
上面的视频是用 Node 生成的,所用的工具有:
- Puppeteer: 提供了一个高级 API 来通过 CDP 控制 Chrome/Chromium
- 语音合成 API: 提供文字便可生成对应的音频
- FFcreator:基于 OpenGL 和 FFmpeg 的视频处理工具库
获取统计数据
首先制作这种统计视频肯定是需要数据支撑的,所以我们需要去对应的网站爬取今日最新数据
数据爬取这里使用的是Puppeteer,因为相比其他数据抓取库我更熟悉它。
因为爬取的股票相关数据网站,这些网站可能会做一些低级的反爬措施,比如 UA 校验或者检测 WebDriver 特征等
当然有很多反爬措施,比如鼠标检测、行为检测和验证码等
这里我们需要伪造一下 UA 及删除所有 WebDriver 特征
await page.evaluateOnNewDocument(() => {
if (navigator.webdriver === false) {
} else if (navigator.webdriver === undefined) {
} else {
delete Object.getPrototypeOf(navigator).webdriver;
}
});
if (browser) {
// UA欺骗
const fakeUA = (await browser.userAgent()).replace(
"HeadlessChrome/",
"Chrome/"
);
await page.setUserAgent(fakeUA);
}
下面就是我们获取对应的 Dom 并提取内容然后储存了
// 上证指数
await page.goto("https://gushitong.baidu.com/index/ab-000001");
await new Promise((res) => {
// 这里为了图省事直接用的等待 5s,其实可以用waitForNavigation
setTimeout(() => {
res();
}, 5000);
});
const shangzhengChange = await page.$eval(
`body>div:nth-of-type(1)>div>div>div:nth-of-type(1)>div:nth-of-type(2)>div>div>div>div:nth-of-type(2)>div:nth-of-type(1)>div>div>div>div>div:nth-of-type(2)>div:nth-of-type(2)>div>div>div:nth-of-type(2)>div>div>div:nth-of-type(1)>div:nth-of-type(9)>span:nth-of-type(2)`,
(element) => element.innerText
);
如果页面的数据太分散了 可以直接截取 HTTP 请求是数据
page.on("response", async (response) => {
const url = response.url();
if (
url.startsWith("https://finance.pae.baidu.com/selfselect/getstockquotation")
) {
const headers = response.headers();
if (headers["content-type"] === "application/json") {
const json = await response.json();
const length = json.Result.fivedays.length;
data[block[blockIndex]] = json.Result.fivedays[length - 1].priceinfos;
}
}
});
我们就得到了对应的数据格式
处理数据 转换文案
在我们爬取完数据之后,我们需要对数据按照相应的模板进行转换
比如:
let template = [
`大家好 欢迎收听 今日股市,今天是${config.date},`,
`截至收盘 沪指${config.huzhi},深成指${config.shenchengzhi},创业板指${config.chunagyebanzhi},`,
`沪深两市今日成交额分别为:${dataJson.shangzhengMakeTotal}和${dataJson.shenzhengMakeTotal},`,
`今日成交额前五名个股为:${personal10Result},`,
`今日行业板块增长前五名为:${hyUp10Result},`,
`今日行业板块下跌前五名为:${hyDown10Result},`,
`今日概念板块增长前五名为:${gnUp10Result},`,
`今日概念板块下跌前五名为:${gnDown10Result},`,
`北向资金全天${config.beixiang},其中沪股通${config.beixianghu}, 深股通${config.beixiangshen},`,
`其中${northUp10Result},感谢观看,下期再见!`,
];
数据处理完之后,会得到这样的内容:
大家好 欢迎收听 今日股市,今天是 2024 年 4 月 10 日,星期三,
截至收盘 沪指跌 0.70%,深成指跌 1.60%,创业板指跌 2.06%,
沪深两市今日成交额分别为:3647.18 亿和 4652.77 亿,
今日成交额前五名个股为:长江电力涨 5.18 亿,三一重工涨 2.26 亿,深中华 A 涨 2.19 亿,宗申动力涨 2.12 亿,中国石油涨 1.83 亿,
今日行业板块增长前五名为:贵金属涨 2.82%,珠宝首饰涨 2.33%,工程机械涨 1.89%,航空机场涨 0.78%,燃气涨 0.73%,
今日行业板块下跌前五名为:游戏跌 4.64%,电子化学品跌 3.68%,半导体跌 3.48%,通信服务跌 3.26%,消费电子跌 3.18%,
今日概念板块增长前五名为:飞行汽车(eVTOL)涨 2.49%,退税商店涨 1.94%,工业母机涨 1.46%,黄金概念涨 1.18%,工程机械概念涨 1.04%,
今日概念板块下跌前五名为:EDR 概念跌 4.15%,电子车牌跌 4.01%,电子后视镜跌 3.93%,存储芯片跌 3.92%,3D 摄像头跌 3.90%,
北向资金全天净流出 41.14 亿元,其中沪股通净流出 31.09 亿元, 深股通净流出 10.05 亿元,
其中紫金矿业涨 5.52 亿,海尔智家涨 2.20 亿,赤峰黄金涨 1.92 亿,中金黄金涨 1.85 亿,洛阳钼业涨 1.82 亿,感谢观看,下期再见!
接下来我们需要将这些文字转换,这里使用的是 百度 AI 开放平台
最终输出的就是音频文件
合成视频
合成视频使用的是 FFcreator, 由于 FFmpeg 在动画效果方面处理的不够精细
所以 FFCreator 使用 OpenGL 来处理图形渲染并使用 shader 后处理来生成转场效果,最后使用 FFmpeg 合成视频
创建入口
const creator = new FFCreator({
cacheDir, // 缓存目录
outputDir, // 输出目录
output, // 输出文件名(FFCreatorCenter中可以不设)
width: 500, // 影片宽
height: 680, // 影片高
cover: "a.jpg", // 设置封面
audioLoop: true, // 音乐循环
fps: 24, // fps
threads: 4, // 多线程(伪造)并行渲染
debug: false, // 开启测试模式
defaultOutputOptions: null, // ffmpeg输出选项配置
});
创建场景
const scene = new FFScene();
scene.setBgColor("#30336b"); // 设置背景色
scene.setDuration(8.5); // 设置停留时长
scene.setTransition("Fat", 1.5); // 设置过渡动画(类型, 时间)
creator.addChild(scene);
创建子元素
// 创建图片元素
const img = new FFImage({ path: imgpath });
img.setXY(250, 340); // 设置位置
img.setScale(2); // 设置缩放
img.setRotate(45); // 设置旋转
img.setOpacity(0.3); // 设置透明度
img.setWH(100, 200); // 设置宽高
img.addEffect("fadeInDown", 1, 1); // 设置动画效果
scene.addChild(img);
const img = new FFImage({ path, width, height, x, y });
// 创建文字元素
const text = new FFText({ text: "这是一个文字", x: 250, y: 80 });
text.setColor("#ffffff"); // 文字颜色
text.setBackgroundColor("#b33771"); // 背景色
text.addEffect("fadeInDown", 1, 1); // 动画
text.alignCenter(); // 文字居中
text.setStyle({ padding: [4, 12, 6, 12] }); // 设置样式object
scene.addChild(text);
// 创建字幕元素
const content = "跟计算机工作酷就酷在这里,它们不会生气,能记住所有东西...省略";
const subtitle = new FFSubtitle({
comma: true, // 是否逗号分割
backgroundColor: "#00219C", // 背景色
color: "#fff", // 文字颜色
fontSize: 24, // 字号
});
subtitle.setText(content); // 设置文案也可以放到conf里
subtitle.frameBuffer = 24; // 缓存帧
subtitle.setDuration(12); // 设置字幕总时长
scene.addChild(subtitle);
// 添加音频
scene.addAudio("../audio/bg.mp3"); // 俩种配置方式
// 创建Echarts图表
const option = {
grid: {
top: "12%",
left: "3%",
right: "4%",
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
data: xAxis,
},
yAxis: {
type: "value",
min: "dataMin",
max: "dataMax",
},
series: [
{
name: "沪指",
type: "line",
symbol: "none",
sampling: "lttb",
itemStyle: {
color: "rgb(255, 0, 0)",
},
data: shangzhengRatios,
},
{
name: "深指",
type: "line",
symbol: "none",
sampling: "lttb",
itemStyle: {
color: "rgb(255, 243, 0)",
},
data: shenzhengRatios,
},
{
name: "创业",
type: "line",
symbol: "none",
sampling: "lttb",
itemStyle: {
color: "rgb(255, 103, 0)",
},
data: chuangyeRatios,
},
],
};
const fchart = new FFChart({
theme: "light",
option: option,
x: 200,
y: 300,
width: 600,
height: 600,
});
fchart.addEffect("fadeIn", 1, 0.5);
fchart.update((chart) => {}, 1000);
fchart.updateNow();
scene.addChild(fchart);
我们将所用到的 9个场景全部写完,就可以生成文章最开始的那个视频了
完整的Demo示例代码:gitee.com/sunsilent/c…
如果你有庞大的媒体资源池及较为智能的媒体分析分类算法,完全可以用FFcreator写一个自己的 图文生成视频