匹配范围
*://m.okjike.com/*jike-post-detail用途 读取即刻移动公开帖子详情页的 SSR 数据,提取帖子正文、作者、互动数量和公开评论。 执行前提 在 https://m.okjike.com/* 页面执行。当前页是目标帖子详情页时会直接读取页面内的 #_NEXTDATA_;如果传入 postid 或 postId,脚本会请求对应公开帖子页并解析 HTML。 脚本只读取公开页面已经下发的 SSR 数据…
*://m.okjike.com/*dom.read1return await (async () => {2 const input = typeof params === "object" && params ? params : {};3 const current = new URL(location.href);4 const currentMatch = current.pathname.match(/^\/(originalPosts|reposts)\/([^/?#]+)/);5 const pickedPostId = input.post_id || input.postId || "";6 const postId = String(pickedPostId || (currentMatch ? currentMatch[2] : "")).trim();7 const commentValue = Number(input.comments_limit ?? input.commentsLimit ?? 20);8 const commentsLimit = Math.max(1, Math.min(50, Number.isFinite(commentValue) ? Math.floor(commentValue) : 20));9 10 if (!postId) {11 throw new Error("未提供 post_id,且当前页面不是即刻帖子详情页。请打开 /originalPosts/<post_id> 或传入 post_id。");12 }13 14 const currentKind = currentMatch ? currentMatch[1] : "originalPosts";15 const targetKind = currentMatch && currentMatch[2] === postId ? currentKind : "originalPosts";16 const targetUrl = `https://m.okjike.com/${targetKind}/${encodeURIComponent(postId)}`;17 const canUseCurrentPage = current.origin === "https://m.okjike.com" && currentMatch && currentMatch[2] === postId;18 19 const getNextDataFromDoc = (doc) => {20 const node = doc.querySelector("script#__NEXT_DATA__");21 if (!node || !node.textContent) return null;22 try {23 return JSON.parse(node.textContent);24 } catch (error) {25 throw new Error(`公开页 SSR 数据解析失败:${error.message}`);26 }27 };28 29 const loadNextData = async () => {30 if (canUseCurrentPage) {31 const currentData = getNextDataFromDoc(document);32 if (currentData) return currentData;33 }34 35 const response = await fetch(targetUrl, {36 credentials: "include",37 headers: { accept: "text/html,application/xhtml+xml" },38 });39 if (!response.ok) {40 throw new Error(`帖子公开页读取失败:HTTP ${response.status}。页面可能不存在、需要登录或被站点限制。`);41 }42 const html = await response.text();43 const doc = new DOMParser().parseFromString(html, "text/html");44 const fetchedData = getNextDataFromDoc(doc);45 if (!fetchedData) {46 throw new Error("帖子公开页没有可读取的 SSR 数据。页面可能改版、需要登录或仅在客户端加载。");47 }48 return fetchedData;49 };50 51 const compactText = (value) => {52 if (value == null) return "";53 return String(value).replace(/\s+/g, " ").trim();54 };55 56 const authorOf = (user) => {57 if (!user || typeof user !== "object") return null;58 return {59 id: user.id || user.userId || "",60 username: user.username || "",61 name: user.screenName || user.nickname || user.name || "",62 bio: user.briefIntro || "",63 };64 };65 66 const postLinkOf = (post) => {67 if (!post || !post.id) return targetUrl;68 const type = String(post.type || "").toUpperCase();69 const segment = type === "REPOST" ? "reposts" : "originalPosts";70 return `https://m.okjike.com/${segment}/${encodeURIComponent(post.id)}`;71 };72 73 const pictureUrls = (pictures) => {74 if (!Array.isArray(pictures)) return [];75 return pictures.map((picture) => picture.picUrl || picture.middlePicUrl || picture.smallPicUrl || picture.thumbnailUrl || "").filter(Boolean);76 };77 78 const commentOf = (comment) => ({79 id: comment.id || "",80 author: authorOf(comment.user),81 content: compactText(comment.content),82 likes: Number(comment.likeCount || 0),83 replies: Number(comment.replyCount || 0),84 time: comment.createdAt || "",85 pictures: pictureUrls(comment.pictures),86 hot_replies: Array.isArray(comment.hotReplies)87 ? comment.hotReplies.map((reply) => ({88 id: reply.id || "",89 author: authorOf(reply.user),90 content: compactText(reply.content),91 likes: Number(reply.likeCount || 0),92 time: reply.createdAt || "",93 }))94 : [],95 });96 97 const data = await loadNextData();98 const pageProps = data && data.props && data.props.pageProps ? data.props.pageProps : {};99 const post = pageProps.post || null;100 const comments = Array.isArray(pageProps.comments) ? pageProps.comments : [];101 102 if (!post || !post.id) {103 throw new Error("未在公开页 SSR 数据中找到帖子详情。页面可能改版、需要登录或帖子不可公开访问。");104 }105 106 return {107 post: {108 id: post.id || postId,109 author: authorOf(post.user),110 content: compactText(post.content),111 likes: Number(post.likeCount || 0),112 comments: Number(post.commentCount || 0),113 time: post.createdAt || "",114 url: postLinkOf(post),115 topic: post.topic116 ? {117 id: post.topic.id || "",118 title: post.topic.content || post.topic.title || post.topic.messagePrefix || "",119 }120 : null,121 pictures: pictureUrls(post.pictures),122 },123 comments: comments.slice(0, commentsLimit).map(commentOf),124 };125})();