From 5e4e893e5a5fb1242a29a4d937cb174e9f6a621e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E7=90=AA=E6=96=8C?= Date: Fri, 29 Mar 2024 18:17:45 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20=E5=8F=96=E6=B6=88b?= =?UTF-8?q?=E7=AB=99=E8=A7=86=E9=A2=91=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/blog/playwright-start.md | 314 ++++++++++++ src/lib/search.json | 921 ++++++++++++++++++++++++++++++++++ src/lib/video.ts | 18 +- src/pages/video.tsx | 4 +- 4 files changed, 1241 insertions(+), 16 deletions(-) create mode 100644 data/blog/playwright-start.md create mode 100644 src/lib/search.json diff --git a/data/blog/playwright-start.md b/data/blog/playwright-start.md new file mode 100644 index 0000000..a3a8276 --- /dev/null +++ b/data/blog/playwright-start.md @@ -0,0 +1,314 @@ +--- +title: 端到端测试框架 Playwright 使用入门 +date: 2023/10/15 23:03:43 +lastmod: 2023/11/2 15:45:10 +tags: [前端, 测试] +draft: false +summary: playwright 介绍 Playwright 是一个端到端(E2E)测试框架, 它可在所有现代浏览器中运行功能强大的测试和自动化。支持多种编程语言 API, 包括 JavaScript +images: https://img.maqib.cn/img/202311021549809.png +authors: ['default'] +layout: PostLayout +--- + +## playwright 介绍 + +Playwright 是一个端到端(E2E)测试框架, 它可在所有现代浏览器中运行功能强大的测试和自动化。支持多种编程语言 API, 包括 JavaScript 、 TypeScript, Python, .NET 和 Java。正因为它基于浏览器,相当于模拟用户真实操作,因此不光能够用来跑测试用例,还可以用来写爬虫。 + +## Playwright Test for VSCode + +我们可以安装一个 vscode 插件 [Playwright Test for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright),来帮助我们运行、录制、调试测试用例。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/66c47c84d5fb46cf9158b6e2ee6dd918~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1054&h=260&s=90414&e=png&b=181818) + +## 初始化项目 + +如果项目中没有安装`Playwright` NPM 包,或者重新开始一个新的测试项目,需要可以在 vscode 命令面板中输入`intsll Playwright`。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1adcff255ace4377b9a4d15f5c626565~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2378&h=1174&s=465727&e=png&a=1&b=f4f4f4) + +选择我们常用的浏览器,不必担心选错,后面可以在项目中更改。还可以选择 GitHub Action ,这样就可以轻松在 Github 中持续集成。 + +这里我选择 chromium,这样可以只下载一个浏览器内核。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0744a17990a84c3cb2924d0b59afac78~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1758&h=560&s=254125&e=png&b=232323) + +点击`OK`后,插件会帮我们自动初始化程序, 下图是初始化的目录结构 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4896073d11c8446da204e97a72a857c3~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=848&h=840&s=197258&e=png&b=191919) + +配置文件都在 `playwright.config.ts` 中。 + +看下 `package.json`,只包含了一个包`@playwright/test` + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/31486963c0304fad9901edb1bef3becb~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1160&h=616&s=171764&e=png&b=202020) + +## 运行测试 + +所有的测试用例都要写在 `tests` 文件夹中,默认有一个测试文件,包含有 2 个测试用例,代码在`example.spec.ts` 中。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e5078f88944c48d0ba9736fd10efa66f~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1666&h=1058&s=416419&e=png&b=1f1f1f) + +第一个测试用例:确保标题包含 Playwright; + +第二个测试用例:确保点击 “Get Started”后,跳转到 intro 的链接。 + +选择左侧的测试用例,并且勾选 `Show browser`,我们便可以直观的看到 Playwright 运行测试的过程。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5646f017cc4947feaf91fa32dc7cf03a~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=800&h=498&s=1292146&e=gif&f=90&b=1d1d1d) + +以上例子默认是使用 chromium 来运行的,并且 chromium 不包含任何 cookie 和缓存信息。 + +在 `playwright.config.ts` 配置文件中, 可以配置启用的浏览器为 chrome,我们只需要增加一个参数 `channel`,让 Playwright 使用浏览器来运行。 也可以是其他浏览器,参数可以为: "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev","msedge-canary". + +```js +use: { ++ channel:'chrome', + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, +``` + +我们虽然改成了使用浏览器来运行,但是启动的浏览器也是一个无痕模式,不包含任何缓存信息。 + +## 添加 cookie + +测试的系统往往需要登录,而在运行每个测试用例之前运行,都需要登录,这肯定是繁琐的,因此我们可以在运行测试用例之前,手动拷贝 cookies,注入到浏览器中。 + +比如掘金的每日签到和抽奖,我就可以使用 Playwright 来实现自动化 + +首先建立一个测试文件 + +```ts +import { test, expect, type Page } from '@playwright/test' + +test('登录', async ({ page, context }) => { + await context.addCookies([ + { + name: 'sessionid', + value: 'xxx', + path: '/', + domain: '.juejin.cn', + }, + { + name: 'sessionid_ss', + value: 'xxx', + path: '/', + domain: '.juejin.cn', + }, + ]) + await page.goto('https://juejin.cn/') +}) +``` + +打开 chrome 控制台,复制 cookies, 添加到代码中 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/58b20f4097e744c18e6f65a7060bb74f~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=567&h=289&s=72569&e=png&b=ffffff) + +此时点击左侧运行的测试用例,发现已经是登录状态。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a0ad5f78ed2843f1bff26390dab3d80a~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2676&h=1128&s=981992&e=png&b=1e1e1e) + +## 录制一个测试用例 + +如果要手动去查找 dom ,从零开始写一个测试用例肯定是繁琐的,因此 Playwright VSCode 插件提供了录制功能。 + +运行上一次测试用例后,浏览器是未关闭的。此时我们点击 vscode 左侧的 `Record new` 按钮,vscode 便会自动创建一个测试文件,并且记录操作步骤。 + +录制时,浏览器又是一个全新的,不保留任何状态,那如果我们要测试的是登录后的功能,岂不是又要登录? +其实 playwright 可以保存登录状态。 + +在上面测试用例后加一句 storageState。 + +```ts +import { test, expect, type Page } from '@playwright/test' + +test('登录', async ({ page, context }) => { + await context.addCookies([ + { + name: 'sessionid', + value: 'xxx', + path: '/', + domain: '.juejin.cn', + }, + { + name: 'sessionid_ss', + value: 'xxx', + path: '/', + domain: '.juejin.cn', + }, + ]) + await page.goto('https://juejin.cn/') + ;+(await context.storageState({ path: 'state.json' })) +}) +``` + +并且在 `playwright.config.ts` 中,配置存储位置。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/233b6bf0f3314c0b993782f0882cecd3~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1250&h=630&s=250225&e=png&b=20201f) + +此时我们录制操作,就已经是登录状态了。 + +![未命名123.2023-10-15 22_46_37.gif](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/96a986ea21014bd390529a273ea72816~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1152&h=720&s=912484&e=gif&f=20&b=141414) + +以下便是录制后的代码。 + +```js +import { test, expect } from '@playwright/test' + +test('test', async ({ page }) => { + await page.goto('https://juejin.cn/') + await page.getByRole('button', { name: '去签到' }).click() + await page.getByRole('button', { name: '立即签到' }).click() + await page.getByRole('button', { name: '去抽奖' }).click() + await page.getByText('免费抽奖次数:1次').click() + await page.getByRole('button', { name: '收下奖励' }).click() +}) +``` + +录制完成后,直接运行代码可能会报错,我们需要调整一下,因为有些文本是异步请求实现的,有些事件是请求成功后绑定的,在手动录制时,因为已经响应完成,因此没问题,我们加上 2 句延迟。 + +```js +test('test', async ({ page }) => { + await page.goto('https://juejin.cn/') + ;+(await page.waitForTimeout(1000)) + await page.getByRole('button', { name: /去签到|已签到/ }).click() + ;+(await page.waitForTimeout(1000)) + await page.getByRole('button', { name: /今日已签到|立即签到/ }).click() + await page.getByRole('button', { name: '去抽奖' }).click() + + const lotteryElement = await page.$('#turntable-item-0') + const buttonText = await lotteryElement?.textContent() + if (buttonText === '免费抽奖次数:1次') { + await lotteryElement?.click() + await page.getByRole('button', { name: '收下奖励' }).click() + } else { + expect(page.locator('#turntable-item-0', { hasText: /单抽/ })).toBeDefined() + } +}) +``` + +便可以运行成功,注意这里我使用了 `waitForTimeout` 这个 api 在官网中已经被标记了废弃(deprecate) + +实际测试场景中请使用改用网络事件、选择器变得可见等信号。 + +```js +await page.goto('https://juejin.cn/') +await page.waitForResponse((res) => + res.url().includes('/user_api/v1/incentive_activity/award_after_login') +) +await page.getByRole('button', { name: /去签到|已签到/ }).click() + +await page.waitForResponse((res) => res.url().includes('/growth_api/v2/get_today_status')) +await page.getByRole('button', { name: /今日已签到|立即签到/ }).click() +``` + +等待接口响应成功后再出发点击事件。 + +还有一点就是,自动录制的代码,一般使用了语义化定位方法,比如`getByRole`、`getByText`,这些定位器往往不够准确,改动代码会导致测试用例失效。 + +因此我们可以使用 `locator` 定位器来替换。 + +在 Playwright 中,Locator 表示一种元素查找方式,是 Playwright 提供的一组方法,用于定位页面上的元素。 + +Locator 支持 XPath 和 CSS 选择器 + +```js +await page + .locator('#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input') + .click() + +await page.locator('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input').click() +``` + +在 vscode 中可以使用 Pick locator 快速活动当前的 dom 定位。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9ccc4b2369a349759142a9aab6787528~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=912&h=822&s=248991&e=png&b=1a1a1a) + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a0dfbcd9daba47128597592fb170d294~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1710&h=1318&s=1095667&e=png&b=faf6f5) + +## 测试用例及断言 + +录制的测试代码只能确保业务能够跑通,但不能证明程序的可靠与健壮。一旦测试用例出错,也不知道是程序错误还是测试用例错误,因此我们还是需要根据测试用例来写可靠的测试代码。 + +比如上述掘金抽奖程序可以包含以下测试用例 + +1. 签到的状态需要根据接口返回显示 + +通过 network 查看签到返回如下: + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e78416983c4143198e6d2e41d5d51cbf~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1412&h=598&s=203189&e=png&b=fefefe) + +因此我的签到测试用例代码如下 + +```js +test('签到的状态根据接口返回显示', async ({ page }) => { + await page.goto('https://juejin.cn/user/center/signin') + const promise = await page.waitForResponse((res) => + res.url().includes('/growth_api/v2/get_today_status') + ) + const res = await promise.json() + if (res.data.check_in_done) { + await expect(page.locator('.signedin')).toHaveText('今日已签到') + } else { + await expect(page.locator('.signedin')).toHaveText('立即签到') + await page.getByRole('button', { name: /立即签到/ }).click() + await page.getByRole('button', { name: '去抽奖' }).click() + //调整到抽奖页面 + await expect(page).toHaveURL(/user\/center\/lottery/) + } +}) +``` + +2. 抽奖页面,根据接口返回显示抽奖次数和奖品 + +通过 network,看到抽奖配置接口返回如下: +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a68f8780ec35403db0a83c4f242c99a1~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=946&h=994&s=546528&e=png&b=fdfcfc) + +因此我的测试用例代码如下 + +```js +test('根据接口返回显示抽奖次数', async ({ page }) => { + await page.goto('https://juejin.cn/user/center/lottery') + const promise = await page.waitForResponse((res) => + res.url().includes('/growth_api/v1/lottery_config/get') + ) + const res = await promise.json() + const lotteryNames = res.data.lottery.map((item) => { + if (item.unlock_count === 0) { + return new RegExp(item.lottery_name) + } else { + return new RegExp(`再抽${item.unlock_count}次解锁`) + } + }) + + await expect(page.locator('.item-container .turntable-item')).toHaveText(lotteryNames) + + if (res.data.free_count) { + await expect(page.locator('#turntable-item-0')).toHaveText( + `免费抽奖次数:${res.data.free_count}次` + ) + } else { + await expect(page.locator('#turntable-item-0')).toHaveText('单抽 200') + } +}) +``` + +有了以上断言,我们便可以确保前端页面显示与接口返回显示一致。 + +运行完成后,可以在 playwright-report 查看测试报告。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/20aefc05fb534ff59772117bdb9db80a~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2104&h=562&s=204763&e=png&b=ffffff) + +当然这是我学习 e2e 测试的一个例子,不够准确,也不够详情。 + +## 小结 + +本文介绍了 Playwright 测试框架的入门使用,Playwright 是一个功能强大的端到端(E2E)测试框架,支持多种编程语言 API,适用于现代浏览器,还可用于编写网络爬虫。 + +首先介绍了 Playwright Test for VSCode 插件,以及如何初始化测试项目,如何运行测试用例,并指出可以选择不同的浏览器作为测试环境, +如何添加 Cookie 来模拟登录状态,以及如何使用录制功能来自动生成测试代码。 + +另外,文章强调了使用 Locator 定位器替代语义化定位方法,以提高测试的准确性。最后,我们通过了一个掘金抽奖程序实例强调了断言的重要性,以确保测试代码的可靠性。 diff --git a/src/lib/search.json b/src/lib/search.json new file mode 100644 index 0000000..f7e9bfb --- /dev/null +++ b/src/lib/search.json @@ -0,0 +1,921 @@ +{ + "code": 0, + "message": "0", + "ttl": 1, + "data": { + "list": { + "tlist": null, + "vlist": [ + { + "comment": 235, + "typeid": 230, + "play": 163214, + "pic": "http://i0.hdslb.com/bfs/archive/0416abb3db10c6a927ee87699b8df0b45b6ee335.jpg", + "subtitle": "", + "description": "原文:https://mp.weixin.qq.com/s/HgvakbWICIYWEoc1EDEzfA", + "copyright": "1", + "title": "推荐6款油猴脚本,不看错亿!", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1673933887, + "length": "03:58", + "video_review": 137, + "aid": 777953508, + "bvid": "BV1b14y1M76x", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 8405120, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 83, + "typeid": 230, + "play": 45664, + "pic": "http://i0.hdslb.com/bfs/archive/1095c727412c1c5cd56a520b13dc7a55f459b3be.jpg", + "subtitle": "", + "description": "", + "copyright": "1", + "title": "推荐几种网站部署方案", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1708848955, + "length": "02:27", + "video_review": 104, + "aid": 1801131299, + "bvid": "BV1Mt42187SY", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 4 + }, + { + "comment": 195, + "typeid": 230, + "play": 45232, + "pic": "http://i1.hdslb.com/bfs/archive/692ae925cfd877c09e58171b24182c7a3060c6f2.jpg", + "subtitle": "", + "description": "", + "copyright": "1", + "title": "tailwindcss 新手入门介绍", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1674376590, + "length": "05:00", + "video_review": 115, + "aid": 350646988, + "bvid": "BV1yR4y1a7by", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 8405120, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 29, + "typeid": 230, + "play": 14634, + "pic": "http://i1.hdslb.com/bfs/archive/eb2df5c1eb6e1952f43ce4a4fe57bd6e33f25e49.jpg", + "subtitle": "", + "description": "GitLens \nESLint\nPrettier\nCode Runer\nMaterial Icon Theme\nLive Server\nMarkdown Preview Enhanced\nDocker\nCode GPT\nVue Language Features\nES7+React/React Native snippets\nTailwind CSS IntelliSense\nComment Translate", + "copyright": "1", + "title": "Visual Studio Code 插件推荐", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1676299725, + "length": "04:22", + "video_review": 2, + "aid": 694269115, + "bvid": "BV1P24y1s7AQ", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 71 + }, + { + "comment": 112, + "typeid": 230, + "play": 11731, + "pic": "http://i0.hdslb.com/bfs/archive/01f54af1022ab71d20a485a6911740f90626e60e.jpg", + "subtitle": "", + "description": "网页地址:https://editor.runjs.cool/", + "copyright": "1", + "title": "我开发的 MDX Editor 桌面 App", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1688816107, + "length": "03:16", + "video_review": 3, + "aid": 315719909, + "bvid": "BV1fP411C7YC", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 55, + "typeid": 230, + "play": 6558, + "pic": "http://i0.hdslb.com/bfs/archive/d1dcf56c1cd888c0c15e0c723930508a938567c4.jpg", + "subtitle": "", + "description": "", + "copyright": "1", + "title": "自研、开源、 markdown 笔记软件", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1696516677, + "length": "02:25", + "video_review": 1, + "aid": 661799335, + "bvid": "BV1vh4y167Yb", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16512, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 11 + }, + { + "comment": 51, + "typeid": 230, + "play": 6551, + "pic": "http://i1.hdslb.com/bfs/archive/ee79fc6ba0849bf8a6ec5f7d49da25a02e30c004.jpg", + "subtitle": "", + "description": "博客地址:https://maqib.cn/\n源码:https://github.com/maqi1520/nextjs-tailwind-blog", + "copyright": "1", + "title": "快速免费搭建个人博客:使用 Next.js 和 Tailwind CSS", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1675257293, + "length": "03:48", + "video_review": 66, + "aid": 481163662, + "bvid": "BV1jT411C7Ec", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 8405120, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 25, + "typeid": 230, + "play": 4842, + "pic": "http://i0.hdslb.com/bfs/archive/bd6fcb47f48e411c6665b692666d508fc24806bc.jpg", + "subtitle": "", + "description": "网址:https://tailblocks.cc/\n效果:https://play.tailwindcss.com/Rcu7njIayY?layout=preview", + "copyright": "1", + "title": "如何使用 Tailwind CSS 写官网?", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1676469804, + "length": "02:56", + "video_review": 1, + "aid": 864473804, + "bvid": "BV1454y1P7sa", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 3 + }, + { + "comment": 5, + "typeid": 230, + "play": 4047, + "pic": "http://i2.hdslb.com/bfs/archive/2d4e3f90474b46d9348a8cca4eb42acd2a6d8ab3.jpg", + "subtitle": "", + "description": "欢迎关注前端微信公众号“JS酷”\n大家可以通过这个地址下载安装\nhttps://greasyfork.org/zh-CN/scripts/450504-csdn-%E5%85%8D%E7%99%BB%E5%BD%95%E5%A4%8D%E5%88%B6", + "copyright": "1", + "title": "使用油猴脚本净化 CSDN", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1662006574, + "length": "01:24", + "video_review": 0, + "aid": 645036456, + "bvid": "BV1qY4y1u7XV", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 15, + "typeid": 230, + "play": 2635, + "pic": "http://i2.hdslb.com/bfs/archive/363cbdca0632f318b510a145564e0a2b23429754.jpg", + "subtitle": "", + "description": "网站:https://editor.runjs.cool/", + "copyright": "1", + "title": "自研笔记软件更新", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1710230451, + "length": "01:55", + "video_review": 1, + "aid": 1251715269, + "bvid": "BV1sJ4m1a7K4", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 60 + }, + { + "comment": 5, + "typeid": 230, + "play": 1064, + "pic": "http://i2.hdslb.com/bfs/archive/27f237ba0d232317352a1ec077c5d26bb9ce2f8d.jpg", + "subtitle": "", + "description": "", + "copyright": "1", + "title": "油猴脚本实现去水印", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1676991251, + "length": "02:56", + "video_review": 9, + "aid": 864817889, + "bvid": "BV1F54y1c7yn", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 1 + }, + { + "comment": 4, + "typeid": 230, + "play": 994, + "pic": "http://i1.hdslb.com/bfs/archive/1b7fa05e47c1853fce30d2d27b0fdcfcaadc39a9.jpg", + "subtitle": "", + "description": "开发了一个前端面试刷题小程序,搜索“面试狗”体验", + "copyright": "1", + "title": "前端面试刷题小程序", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1673276078, + "length": "01:42", + "video_review": 0, + "aid": 477620666, + "bvid": "BV18K41117uA", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 8405376, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 3, + "typeid": 230, + "play": 791, + "pic": "http://i0.hdslb.com/bfs/archive/d54a3adfd80729bf6e8781e97f81d893814b11ed.jpg", + "subtitle": "", + "description": "", + "copyright": "1", + "title": "语音转文字+ChatGPT+微软TTS", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1681045539, + "length": "05:20", + "video_review": 0, + "aid": 739815250, + "bvid": "BV16k4y1Y7Vy", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 2113920, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 4, + "typeid": 230, + "play": 728, + "pic": "http://i1.hdslb.com/bfs/archive/503e01999fc63c98f512cf6ca2d1ab8c6260314d.jpg", + "subtitle": "", + "description": "文章:https://juejin.cn/post/7217820487203192892\n代码仓库:https://github.com/maqi1520/openai-helper\n预览地址:https://openai.maqib.cn/", + "copyright": "1", + "title": "GPT3.5 API Tailwind CSS 代码生成器", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1680617301, + "length": "01:05", + "video_review": 0, + "aid": 269559212, + "bvid": "BV1pc411W7zt", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16512, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 4, + "typeid": 230, + "play": 620, + "pic": "http://i0.hdslb.com/bfs/archive/0dfbe34c355a42f24f227e85a75bd5d20617d080.jpg", + "subtitle": "", + "description": "", + "copyright": "1", + "title": "试试免费GPT-4工具 Cursor", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1679324595, + "length": "03:07", + "video_review": 0, + "aid": 226370544, + "bvid": "BV1Gb411o7Re", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 2113920, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 3, + "typeid": 230, + "play": 536, + "pic": "http://i1.hdslb.com/bfs/archive/f77185e59e3d87eb160191ce6c7c8c971309330b.jpg", + "subtitle": "", + "description": "欢迎关注前端微信公众号“JS酷”", + "copyright": "1", + "title": "用 MDX 来给微信公众号排版", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1662006977, + "length": "01:23", + "video_review": 0, + "aid": 560067536, + "bvid": "BV1Ne4y1Y74A", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 6, + "typeid": 230, + "play": 467, + "pic": "http://i0.hdslb.com/bfs/archive/808a5f031808d98aaaa3c3c6df290c730c06eb9c.jpg", + "subtitle": "", + "description": "在线地址:https://ppt.runjs.cool\n文章:https://juejin.cn/post/7129385772561465358\nReact 实现录屏代码:https://code.juejin.cn/pen/7140845249294762017", + "copyright": "1", + "title": "Markdown 实现写 PPT、浏览器录视频原理", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1675519522, + "length": "07:17", + "video_review": 0, + "aid": 481301192, + "bvid": "BV1VT411X76c", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 0, + "typeid": 230, + "play": 407, + "pic": "http://i0.hdslb.com/bfs/archive/6bc2db6b9aed224c2dce1bd8bccfe37070fec3c2.jpg", + "subtitle": "", + "description": "稀土掘金:https://juejin.cn/column/7140121965360054308\n欢迎关注前端微信公众号“JS酷”", + "copyright": "1", + "title": "掘金专栏《Next.js 全栈开发实战》", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1673173870, + "length": "02:25", + "video_review": 0, + "aid": 904961859, + "bvid": "BV1WP4y1C75F", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 6, + "typeid": 230, + "play": 382, + "pic": "http://i1.hdslb.com/bfs/archive/efdacc3a132baf41a4ef781e7ddd43cbc860a708.jpg", + "subtitle": "", + "description": "使用云函数白嫖掘金马克杯\nhttps://juejin.cn/post/7130878015042502670", + "copyright": "1", + "title": "使用云函数白嫖掘金马克杯", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1667722466, + "length": "05:22", + "video_review": 0, + "aid": 219770372, + "bvid": "BV1H8411h72f", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 1, + "typeid": 230, + "play": 353, + "pic": "http://i0.hdslb.com/bfs/archive/69ebeda9482bbd4cf2e7b7bfeabe48883e98290c.jpg", + "subtitle": "", + "description": "欢迎关注前端微信公众号“JS酷”\n我开发了一个微信排版工具 editor.runjs.cool \n使用 mdx 来排版,欢迎大家体验和使用。", + "copyright": "1", + "title": "【MDX editor】微信排版工具新选择", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1648941983, + "length": "04:13", + "video_review": 0, + "aid": 980285762, + "bvid": "BV1K44y1P7at", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 1, + "typeid": 230, + "play": 309, + "pic": "http://i1.hdslb.com/bfs/archive/3f699ca198ced5f398d7e459c8a183043b9f05ee.jpg", + "subtitle": "", + "description": "代码:https://juejin.cn/post/7078206989402112037", + "copyright": "1", + "title": "我用 Nodejs 爬了一万多张小姐姐壁纸", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1673358260, + "length": "00:41", + "video_review": 0, + "aid": 477610587, + "bvid": "BV1tK41117sP", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 1, + "typeid": 230, + "play": 274, + "pic": "http://i2.hdslb.com/bfs/archive/9edff3a734d596150e76edf0f79eddb093790c35.jpg", + "subtitle": "", + "description": "-", + "copyright": "1", + "title": "【油猴脚本】掘金文章一键拷贝到微信公众平台", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1644288015, + "length": "01:06", + "video_review": 0, + "aid": 636278349, + "bvid": "BV1kb4y1j7RS", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 8405376, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 2, + "typeid": 230, + "play": 222, + "pic": "http://i0.hdslb.com/bfs/archive/ef0fb56bf79f504dd50959fab1f91c965e24bc08.jpg", + "subtitle": "", + "description": "稀土掘金:https://juejin.cn/post/7179581995259789372\n欢迎关注前端微信公众号“JS酷”", + "copyright": "1", + "title": "Chrome 插件七牛云图床", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1673102916, + "length": "02:59", + "video_review": 0, + "aid": 477398856, + "bvid": "BV1aK411y7iD", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16768, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 0, + "typeid": 230, + "play": 220, + "pic": "http://i2.hdslb.com/bfs/archive/647c3eba5d8ecad9b4115b9e099d5480a900340a.jpg", + "subtitle": "", + "description": "-", + "copyright": "1", + "title": "年会中了二等奖", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1673666917, + "length": "01:15", + "video_review": 0, + "aid": 350333234, + "bvid": "BV1ZR4y1Y7kL", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 8405120, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + }, + { + "comment": 0, + "typeid": 230, + "play": 144, + "pic": "http://i0.hdslb.com/bfs/archive/4b96d4b3772ee61c025681fcda3aa9ae4af704d9.jpg", + "subtitle": "", + "description": "稀土掘金:https://juejin.cn/post/7116055375371042830\n欢迎关注前端微信公众号“JS酷”", + "copyright": "1", + "title": "Iconfont 还是不能上传,如何维护你的 Icon?", + "review": 0, + "author": "狂奔滴小馬", + "mid": 43664526, + "created": 1662007457, + "length": "09:43", + "video_review": 0, + "aid": 942521799, + "bvid": "BV1kW4y1t7Jw", + "hide_click": false, + "is_pay": 0, + "is_union_video": 0, + "is_steins_gate": 0, + "is_live_playback": 0, + "is_lesson_video": 0, + "is_lesson_finished": 0, + "lesson_update_info": "", + "jump_url": "", + "meta": null, + "is_avoided": 0, + "season_id": 0, + "attribute": 16512, + "is_charging_arc": false, + "vt": 0, + "enable_vt": 0, + "vt_display": "", + "playback_position": 0 + } + ], + "slist": [] + }, + "page": { "pn": 1, "ps": 25, "count": 25 }, + "episodic_button": { + "text": "播放全部", + "uri": "//www.bilibili.com/medialist/play/43664526?from=space" + }, + "is_risk": false, + "gaia_res_type": 0, + "gaia_data": null + } +} diff --git a/src/lib/video.ts b/src/lib/video.ts index f7d3c02..843a1a7 100644 --- a/src/lib/video.ts +++ b/src/lib/video.ts @@ -1,5 +1,6 @@ import fs from 'fs' import http from 'http' +import res from './search.json' export type VItem = { bvid: string @@ -20,31 +21,20 @@ interface JSONResponse { function download(url: string) { const items = url.split('/') - return new Promise((reslove) => { + return new Promise((resolve) => { http.get(url + '@640w_400h', (res) => { - const path = `./public/static/images/${items[items.length - 1]}` + const path = `./public/static/images/video/${items[items.length - 1]}` const filePath = fs.createWriteStream(path) res.pipe(filePath) filePath.on('finish', () => { filePath.close() - reslove(items[items.length - 1]) + resolve(items[items.length - 1]) }) }) }) } export async function getVideos() { - const response = await fetch( - 'https://api.bilibili.com/x/space/wbi/arc/search?mid=43664526&ps=30&tid=0&pn=1&keyword=&order=pubdate&platform=web&web_location=1550101&order_avoided=true&w_rid=189e3168c6d493b6ecf6c9c205cbd0e3&wts=1689815990', - { - headers: { - 'user-agent': - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36', - }, - } - ) - const res: JSONResponse = await response.json() - const videos = res.data.list.vlist for (let index = 0; index < videos.length; index++) { diff --git a/src/pages/video.tsx b/src/pages/video.tsx index 9bc8fa6..79b7a53 100644 --- a/src/pages/video.tsx +++ b/src/pages/video.tsx @@ -10,7 +10,7 @@ export const getStaticProps: GetStaticProps<{ videos: VItem[] }> = async () => { return { props: { videos: JSON.parse(JSON.stringify(videos)) } } } -export default function Vodeo({ videos }: InferGetStaticPropsType) { +export default function Video({ videos }: InferGetStaticPropsType) { return ( <> @@ -37,7 +37,7 @@ export default function Vodeo({ videos }: InferGetStaticPropsType