灵猴市集Linghou Market

豆瓣电影 Top250

豆瓣douban-movie-top250

豆瓣电影 Top250 提取 用途 读取豆瓣电影 Top250 榜单,返回电影排名、条目 ID、片名、评分和详情页地址。 执行前提 先打开 https://movie.douban.com/top250 页面,脚本会在同一站点内按分页读取榜单。页面公开可访问,但站点可能根据访问频率、Cookie 状态或验证页限制结果。 参数 limit:可选,返回数量,整…

电影榜单内容提取
版本1.0.0
扫描passed
更新2026-06-28
超时30000ms

匹配范围

*://movie.douban.com/top250*

排除范围

未声明

能力声明

dom.read

代码

源码已展开
1const 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);