UNPKG

rsshub

Version:
278 lines (267 loc) 9.3 kB
import { Route, DataItem } from '@/types'; import ofetch from '@/utils/ofetch'; import { load, Cheerio, AnyNode } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import { Context } from 'hono'; import cache from '@/utils/cache'; interface Post extends DataItem { external: boolean; } const baseUrl = 'https://rsgis.whu.edu.cn'; const categoryMap = { index: { name: '首页', path: '', }, xyxw: { name: '学院新闻', path: 'xyxw1', sub: { xyyw: { name: '学院要闻', path: 'xyyw2', }, hzjl: { name: '合作交流', path: 'hzjl', }, mtjj: { name: '媒体聚焦', path: 'mtjj', }, xgyw: { name: '学工要闻', path: 'xgyw', }, }, }, kxyj: { name: '科学研究', path: 'kxyj', sub: { xsbg: { name: '学术报告', path: 'xsbg', }, xsjl: { name: '学术交流', path: 'xsjl', }, kycg: { name: '学术成果', path: 'kycg', }, sbxx: { name: '申报信息', path: 'sbxx', }, }, }, tzgg: { name: '通知公告', path: 'tzgg1', sub: { xytz: { name: '学院通知', path: 'xytz', }, jxdt: { name: '教学动态', path: 'jxdt', }, xsdt: { name: '学术动态', path: 'xsdt', }, rcyj: { name: '人才引进', path: 'rcyj', }, }, }, }; /** * Check whether the link is external. * * @param link Post link * @returns Whether or not weixin post */ function checkExternal(link: string): boolean { const matchWeixin = link.match(/^((http:\/\/)|(https:\/\/))?([\dA-Za-z]([\dA-Za-z-]{0,61}[\dA-Za-z])?\.)+[A-Za-z]{2,6}(\/)/); return !!matchWeixin?.length; } /** * Get information from a list of paired link and date. * * @param element * @returns A list of RSS meta node. */ function parseListLinkDateItem(element: Cheerio<AnyNode>, currentUrl: string) { const linkElement = element.find('a').first(); const title = linkElement.text(); const href = linkElement.attr('href'); if (href === undefined) { throw new Error('Cannot get link'); } const external = checkExternal(href); const link = external ? href : new URL(href, currentUrl).href; const pubDate = element.find('div.date1').first().text(); return { title, link, pubDate: timezone(parseDate(pubDate, 'YYYY-MM-DD'), +8), description: title, external, }; } async function getDetail(item: Post): Promise<DataItem | any> { const link = item.link; return link ? await cache.tryGet(`whu:rsgis:${link}`, async () => { if (item.external) { item.description = `<a href="${link}">阅读原文</a>`; } else { const response = await ofetch(link); const $ = load(response); const title = $('div.content div.content_title h1').first().text(); const content = $('div.content div.v_news_content').first().html(); item.title = title; item.description = content || ''; } return item; }) : item; } /** * Process index type. */ async function handleIndex(): Promise<Array<Post>> { const url = `${baseUrl}/index.htm`; const response = await ofetch(url); const $ = load(response); // 学院新闻 const xyxwList: Array<Post> = $('div.main1 > div.newspaper:nth-child(1) > div.newspaper_list > ul > li') .toArray() .map((item) => parseListLinkDateItem($(item), baseUrl)); // 通知公告 const tzggList: Array<Post> = $('div.main1 > div.newspaper:nth-child(2) > div.newspaper_list > ul > li') .toArray() .map((item) => parseListLinkDateItem($(item), baseUrl)); // 学术动态 const xsdtList: Array<Post> = $('div.main3 div.inner > div.newspaper:nth-child(1) > ul.newspaper_list2 > li:nth-child(1) > ul > li') .toArray() .map((item) => parseListLinkDateItem($(item), baseUrl)); // 学术进展 const xsjzList: Array<Post> = $('div.main3 div.inner > div.newspaper:nth-child(1) > ul.newspaper_list2 > li:nth-child(2) > ul > li') .toArray() .map((item) => parseListLinkDateItem($(item), baseUrl)); // 教学动态 const jxdtList: Array<Post> = $('div.main3 div.inner > div.newspaper:nth-child(2) > div.newspaper_list2 > ul > li') .toArray() .map((item) => parseListLinkDateItem($(item), baseUrl)); // 学工动态 const xgdtList: Array<Post> = $('div.main3 div.inner > div.newspaper:nth-child(3) > div.newspaper_list2 > ul > li') .toArray() .map((item) => parseListLinkDateItem($(item), baseUrl)); // 组合所有新闻 const fullList = await Promise.all([...xyxwList, ...tzggList, ...xsdtList, ...xsjzList, ...jxdtList, ...xgdtList].map(async (item) => await getDetail(item))); return fullList; } /** * Process non-index types. * * @param type Level 1 type * @param sub Level 2 type */ async function handlePostList(type: string, sub: string): Promise<Array<DataItem>> { let urlList: Array<{ url: string; base: string }> = []; const category = categoryMap[type]; if (sub === 'all') { const subMap = category.sub; urlList = Object.keys(subMap).map((key) => { const subType = subMap[key]; return { url: `${baseUrl}/${category.path}/${subType.path}.htm`, base: `${baseUrl}/${category.path}`, }; }); } else if (sub in category.sub) { urlList.push({ url: `${baseUrl}/${category.path}/${category.sub[sub].path}.htm`, base: `${baseUrl}/${category.path}`, }); } else { throw new Error('No such sub type.'); } const urlPosts = await Promise.all( urlList.map(async (url) => { const response = await ofetch(url.url); const $ = load(response); return $('div.neiinner > div.nav_right > div.right_inner > div.list > ul > li') .toArray() .map((item) => parseListLinkDateItem($(item), url.base)); }) ); const fullList = await Promise.all(urlPosts.flat().map(async (item) => await getDetail(item))); return fullList; } export const route: Route = { path: '/rsgis/:type/:sub?', categories: ['university'], example: '/whu/rsgis/index', parameters: { type: '栏目,详见表格', sub: '子栏目。当 `type` 为 `index` 的时候不起效;当 `type` 为其他合法值时,默认为 `all` 表示所有子类,其他可选项见下表。', }, radar: [ { source: ['rsgis.whu.edu.cn/index.htm'], target: '/rsgis/index', }, ], name: '武汉大学遥感信息工程学院', maintainers: ['HPDell'], description: ` | 分类名 | \`type\` 值 | 子类名 | \`sub\` 值 | | :------: | :-------- | :------: | :------- | | 首页 | \`index\` | | | | 学院新闻 | \`xyxw\` | 全部 | \`all\` | | | | 学院要闻 | \`xyyw\` | | | | 合作交流 | \`hzjl\` | | | | 媒体聚焦 | \`mtjj\` | | | | 学工要闻 | \`xgyw\` | | 科学研究 | \`kxyj\` | 全部 | \`all\` | | | | 学术报告 | \`xsbg\` | | | | 学术交流 | \`xsjl\` | | | | 学术成果 | \`kycg\` | | | | 申报信息 | \`sbxx\` | | 通知公告 | \`tzgg\` | 全部 | \`all\` | | | | 学院通知 | \`xytz\` | | | | 教学动态 | \`jxdt\` | | | | 学术动态 | \`xsdt\` | | | | 人才引进 | \`rcyj\` | `, handler: async (ctx: Context) => { const { type = 'index', sub = 'all' } = ctx.req.param(); let itemList: Array<DataItem> = []; switch (type) { case 'index': itemList = await handleIndex(); break; case 'xyxw': case 'kxyj': case 'tzgg': itemList = await handlePostList(type, sub); break; default: throw new Error('No such type'); } return { title: `${categoryMap[type].name} - 武汉大学遥感信息工程学院`, link: baseUrl, description: `${categoryMap[type].name} - 武汉大学遥感信息工程学院`, item: itemList, }; }, };