feat: add an router for getting all collections of a user in zhihu.com. (#18028)

* 添加知乎路由,用于获取指定用户所有公开的收藏内容

* 修改example地址

* 修改代码以符合路由规范

* Update lib/routes/zhihu/all-collections.ts

Co-authored-by: Tony <TonyRL@users.noreply.github.com>

* 修改代码以符合路由规范和代码审阅者要求的got请求代码格式统一和re-cast的问题

---------

Co-authored-by: yuerongkang <yuerongkang@drivedream.com>
This commit is contained in:
CohenV 2025-01-07 02:59:37 +08:00 committed by GitHub
parent 1e246f25a4
commit c405ee80ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 135 additions and 0 deletions

View File

@ -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' ? `<img src=${content.video.url}/>` : content.content,
pubDate: parseDate((content.type === 'article' ? content.updated : content.updated_time) * 1000),
category: [item.collectionTitle],
};
}),
};
}

View File

@ -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;
};
}