From c405ee80ee7f5af6f41947a8f32f5b7d12974bed Mon Sep 17 00:00:00 2001 From: CohenV <48265999+Healthyyue@users.noreply.github.com> Date: Tue, 7 Jan 2025 02:59:37 +0800 Subject: [PATCH] feat: add an router for getting all collections of a user in zhihu.com. (#18028) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加知乎路由,用于获取指定用户所有公开的收藏内容 * 修改example地址 * 修改代码以符合路由规范 * Update lib/routes/zhihu/all-collections.ts Co-authored-by: Tony * 修改代码以符合路由规范和代码审阅者要求的got请求代码格式统一和re-cast的问题 --------- Co-authored-by: yuerongkang --- lib/routes/zhihu/all-collections.ts | 109 ++++++++++++++++++++++++++++ lib/routes/zhihu/types.ts | 26 +++++++ 2 files changed, 135 insertions(+) create mode 100644 lib/routes/zhihu/all-collections.ts diff --git a/lib/routes/zhihu/all-collections.ts b/lib/routes/zhihu/all-collections.ts new file mode 100644 index 00000000000000..31d0e0c3859206 --- /dev/null +++ b/lib/routes/zhihu/all-collections.ts @@ -0,0 +1,109 @@ +import { Route, ViewType, Collection, CollectionItem } from '@/types'; +import got from '@/utils/got'; +import { header } from './utils'; +import { parseDate } from '@/utils/parse-date'; +import cache from '@/utils/cache'; + +export const route: Route = { + path: '/people/allCollections/:id', + categories: ['social-media'], + view: ViewType.Articles, + example: '/zhihu/people/allCollections/87-44-49-67', + parameters: { id: '作者 id,可在用户主页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['www.zhihu.com/people/:id'], + // target: 'people/allCollections/:id', + }, + ], + name: '用户全部收藏内容', + maintainers: ['Healthyyue'], + handler, +}; + +async function handler(ctx) { + const id = ctx.req.param('id'); + const apiPath = `https://api.zhihu.com/people/${id}/collections`; + + const response = await got(apiPath, { + headers: { + Referer: `https://www.zhihu.com/people/${id}/collections`, + }, + }); + + const collections = response.data.data as Collection[]; + + const allCollectionItems = await Promise.all( + collections.map(async (collection) => { + const firstPageResponse = await got(`https://www.zhihu.com/api/v4/collections/${collection.id}/items?offset=0&limit=20`, { + headers: { + ...header, + Referer: `https://www.zhihu.com/collection/${collection.id}`, + }, + }); + + const { + data: items, + paging: { totals }, + } = firstPageResponse.data; + + if (totals > 20) { + const offsetList = Array.from({ length: Math.ceil(totals / 20) - 1 }, (_, index) => (index + 1) * 20); + + const otherPages = await Promise.all( + offsetList.map((offset) => + cache.tryGet(`https://www.zhihu.com/api/v4/collections/${collection.id}/items?offset=${offset}&limit=20`, async () => { + const response = await got(`https://www.zhihu.com/api/v4/collections/${collection.id}/items?offset=${offset}&limit=20`, { + headers: { + ...header, + Referer: `https://www.zhihu.com/collection/${collection.id}`, + }, + }); + return response.data.data; + }) + ) + ); + + items.push(...otherPages.flat()); + } + + return { + collectionId: collection.id, + collectionTitle: collection.title, + items, + }; + }) + ); + + const items = allCollectionItems.flatMap( + (collection) => + collection.items.map((item) => ({ + ...item, + collectionTitle: collection.collectionTitle, + })) as CollectionItem[] + ); + + return { + title: `${collections[0].creator.name}的知乎收藏`, + link: `https://www.zhihu.com/people/${id}/collections`, + item: items.map((item) => { + const content = item.content; + + return { + title: content.type === 'article' || content.type === 'zvideo' ? content.title : content.question.title, + link: content.url, + description: content.type === 'zvideo' ? `` : content.content, + pubDate: parseDate((content.type === 'article' ? content.updated : content.updated_time) * 1000), + category: [item.collectionTitle], + }; + }), + }; +} diff --git a/lib/routes/zhihu/types.ts b/lib/routes/zhihu/types.ts index 7d685f5cc9070c..d03563c5311bd9 100644 --- a/lib/routes/zhihu/types.ts +++ b/lib/routes/zhihu/types.ts @@ -102,3 +102,29 @@ export interface Articles { }; data: Article[]; } + +export interface CollectionItem { + content: { + type: string; + title?: string; + question?: { + title: string; + }; + url: string; + content: string; + video?: { + url: string; + }; + updated?: number; + updated_time?: number; + }; + collectionTitle?: string; +} + +export interface Collection { + id: string; + title: string; + creator: { + name: string; + }; +}