匹配范围
*://movie.douban.com/top250*douban-movie-top250豆瓣电影 Top250 提取 用途 读取豆瓣电影 Top250 榜单,返回电影排名、条目 ID、片名、评分和详情页地址。 执行前提 先打开 https://movie.douban.com/top250 页面,脚本会在同一站点内按分页读取榜单。页面公开可访问,但站点可能根据访问频率、Cookie 状态或验证页限制结果。 参数 limit:可选,返回数量,整…
*://movie.douban.com/top250*dom.read1const rawParams = params && typeof params === "object" ? params : {};2 3function integerParam(name, fallback, min, max) {4 const raw = rawParams[name];5 const value = raw === undefined || raw === null || raw === "" ? fallback : Number(raw);6 if (!Number.isInteger(value) || value < min || value > max) {7 throw new Error(`${name} must be an integer between ${min} and ${max}`);8 }9 return value;10}11 12function normalizeStart(value) {13 const normalized = Math.floor(value / 25) * 25;14 return Math.min(225, Math.max(0, normalized));15}16 17function sleep(ms) {18 return new Promise(resolve => setTimeout(resolve, ms));19}20 21function pageStartFromUrl(url) {22 try {23 const parsed = new URL(url);24 return Number(parsed.searchParams.get("start") || "0") || 0;25 } catch {26 return 0;27 }28}29 30function normalizeUrl(href, baseUrl) {31 if (!href) return "";32 try {33 return new URL(href, baseUrl || "https://movie.douban.com/top250").toString();34 } catch {35 return "";36 }37}38 39function parseTop250Page(doc, baseUrl, results, limit) {40 const items = doc.querySelectorAll(".item");41 for (const item of items) {42 if (results.length >= limit) break;43 44 const rankText = item.querySelector(".pic em")?.textContent || "";45 const linkEl = item.querySelector("a[href*='/subject/'], a[href*=\"/subject/\"]");46 const titleEl = item.querySelector(".title");47 const ratingText = item.querySelector(".rating_num")?.textContent || "";48 const url = normalizeUrl(linkEl?.getAttribute("href") || linkEl?.href || "", baseUrl);49 const id = url.match(/\/subject\/(\d+)\//)?.[1] || "";50 const title = (titleEl?.textContent || "").trim();51 const rank = Number.parseInt(rankText, 10);52 const rating = Number.parseFloat(ratingText);53 54 if (!id || !title) continue;55 56 results.push({57 rank: Number.isFinite(rank) ? rank : results.length + 1,58 id,59 title,60 rating: Number.isFinite(rating) ? rating : 0,61 url,62 });63 }64}65 66const limit = integerParam("limit", 250, 1, 250);67const start = normalizeStart(integerParam("start", 0, 0, 225));68const results = [];69 70for (let offset = start; offset < 250 && results.length < limit; offset += 25) {71 const pageUrl = `https://movie.douban.com/top250?start=${offset}`;72 const canUseCurrentPage =73 location.hostname === "movie.douban.com" &&74 location.pathname === "/top250" &&75 pageStartFromUrl(location.href) === offset;76 77 if (canUseCurrentPage) {78 parseTop250Page(document, location.href, results, limit);79 } else {80 const response = await fetch(pageUrl, { credentials: "include" });81 if (!response.ok) {82 throw new Error(`Failed to fetch ${pageUrl}: HTTP ${response.status}`);83 }84 85 const html = await response.text();86 const page = new DOMParser().parseFromString(html, "text/html");87 parseTop250Page(page, pageUrl, results, limit);88 }89 90 if (results.length < limit) {91 await sleep(150);92 }93}94 95return results.slice(0, limit);