匹配范围
*://*.bilibili.com/*bilibili-video-search用途 搜索哔哩哔哩公开视频或用户,返回适合继续打开详情页或用户主页的简明结果。 执行前提 需要在 bilibili.com 页面执行。脚本通过页面环境请求公开接口,接口可能因为风控、频率、地域或权限限制返回失败。 参数 query:必填,搜索关键词。 type:可选,video 或 user,默认 video。 page:可选,页码,默认 1,范围 1 到…
*://*.bilibili.com/*dom.read1const input = (typeof params === "object" && params !== null) ? params : {};2const host = String(location.hostname || "").toLowerCase();3 4if (!/(^|\.)bilibili\.com$/.test(host)) {5 throw new Error("请在 bilibili.com 页面执行。");6}7 8function readQuery(value) {9 if (typeof value !== "string") {10 throw new Error("query 为必填字符串。");11 }12 const text = value.trim();13 if (!text) {14 throw new Error("query 不能为空。");15 }16 return text;17}18 19function parseType(value) {20 if (value === undefined || value === null || value === "") return "video";21 if (value !== "video" && value !== "user") {22 throw new Error("type 只能是 video 或 user。");23 }24 return value;25}26 27function parseInteger(value, field, defaultValue, min, max) {28 if (value === undefined || value === null || value === "") return defaultValue;29 const number = Number(value);30 if (!Number.isInteger(number) || number < min || number > max) {31 throw new Error(field + " 必须是 " + min + " 到 " + max + " 之间的整数。");32 }33 return number;34}35 36function cleanText(value) {37 return String(value ?? "")38 .replace(/<[^>]*>/g, "")39 .replace(/ /g, " ")40 .replace(/&/g, "&")41 .replace(/"/g, '"')42 .replace(/'/g, "'")43 .replace(/</g, "<")44 .replace(/>/g, ">")45 .replace(/&#(\d+);/g, (_, code) => String.fromCharCode(Number(code)))46 .replace(/&#x([0-9a-fA-F]+);/g, (_, code) => String.fromCharCode(parseInt(code, 16)))47 .replace(/\s+/g, " ")48 .trim();49}50 51function extractBvid(value) {52 const match = String(value || "").match(/(?:\/video\/)?(BV[0-9A-Za-z]{10,})/);53 return match ? match[1] : "";54}55 56const query = readQuery(input.query);57const type = parseType(input.type);58const page = parseInteger(input.page, "page", 1, 1, 20);59const limit = parseInteger(input.limit, "limit", 20, 1, 50);60const searchType = type === "user" ? "bili_user" : "video";61const apiUrl = "https://api.bilibili.com/x/web-interface/wbi/search/type"62 + "?search_type=" + encodeURIComponent(searchType)63 + "&keyword=" + encodeURIComponent(query)64 + "&page=" + encodeURIComponent(String(page));65const response = await fetch(apiUrl, { credentials: "include" });66 67if (!response.ok) {68 throw new Error("搜索失败,接口状态码:" + response.status + "。");69}70 71const payload = await response.json();72 73if (!payload || payload.code !== 0 || !payload.data) {74 const message = payload && payload.message ? payload.message : "未知错误";75 throw new Error("搜索失败:" + message + "。");76}77 78const result = Array.isArray(payload.data.result) ? payload.data.result : [];79 80if (type === "user") {81 return result.slice(0, limit).map((item, index) => {82 const mid = item.mid ?? item.id ?? null;83 return {84 rank: index + 1,85 name: cleanText(item.uname ?? item.name ?? ""),86 sign: cleanText(item.usign ?? item.sign ?? ""),87 fans: item.fans ?? item.fans_count ?? 0,88 mid,89 url: mid ? "https://space.bilibili.com/" + mid : ""90 };91 });92}93 94return result.slice(0, limit).map((item, index) => {95 const bvid = item.bvid || extractBvid(item.arcurl);96 return {97 rank: index + 1,98 title: cleanText(item.title || ""),99 author: cleanText(item.author || item.uname || ""),100 play: item.play ?? 0,101 duration: item.duration || "",102 bvid,103 url: bvid ? "https://www.bilibili.com/video/" + bvid : (item.arcurl || "")104 };105});