feat(route): add JPMC Institute (#18818)

* feat(route): add JPMorgan Chase Institute

* feat(route/jpmc): fix page content scraping

* chore(route/jpmc): remove deprecated routes

* fix(route/jpmc): make gh es linter happy

* fix(route/jpmc): not caching the items array
This commit is contained in:
Jiahao Lee
2025-04-16 18:24:41 +08:00
committed by GitHub
parent 14d4d69bf5
commit fe3416146c
3 changed files with 116 additions and 52 deletions
@@ -1,52 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');
const base = 'https://institute.jpmorganchase.com';
const url = `${base}/institute/research`;
const parseDetails = (link, ctx) => {
const fullLink = `${base}${link}`;
return ctx.cache.tryGet(fullLink, async () => {
const response = await got({
url: fullLink,
});
const $ = cheerio.load(response.data);
const authors = [];
$('.author-name').each((i, elem) => {
authors.push($(elem).text());
});
return {
category: $('.eyebrow').text(),
author: authors.filter(Boolean).join(', '),
title: $('title').text() + ' | ' + $('.copy-wrap p').text(),
description: $('.jpmc-wrapper').html(),
link: fullLink,
pubDate: parseDate($('.date-field').text(), 'MMMM YYYY'),
};
});
};
module.exports = async (ctx) => {
const response = await got({
method: 'get',
url,
});
const title = 'All Reports';
const $ = cheerio.load(response.data);
const items = $('.item a')
.map((i, item) => {
const link = item.attribs.href;
return parseDetails(link, ctx);
})
.get();
ctx.state.data = {
title: `${title} - JPMorgan Chase Institute`,
link: url,
description: `${title} - JPMorgan Chase Institute`,
item: await Promise.all(items),
};
};
+9
View File
@@ -0,0 +1,9 @@
import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'JPMorgan Chase',
url: 'www.jpmorganchase.com',
zh: {
name: '摩根大通',
},
};
+107
View File
@@ -0,0 +1,107 @@
import { Data, DataItem, Route } from '@/types';
import ofetch from '@/utils/ofetch';
import cache from '@/utils/cache';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
const base = 'https://www.jpmorganchase.com';
const frontPageUrl = `${base}/institute/all-topics`;
const indexUrl = `${base}/services/json/v1/dynamic-grid.service/parent=jpmorganchase/global/US/en/home/institute/all-topics&comp=root/content-parsys/dynamic_grid&page=p1.json`;
export const route: Route = {
path: '/',
example: '/jpmorganchase',
categories: ['finance'],
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['jpmorganchase.com/institute/all-topics'],
target: '/',
},
],
name: 'Research Topics',
maintainers: ['dousha'],
handler,
url: 'www.jpmorganchase.com/institute/all-topics',
};
type PartitionMeta = {
'total-items': number;
page: number;
'page-size': number;
'max-pages': number;
'partition-size': string;
};
type IndexEntry = {
title: string;
date: string;
hideDate: boolean;
description: string;
type: string;
link: string;
linkText: string;
image: string;
};
async function fetchIndexEntires(): Promise<IndexEntry[]> {
const response = await ofetch(indexUrl);
if (!('meta' in response)) {
return [];
}
const meta = response.meta as PartitionMeta;
const maxItemCount = Number(meta['partition-size']);
return (response.items as IndexEntry[]).slice(0, maxItemCount);
}
function fetchDataItem(entry: IndexEntry): Promise<DataItem> {
const url = `${base}${entry.link}`;
return cache.tryGet(url, async () => {
let authors: string[] = [];
let description: string = '';
let category: string[] = [];
let articleDate: string = entry.date;
const pageContent: string = await ofetch(url);
if (pageContent.length > 0) {
const $ = load(pageContent);
category = [$('.eyebrow').text()];
authors = $('.author-name')
.toArray()
.map((el) => $(el).text().trim());
articleDate = $('.date').text().trim() || entry.date;
description = $('.root').children('div').children('div:eq(1)').html() || '';
}
return {
category,
author: authors.join(', '),
title: entry.title,
description,
link: url,
pubDate: parseDate(articleDate),
} satisfies DataItem;
}) as Promise<DataItem>;
}
async function handler(): Promise<Data> {
const entires = await fetchIndexEntires();
const items = await Promise.all(entires.map((it) => fetchDataItem(it)));
return {
title: 'All Topics - JPMorganChase Institute',
link: frontPageUrl,
item: items,
};
}