匹配范围
*://*.juejin.cn/*juejin-recommend-articles掘金推荐文章 用途 读取掘金首页推荐文章流,返回文章标题、摘要、阅读数、点赞数、评论数、作者、标签、文章链接和下一页游标。 执行前提 请在 juejin.cn 页面执行。该脚本读取公开推荐接口;通常不要求登录,但推荐流可能随时间、地区、风控策略或接口变更而变化。 参数 limit:可选,返回数量,整数,范围 1-100,默认 20。 cursor:可选,分…
*://*.juejin.cn/*dom.read1const input = typeof params === 'object' && params !== null ? params : {};2 3function readLimit(value, fallback, max) {4 const raw = value == null || value === '' ? fallback : value;5 const n = typeof raw === 'number'6 ? raw7 : typeof raw === 'string' && /^[1-9]\d*$/.test(raw)8 ? Number(raw)9 : NaN;10 if (!Number.isSafeInteger(n) || n < 1 || n > max) {11 throw new Error(`limit must be an integer from 1 to ${max}`);12 }13 return n;14}15 16function readCursor(value, fallback) {17 const raw = value == null || value === '' ? fallback : value;18 if (typeof raw === 'number' && Number.isSafeInteger(raw) && raw >= 0) {19 return String(raw);20 }21 if (typeof raw === 'string' && /^(0|[1-9]\d*)$/.test(raw)) {22 return raw;23 }24 throw new Error('cursor must be a non-negative integer string');25}26 27function readResponseCursor(value) {28 if (value == null || value === '') {29 return '';30 }31 return readCursor(value, '0');32}33 34function readOptionalNumber(value) {35 if (value == null || value === '') {36 return null;37 }38 const n = Number(value);39 if (!Number.isFinite(n)) {40 return null;41 }42 return n;43}44 45function readArticleId(value) {46 const id = String(value || '').trim();47 if (!/^\d{16,20}$/.test(id)) {48 throw new Error('API returned a malformed article id');49 }50 return id;51}52 53function mapArticle(row, rank, nextCursor, hasMore) {54 const info = row?.item_info || {};55 const article = info.article_info || {};56 const author = info.author_user_info || {};57 const tags = Array.isArray(info.tags)58 ? info.tags.map((tag) => tag?.tag_name).filter(Boolean).slice(0, 6).join(', ')59 : '';60 const articleId = readArticleId(article.article_id);61 return {62 rank,63 article_id: articleId,64 title: String(article.title || '').trim(),65 brief: String(article.brief_content || '').trim(),66 views: readOptionalNumber(article.view_count),67 likes: readOptionalNumber(article.digg_count),68 comments: readOptionalNumber(article.comment_count),69 author: String(author.user_name || '').trim(),70 tags,71 url: `https://juejin.cn/post/${articleId}`,72 next_cursor: nextCursor,73 has_more: hasMore,74 };75}76 77async function fetchJson(url, body) {78 const response = await fetch(url, {79 method: 'POST',80 credentials: 'include',81 headers: {82 accept: 'application/json',83 'content-type': 'application/json',84 },85 body: JSON.stringify(body),86 });87 if (response.status === 429) {88 throw new Error('request was rate limited');89 }90 if (!response.ok) {91 throw new Error(`request failed with HTTP ${response.status}`);92 }93 const payload = await response.json();94 if (!payload || typeof payload !== 'object' || Array.isArray(payload) || !Object.prototype.hasOwnProperty.call(payload, 'err_no')) {95 throw new Error('API returned a malformed response');96 }97 if (payload.err_no !== 0) {98 throw new Error(`API returned err_no ${payload.err_no}: ${payload.err_msg || ''}`);99 }100 return payload;101}102 103const limit = readLimit(input.limit, 20, 100);104const cursor = readCursor(input.cursor, '0');105const payload = await fetchJson('https://api.juejin.cn/recommend_api/v1/article/recommend_all_feed', {106 id_type: 2,107 client_type: 2608,108 sort_type: 200,109 limit,110 cursor,111});112 113const rows = Array.isArray(payload.data) ? payload.data : [];114if (rows.length === 0) {115 throw new Error('API returned no articles');116}117const nextCursor = readResponseCursor(payload.cursor);118if (payload.has_more != null && typeof payload.has_more !== 'boolean') {119 throw new Error('API returned a malformed has_more flag');120}121if (payload.has_more === true && !nextCursor) {122 throw new Error('API returned has_more without a next cursor');123}124const hasMore = payload.has_more == null ? '' : String(payload.has_more);125 126return rows.slice(0, limit).map((row, index) => mapArticle(row, index + 1, nextCursor, hasMore));