feat(route): add Guandong Museum Exhibitions and news route (#22729)

* feat:add Guandong Museum Exhibitions and news

* fix: update selector
This commit is contained in:
Jiamin
2026-07-22 01:21:36 +08:00
committed by GitHub
parent 016e0f7dd0
commit 737cf6063b
3 changed files with 194 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
import { load } from 'cheerio';
import dayjs from 'dayjs';
import type { Context } from 'hono';
import { renderToString } from 'hono/jsx/dom/server';
import type { DataItem, Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { namespace } from './namespace';
// convert exhibition date string to YYYY-MM-DD format, e.g. "2023年5月1日" or "2023/5/1" => "2023-05-01"
const formatExhibitionDate = (d?: string) => {
if (!d) {
return;
}
return dayjs(d.replaceAll(/[年月/.]/g, '-').replaceAll('日', '')).format('YYYY-MM-DD');
};
const parseExhibitionDuration = (duration?: string) => {
const allDates = duration?.match(/\d{4}[./年-]\d{1,2}[./月-]\d{1,2}日?/g) || [];
return {
startDate: formatExhibitionDate(allDates[0]),
endDate: formatExhibitionDate(allDates[1]),
};
};
export const route: Route = {
path: '/exhibition/:type?',
categories: ['travel'],
example: '/gdmuseum/exhibition/temp',
parameters: {
type: 'Exhibition type, supported values: temp(临时展览), default: All exhibitions.',
},
name: 'Current Exhibitions',
maintainers: ['magazian'],
radar: [
{
source: ['www.gdmuseum.org.cn/col9/list'],
target: '/exhibition',
},
],
handler: async (ctx: Context) => {
const typeParam = ctx.req.param('type') || 'all';
const museumName = namespace.zh?.name || namespace.name;
const baseUrl = 'https://www.gdmuseum.org.cn';
const url = `${baseUrl}/col9/list`;
const response = await got(url);
const $ = load(response.data);
const list = $('.ULLIST li a[href^="/cn/col"]')
.toArray()
.map((el) => {
const $item = $(el);
const title = $item.find('.divtt.qui-dot').text();
if (typeParam === 'temp') {
if (title.includes('常设展览')) {
return null;
}
} else if (typeParam !== 'all') {
return null;
}
const rawLink = $item.attr('href');
const itemLink = rawLink ? new URL(rawLink, baseUrl).href : '';
const rawSrc = $item.find('img').attr('src');
const imgUrl = rawSrc ? new URL(rawSrc, baseUrl).href : undefined;
const textDurationAndLocation = $item.find('.quitxt.qui-dot').text().trim();
const [fullDuration = '', location = ''] = textDurationAndLocation.split('|').map((p) => p.trim());
const { startDate, endDate } = parseExhibitionDuration(fullDuration);
const pubDate = startDate ? parseDate(startDate) : undefined;
const description = renderToString(
<div>
{imgUrl && <img src={imgUrl} />}
<br />
<p>
<b></b>
{location || '参考详情'}
</p>
<p>
<b></b>
{startDate || '未定/常设'}
</p>
<p>
<b></b>
{endDate || '未定/常设'}
</p>
{fullDuration && (
<p>
<small>{fullDuration}</small>
</p>
)}
</div>
);
return {
title,
link: itemLink,
pubDate,
description,
// For further .ics file processing
_extra: {
museumName,
location,
startDate,
endDate,
},
} as DataItem;
})
.filter((i): i is DataItem => i !== null);
return {
title: `${museumName} - 正在热展${typeParam === 'temp' ? ' - 临时展览' : ''}`,
link: url,
language: 'zh-CN',
item: list,
};
},
};
+10
View File
@@ -0,0 +1,10 @@
import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'Guangdong Museum',
url: 'gdmuseum.org.cn',
zh: {
name: '广东省博物馆',
},
};
+58
View File
@@ -0,0 +1,58 @@
import { load } from 'cheerio';
import type { DataItem, Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { namespace } from './namespace';
export const route: Route = {
path: '/information',
categories: ['travel'],
example: '/gdmuseum/information',
name: 'Information',
maintainers: ['magazian'],
radar: [
{
source: ['www.gdmuseum.org.cn/cn/col51/list'],
target: '/information',
},
],
handler: async () => {
const baseUrl = 'https://www.gdmuseum.org.cn';
const apiUrl = `${baseUrl}/cn/col51/list`;
const museumName = namespace.zh?.name || namespace.name;
const response = await got(apiUrl);
const $ = load(response.data);
const list = $('.ULLIST li a[href^="/cn/col"]')
.toArray()
.map((el) => {
const $item = $(el);
const rawLink = $item.attr('href') || '';
const itemLink = new URL(rawLink, baseUrl).href;
const title = $item.find('h3.h3.qui-dot').text();
const day = $item.find('time b').text().trim();
const yearMonth = $item.find('time i').text().trim();
const pubDate = timezone(parseDate(`${yearMonth}-${day}`), 8);
return {
title,
link: itemLink,
pubDate,
} as DataItem;
});
return {
title: `${museumName} - 公告`,
link: apiUrl,
language: 'zh-CN',
item: list,
};
},
};