不到100行代码写了一个巴黎奥运中国夺奖及排行图片生成工具

原创
前端路迹
2024-7-29 09:15
编辑于 2024-7-29 12:29

先看推送的图片效果(如下图所示),可以将图片推送到企业微信、钉钉的机器人。

上图实际上是两张图片拼接的,左侧是最新的中国队夺奖信息,右侧是整体的奖牌排名。下面来介绍如何用 NodeJS 来实现这个功能。

接口

首先肯定是要找到一个能够实时获取中国队夺奖的接口,这里我用的是百度的,但我们打开百度的奥运专项页面的时候,发现它定时在请求一个接口,这个接口里面就包含了奖牌的信息。

利用一个第三方http请求库来调用接口,这里我用的是got

var res = await got("上图的url").json()
var tabData = res?.tplData?.data?.tabsList?.[0]?.data?.[0]?.tabData
var list = []
// 将数据打平
if (tabData?.length) {
    tabData.forEach((item) => {
        item.dateList.forEach((item) => {
            // 倒序插入
            list.unshift(item)
        })
    })
}

上面list的数据如下图所示,是一个正序的的中国队夺奖的信息。

图片生成

左侧的图片是一个百度的搜索结果图,那么这里关键的是要拼接一个搜索关键字,这里就要用到上一步接口返回的数据的字段去拼接。

// 取数组最后一个(最新)
var latest = list[list.length - 1]

const medalType = {
        bronze: "铜牌",
        silver: "银牌",
        gold: "金牌",
}
// 拼接百度搜索关键词
var keyword =
        latest.playerName +
        "夺" +
        latest.smallMatch +
        medalType[latest.medalType] +
        "(" +
        latest.medal +
        ")"

var url = `https://www.baidu.com/s?word=${keyword}`

接着用 puppeteer 来打开url并调用puppeteer的api截图,注意这里截图传入了裁剪的参数,这里直接将 encoding 设为base64,方便后续拼接。

const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.setViewport({
    width: 1440,
    height: 1080,
    deviceScaleFactor: 2,
})
await page.goto(url, { waitUntil: "networkidle0" })

const medalImg = await page.screenshot({
    encoding: "base64",
    clip: {
        x: 130,
        y: 12,
        width: 600,
        height: 600,
    },
})

右边排名的图也是调用 puppeteer 来打开url并截图,排名的url也是用的百度的。

await page.setViewport({
    width: 600,
    height: 600,
    deviceScaleFactor: 2,
})

await page.goto(
    "https://tiyu.baidu.com/al/major/home?page=home&match=2024%E5%B9%B4%E5%B7%B4%E9%BB%8E%E5%A5%A5%E8%BF%90%E4%BC%9A&tab=%E5%A5%96%E7%89%8C%E6%A6%9C",
    {
        waitUntil: "networkidle0",
    }
)

const rankImg = await page.screenshot({
    encoding: "base64",
})

图片拼接

图片拼接可以用第三方库来完成,由于涉及图形处理,第三方安装可能没那么顺利,上面用到了puppeteer来实现了生成图片,它的能力是非常强大的,图片拼接当然也可以用它来完成。

既然 puppeteer 可以打开一个网页并截图,那么我们可以将上面的两张图放到一个网页里面自然就能实现图片拼接了,但是我们也不想去部署一个http server来做这个事情,这里就要引入一个知识点,那就是浏览器地址栏是支持输入html并展示的,格式如下:

data:text/html,<body style="margin:0">hello world</body>

这也是为什么上面的两个图片为什么选择返回base64,就是为了拼接成一段html代码,然后用 puppeteer 来打开并截图,进而实现图片拼接。

await page.setViewport({
    width: 1200,
    height: 600,
    deviceScaleFactor: 2,
})

const html = `data:text/html,<body style="margin:0"><div style="display:flex"><img style="width:600px" src="data:image/png;base64,${medalImg}"><img style="width:600px" src="data:image/png;base64,${rankImg}"></div></body>`

await page.goto(html)

await page.screenshot({
    path: path.resolve(__dirname, `../files/${Date.now()}.png`),
})

完整代码

const got = require("got")
const puppeteer = require("puppeteer")
const path = require("path")

async function parisOlympics(robot, job) {
    var res = await got(
        "https://tiyu.baidu.com/al/major/delegation?page=delegation&match=2024%E5%B9%B4%E5%B7%B4%E9%BB%8E%E5%A5%A5%E8%BF%90%E4%BC%9A&tab=%E8%8E%B7%E5%A5%96%E5%90%8D%E5%8D%95&id=29&tab_type=single&request__node__params=1"
    ).json()
    var tabData = res?.tplData?.data?.tabsList?.[0]?.data?.[0]?.tabData
    var list = []
    // 将数据打平
    if (tabData?.length) {
        tabData.forEach((item) => {
            item.dateList.forEach((item) => {
                // 倒序插入
                list.unshift(item)
            })
        })
    }

    if (list.length) {
        // 取数组最后一个(最新)
        var latest = list[list.length - 1]

        const medalType = {
            bronze: "铜牌",
            silver: "银牌",
            gold: "金牌",
        }
        // 拼接百度搜索关键词
        var keyword =
            latest.playerName +
            "夺" +
            latest.smallMatch +
            medalType[latest.medalType] +
            "(" +
            latest.medal +
            ")"

        var url = `https://www.baidu.com/s?word=${keyword}`

        const browser = await puppeteer.launch()
        const page = await browser.newPage()
        await page.setViewport({
            width: 1440,
            height: 1080,
            deviceScaleFactor: 2,
        })
        await page.goto(url, { waitUntil: "networkidle0" })

        var timestamp = Date.now()

        const medalImg = await page.screenshot({
            encoding: "base64",
            clip: {
                x: 130,
                y: 12,
                width: 600,
                height: 600,
            },
        })

        await page.setViewport({
            width: 600,
            height: 600,
            deviceScaleFactor: 2,
        })

        await page.goto(
            "https://tiyu.baidu.com/al/major/home?page=home&match=2024%E5%B9%B4%E5%B7%B4%E9%BB%8E%E5%A5%A5%E8%BF%90%E4%BC%9A&tab=%E5%A5%96%E7%89%8C%E6%A6%9C",
            {
                waitUntil: "networkidle0",
            }
        )

        const rankImg = await page.screenshot({
            encoding: "base64",
        })

        await page.setViewport({
            width: 1200,
            height: 600,
            deviceScaleFactor: 2,
        })

        const html = `data:text/html,<body style="margin:0"><div style="display:flex"><img style="width:600px" src="data:image/png;base64,${medalImg}"><img style="width:600px" src="data:image/png;base64,${rankImg}"></div></body>`

        await page.goto(html)

        await page.screenshot({
            path: path.resolve(__dirname, `../files/${Date.now()}.png`),
        })

        await browser.close()
    }
}
转载请注明出处。本文地址: https://www.qinshenxue.com/article/generation-tool-for-china-victory-and-ranking-at-the-paris-olympics.html
关注我的公众号