From d10fb2334c4c19398e265e3187caa24bf97cb89b Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 25 Jan 2023 12:41:36 -0800 Subject: [PATCH] fix(route): caixin blog (#11695) * fix(route): caixin blog * fix: url --- docs/traditional-media.md | 10 ++-- lib/router.js | 12 ++--- lib/routes/caixin/article.js | 46 ---------------- lib/routes/caixin/blog.js | 62 ---------------------- lib/routes/caixin/category.js | 48 ----------------- lib/routes/caixin/latest.js | 75 --------------------------- lib/routes/caixin/yxnews.js | 30 ----------- lib/v2/caixin/article.js | 27 ++++++++++ lib/v2/caixin/blog.js | 58 +++++++++++++++++++++ lib/v2/caixin/category.js | 56 ++++++++++++++++++++ lib/{routes => v2}/caixin/database.js | 28 +++++----- lib/v2/caixin/k.js | 40 ++++++++++++++ lib/v2/caixin/latest.js | 50 ++++++++++++++++++ lib/v2/caixin/maintainer.js | 8 +++ lib/v2/caixin/radar.js | 43 +++++++++++++++ lib/v2/caixin/router.js | 8 +++ lib/v2/caixin/templates/article.art | 38 ++++++++++++++ lib/v2/caixin/utils.js | 52 +++++++++++++++++++ 18 files changed, 405 insertions(+), 286 deletions(-) delete mode 100644 lib/routes/caixin/article.js delete mode 100644 lib/routes/caixin/blog.js delete mode 100644 lib/routes/caixin/category.js delete mode 100644 lib/routes/caixin/latest.js delete mode 100644 lib/routes/caixin/yxnews.js create mode 100644 lib/v2/caixin/article.js create mode 100644 lib/v2/caixin/blog.js create mode 100644 lib/v2/caixin/category.js rename lib/{routes => v2}/caixin/database.js (62%) create mode 100644 lib/v2/caixin/k.js create mode 100644 lib/v2/caixin/latest.js create mode 100644 lib/v2/caixin/maintainer.js create mode 100644 lib/v2/caixin/radar.js create mode 100644 lib/v2/caixin/router.js create mode 100644 lib/v2/caixin/templates/article.art create mode 100644 lib/v2/caixin/utils.js diff --git a/docs/traditional-media.md b/docs/traditional-media.md index dda54b80f..908ab783e 100644 --- a/docs/traditional-media.md +++ b/docs/traditional-media.md @@ -586,7 +586,7 @@ Provides all of the articles by the specified Yahoo! author. ### 新闻分类 - + Column 列表: @@ -610,11 +610,11 @@ Category 列表: ### 首页新闻 - + ### 最新文章 - + 说明:此 RSS feed 会自动抓取财新网的最新文章,但不包含 FM 及视频内容。 @@ -622,11 +622,11 @@ Category 列表: ### 财新数据通 - + ### 财新一线 - + ## 参考消息 diff --git a/lib/router.js b/lib/router.js index c2180f353..e3065af34 100644 --- a/lib/router.js +++ b/lib/router.js @@ -288,11 +288,11 @@ router.get('/mihoyo/bh2/:type', lazyloadRouteHandler('./routes/mihoyo/bh2')); // router.get('/cctv-special/:id?', lazyloadRouteHandler('./routes/cctv/special')); // 财新博客 -router.get('/caixin/blog/:column', lazyloadRouteHandler('./routes/caixin/blog')); -router.get('/caixin/article', lazyloadRouteHandler('./routes/caixin/article')); -router.get('/caixin/database', lazyloadRouteHandler('./routes/caixin/database')); -router.get('/caixin/yxnews', lazyloadRouteHandler('./routes/caixin/yxnews')); -router.get('/caixin/:column/:category', lazyloadRouteHandler('./routes/caixin/category')); +// router.get('/caixin/blog/:column', lazyloadRouteHandler('./routes/caixin/blog')); +// router.get('/caixin/article', lazyloadRouteHandler('./routes/caixin/article')); +// router.get('/caixin/database', lazyloadRouteHandler('./routes/caixin/database')); +// router.get('/caixin/yxnews', lazyloadRouteHandler('./routes/caixin/yxnews')); +// router.get('/caixin/:column/:category', lazyloadRouteHandler('./routes/caixin/category')); // 草榴社区 router.get('/t66y/post/:tid', lazyloadRouteHandler('./routes/t66y/post')); @@ -3633,7 +3633,7 @@ router.get('/cppcc/:slug?', lazyloadRouteHandler('./routes/gov/cppcc/index')); router.get('/nace/blog/:sort?', lazyloadRouteHandler('./routes/nace/blog')); // Caixin Latest -router.get('/caixin/latest', lazyloadRouteHandler('./routes/caixin/latest')); +// router.get('/caixin/latest', lazyloadRouteHandler('./routes/caixin/latest')); // Semiconductor Industry Association router.get('/semiconductors/latest-news', lazyloadRouteHandler('./routes/semiconductors/latest-news')); diff --git a/lib/routes/caixin/article.js b/lib/routes/caixin/article.js deleted file mode 100644 index a2b643353..000000000 --- a/lib/routes/caixin/article.js +++ /dev/null @@ -1,46 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); - -module.exports = async (ctx) => { - const response = await got({ - method: 'get', - url: 'http://mapiv5.caixin.com//m/api/getWapIndexListByPage?page=1&callback=&_=1560140003179', - headers: { - Referer: `http://mapiv5.caixin.com/`, - Host: 'mapiv5.caixin.com', - }, - }); - - const data = response.data.data.list; - - const items = await Promise.all( - data.map(async (item) => { - const link = item.web_url; - const summary = `

${item.summary}

`; - const key = 'caixin_article_' + link; - - const fullText = await ctx.cache.tryGet(key, async () => { - const result = await got.get(link); - - const $ = cheerio.load(result.data); - - return $('div#Main_Content_Val.text').html(); - }); - - return { - title: item.title, - description: fullText ? summary + fullText : summary, - link, - pubDate: new Date(item.time * 1000), - author: item.author_name, - }; - }) - ); - - ctx.state.data = { - title: `财新网 - 首页`, - link: `http://www.caixin.com/`, - description: '财新网 - 首页', - item: items, - }; -}; diff --git a/lib/routes/caixin/blog.js b/lib/routes/caixin/blog.js deleted file mode 100644 index 39722b518..000000000 --- a/lib/routes/caixin/blog.js +++ /dev/null @@ -1,62 +0,0 @@ -const got = require('@/utils/got'); -const parser = require('@/utils/rss-parser'); -const cheerio = require('cheerio'); -const { isValidHost } = require('@/utils/valid-host'); - -async function load(link, need_feed_description) { - const response = await got.get(link); - const $ = cheerio.load(response.data); - const article = $('.blog_content').removeAttr('style'); - article.find('img').removeAttr('style'); - article - .find('div') - // Non-breaking space U+00A0, ` ` in html - // element.children[0].data === $(element, article).text() - .filter((_, element) => element.children[0].data === String.fromCharCode(160)) - .remove(); - const description = article.html(); - - const item = { description }; - - if (need_feed_description) { - const author = $('div.widget.author_detail p:nth-child(2)'); - // workaround to split author name and author article - const author_name = author.find('strong'); - author_name.text(author_name.text() + ','); - item.feed_description = author.text(); - } - return item; -} - -module.exports = async (ctx) => { - const { column } = ctx.params; - if (!isValidHost(column)) { - throw Error('Invalid column'); - } - const link = `http://${column}.blog.caixin.com`; - const feed_url = `${link}/feed`; - const feed = await parser.parseURL(feed_url); - - const title = `财新博客 - ${/[^»]*$/.exec(feed.title)[0]}`; - - const items = await Promise.all( - feed.items.slice(0, 10).map(async (item, index) => { - const link = item.link; - const single = { - title: item.title, - pubDate: item.pubDate, - link, - author: item['dc:creator'], - }; - const other = await ctx.cache.tryGet(link, () => load(link, index === 0)); - return Promise.resolve(Object.assign({}, single, other)); - }) - ); - - ctx.state.data = { - title, - link, - description: items[0].feed_description, - item: items, - }; -}; diff --git a/lib/routes/caixin/category.js b/lib/routes/caixin/category.js deleted file mode 100644 index a5fdaf765..000000000 --- a/lib/routes/caixin/category.js +++ /dev/null @@ -1,48 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const { isValidHost } = require('@/utils/valid-host'); - -module.exports = async (ctx) => { - const category = ctx.params.category; - const column = ctx.params.column; - const url = `http://${column}.caixin.com/${category}`; - if (!isValidHost(column)) { - throw Error('Invalid column'); - } - - const response = await got({ - method: 'get', - url, - headers: { - Referer: url, - }, - }); - - const $ = cheerio.load(response.data); - const title = $('title').text(); - const list = $('.stitXtuwen_list dl dd'); - const items = []; - - for (let i = 0; i < list.length; i++) { - const item = { - title: $(list[i]).children('h4').children().text(), - desc: $(list[i]).children('p').text(), - pubDate: new Date($(list[i]).children('span').text().replace('年', '-').replace('月', '-').replace('日', '')).toUTCString(), - url: $(list[i]).children('h4').children().attr('href'), - }; - items.push(item); - } - - ctx.state.data = { - title, - link: url, - description: '财新网 - 提供财经新闻及资讯服务', - item: items.map((item) => ({ - title: item.title, - description: item.desc, - guid: item.title, - link: item.url, - pubDate: item.pubDate, - })), - }; -}; diff --git a/lib/routes/caixin/latest.js b/lib/routes/caixin/latest.js deleted file mode 100644 index a9b73ee5c..000000000 --- a/lib/routes/caixin/latest.js +++ /dev/null @@ -1,75 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); - -const { parseDate } = require('@/utils/parse-date'); -const timezone = require('@/utils/timezone'); - -module.exports = async (ctx) => { - const li_r = await got({ - method: 'get', - url: 'https://www.caixin.com/2021-08-22/101758426.html', - }); - - const $ = cheerio.load(li_r.data); - const list = $('div.columnBox a[name="new_artical"]~ul.list li'); - - const tasks = []; - - list.map((_index, li) => $(li).find('a').first().attr('href')) - .filter((_index, link) => !link.includes('fm.caixin.com') && !link.includes('video.caixin.com') && !link.includes('datanews.caixin.com')) // content filter - .each((_index, link) => { - const entry = ctx.cache.tryGet(link, async () => { - const entry_r = await got.get(link); - const $ = cheerio.load(entry_r.data); - // title - const h1 = $('div#conTit h1').text(); - // desc items - const subhead = $('div#subhead.subhead').text(); - let pic_url = $('img.cx-img-loader').attr('src'); - if (pic_url === undefined) { - pic_url = $('img.cx-img-loader').attr('data-src'); - } - const pic_alt = $('dl.media_pic dd').text(); - const content = $('div#Main_Content_Val.text').html(); - - /* - const [, count] = - $('a.box_titlecontent.cost-uibtn') - .text() - .match(/本文共计(\d+)字/) || []; - if (count !== 0 && count !== undefined) { - content = `${content}

此乃财新通收费文章,全文共计${count}字。

`; - } - */ - // desc - let desc; - if (pic_url === undefined) { - desc = `

${subhead}

${content}`; - } else { - desc = `

${subhead}

${pic_alt}${content}`; - } - // time - /* - const [, year, month, day, hour, minute] = $('div#artInfo.artInfo') - .text() - .match(/(\d+)年(\d+)月(\d+)日 *(\d+):(\d+)/); - */ - - return { - title: h1, - description: desc, - pubDate: timezone(parseDate($('div#artInfo.artInfo').text(), 'YYYY年MM月DD日 HH:mm'), +8), - link, - }; - }); - tasks.push(entry); - }); - - const rss = await Promise.all(tasks); - - ctx.state.data = { - title: '财新网 - 最新文章', - link: 'http://www.caixin.com/', - item: rss, - }; -}; diff --git a/lib/routes/caixin/yxnews.js b/lib/routes/caixin/yxnews.js deleted file mode 100644 index c488c539b..000000000 --- a/lib/routes/caixin/yxnews.js +++ /dev/null @@ -1,30 +0,0 @@ -const got = require('@/utils/got'); - -module.exports = async (ctx) => { - const response = await got({ - method: 'get', - url: 'http://k.caixin.com/app/v1/list?productIdList=8,28&uid=&unit=1&name=&code=&deviceType=&device=&userTag=&p=1&c=20', - headers: { - Referer: `http://k.caixin.com/web/`, - Host: 'k.caixin.com', - }, - }); - - const data = response.data.data.list; - const items = await Promise.all( - data.map((item) => ({ - title: item.title, - description: item.text, - link: `http://k.caixin.com/web/detail_${item.oneline_news_code}`, - pubDate: new Date(item.ts), - author: '财新一线', - })) - ); - - ctx.state.data = { - title: `财新网 - 财新一线新闻`, - link: `http://k.caixin.com/`, - description: `财新网 - 财新一线新闻`, - item: items, - }; -}; diff --git a/lib/v2/caixin/article.js b/lib/v2/caixin/article.js new file mode 100644 index 000000000..1c9816f9d --- /dev/null +++ b/lib/v2/caixin/article.js @@ -0,0 +1,27 @@ +const got = require('@/utils/got'); +const { parseDate } = require('@/utils/parse-date'); +const { parseArticle } = require('./utils'); + +module.exports = async (ctx) => { + const { data: response } = await got('https://mapiv5.caixin.com/m/api/getWapIndexListByPage'); + + const list = response.data.list.map((item) => ({ + title: item.title, + description: item.summary, + author: item.author_name, + pubDate: parseDate(item.time, 'X'), + link: item.web_url, + pics: item.pics, + audio: item.cms_audio_url, + audio_image_url: item.audio_image_url, + })); + + const items = await Promise.all(list.map((item) => parseArticle(item, ctx.cache.tryGet))); + + ctx.state.data = { + title: '财新网 - 首页', + link: 'https://www.caixin.com', + description: '财新网 - 首页', + item: items, + }; +}; diff --git a/lib/v2/caixin/blog.js b/lib/v2/caixin/blog.js new file mode 100644 index 000000000..b4aa1cc08 --- /dev/null +++ b/lib/v2/caixin/blog.js @@ -0,0 +1,58 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { isValidHost } = require('@/utils/valid-host'); +const { parseDate } = require('@/utils/parse-date'); +const { parseBlogArticle } = require('./utils'); + +module.exports = async (ctx) => { + const { column } = ctx.params; + if (!isValidHost(column)) { + throw Error('Invalid column'); + } + const link = `https://${column}.blog.caixin.com`; + const { data: response } = await got(link); + const $ = cheerio.load(response); + const authorId = $('script[type="text/javascript"]') + .text() + .match(/window\.authorId = (\d+);/)[1]; + const authorName = $('script[type="text/javascript"]') + .text() + .match(/window\.authorName = "(.*)";/)[1]; + const avatar = $('script[type="text/javascript"]') + .text() + .match(/avatar: "(.*?)",/)[1]; + const introduce = $('script[type="text/javascript"]') + .text() + .match(/introduce: "(.*?)"\n/)[1]; + + const { + data: { data }, + } = await got('https://blog.caixin.com/blog-api/post/posts', { + searchParams: { + page: 1, + size: ctx.query.limit ? parseInt(ctx.query.limit) : 20, + content: '', + authorId, + sort: 'publishTime', + direction: 'DESC', + }, + }); + + const posts = data.map((item) => ({ + title: item.title, + description: item.brief, + author: item.displayName, + link: item.guid.replace('http://', 'https://'), + pubDate: parseDate(item.publishTime, 'x'), + })); + + const items = await Promise.all(posts.map((item) => parseBlogArticle(item, ctx.cache.tryGet))); + + ctx.state.data = { + title: `财新博客 - ${authorName}`, + link, + description: introduce, + image: avatar, + item: items, + }; +}; diff --git a/lib/v2/caixin/category.js b/lib/v2/caixin/category.js new file mode 100644 index 000000000..be225e0c7 --- /dev/null +++ b/lib/v2/caixin/category.js @@ -0,0 +1,56 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { isValidHost } = require('@/utils/valid-host'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); +const { parseArticle } = require('./utils'); + +module.exports = async (ctx) => { + const category = ctx.params.category; + const column = ctx.params.column; + const url = `https://${column}.caixin.com/${category}`; + if (!isValidHost(column)) { + throw Error('Invalid column'); + } + + const response = await got(url); + + const $ = cheerio.load(response.data); + const title = $('head title').text(); + const entity = JSON.parse( + $('script') + .text() + .match(/var entity = (\{.*?\})/)[1] + ); + + const { + data: { datas: data }, + } = await got('https://gateway.caixin.com/api/extapi/homeInterface.jsp', { + searchParams: { + subject: entity.id, + type: 0, + count: ctx.query.limit ? parseInt(ctx.query.limit) : 25, + picdim: '_266_177', + start: 0, + }, + }); + + const list = data.map((item) => ({ + title: item.desc, + description: item.summ, + link: item.link.replace('http://', 'https://'), + pubDate: timezone(parseDate(item.time), +8), + category: item.keyword.split(' '), + audio: item.audioUrl, + audio_image_url: item.pict.imgs[0].url, + })); + + const items = await Promise.all(list.map((item) => parseArticle(item, ctx.cache.tryGet))); + + ctx.state.data = { + title, + link: url, + description: '财新网 - 提供财经新闻及资讯服务', + item: items, + }; +}; diff --git a/lib/routes/caixin/database.js b/lib/v2/caixin/database.js similarity index 62% rename from lib/routes/caixin/database.js rename to lib/v2/caixin/database.js index 4d4bc573a..ca83e69d7 100644 --- a/lib/routes/caixin/database.js +++ b/lib/v2/caixin/database.js @@ -2,38 +2,38 @@ const got = require('@/utils/got'); const cheerio = require('cheerio'); const timezone = require('@/utils/timezone'); const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); module.exports = async (ctx) => { const rootUrl = 'https://database.caixin.com'; - const currentUrl = `${rootUrl}/news`; - const response = await got({ - method: 'get', - url: currentUrl, - }); + const currentUrl = `${rootUrl}/news/`; + const response = await got(currentUrl); const $ = cheerio.load(response.data); const list = $('h4 a') - .map((_, item) => { + .toArray() + .map((item) => { item = $(item); return { title: item.text(), - link: item.attr('href'), + link: item.attr('href').replace('http://', 'https://'), }; - }) - .get(); + }); const items = await Promise.all( list.map((item) => ctx.cache.tryGet(item.link, async () => { - const detailResponse = await got({ - method: 'get', - url: item.link, - }); + const detailResponse = await got(item.link); const content = cheerio.load(detailResponse.data); item.pubDate = timezone(parseDate(content('#pubtime_baidu').text()), +8); - item.description = `

${content('#subhead').html()}

`; + item.description = art(path.join(__dirname, 'templates/article.art'), { + item, + $: content, + }); + item.author = content('.top-author').text(); return item; }) diff --git a/lib/v2/caixin/k.js b/lib/v2/caixin/k.js new file mode 100644 index 000000000..3c4478f37 --- /dev/null +++ b/lib/v2/caixin/k.js @@ -0,0 +1,40 @@ +const got = require('@/utils/got'); +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx) => { + const response = await got('https://k.caixin.com/app/v1/list', { + searchParams: { + productIdList: '8,28', + uid: '', + unit: 1, + name: '', + code: '', + deviceType: '', + device: '', + userTag: '', + p: 1, + c: 20, + }, + }); + + const data = response.data.data.list; + const items = data.map((item) => { + const hasAudio = item.audio_url || Object.values(item.audios)[0]; + return { + title: item.title, + description: item.text, + link: `http://k.caixin.com/web/detail_${item.oneline_news_code}`, + pubDate: parseDate(item.ts, 'x'), + author: '财新一线', + enclosure_url: hasAudio ? item.audio_url || Object.values(item.audios)[0] : undefined, + enclosure_type: hasAudio ? 'audio/mpeg' : undefined, + }; + }); + + ctx.state.data = { + title: '财新网 - 财新一线新闻', + link: 'https://k.caixin.com/web/', + description: '财新网 - 财新一线新闻', + item: items, + }; +}; diff --git a/lib/v2/caixin/latest.js b/lib/v2/caixin/latest.js new file mode 100644 index 000000000..c3bc0311f --- /dev/null +++ b/lib/v2/caixin/latest.js @@ -0,0 +1,50 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); +const { art } = require('@/utils/render'); +const path = require('path'); + +module.exports = async (ctx) => { + const li_r = await got('https://www.caixin.com/2021-08-22/101758426.html'); + + const $ = cheerio.load(li_r.data); + const list = $('div.columnBox a[name="new_artical"]~ul.list li') + .toArray() + .map((li) => ({ + link: $(li).find('a').first().attr('href').replace('http://', 'https://'), + })) + .filter((item) => !item.link.startsWith('https://fm.caixin.com/') && !item.link.startsWith('https://video.caixin.com/') && !item.link.startsWith('https://datanews.caixin.com/')); // content filter + + const rss = await Promise.all( + list.map((item) => + ctx.cache.tryGet(`caixin:latest:${item.link}`, async () => { + const entry_r = await got(item.link); + const $ = cheerio.load(entry_r.data); + // title + const h1 = $('div#conTit h1').text(); + + // desc + const desc = art(path.join(__dirname, 'templates/article.art'), { + item, + $, + }); + + item.title = h1; + item.description = desc; + item.pubDate = timezone(parseDate($('#pubtime_baidu').text()), +8); + // prevent cache coliision with /caixin/article and /caixin/:column/:category + // since those have podcasts + item.guid = `caixin:latest:${item.link}`; + + return item; + }) + ) + ); + + ctx.state.data = { + title: '财新网 - 最新文章', + link: 'https://www.caixin.com/', + item: rss, + }; +}; diff --git a/lib/v2/caixin/maintainer.js b/lib/v2/caixin/maintainer.js new file mode 100644 index 000000000..0337e9d4a --- /dev/null +++ b/lib/v2/caixin/maintainer.js @@ -0,0 +1,8 @@ +module.exports = { + '/article': ['EsuRt'], + '/blog/:column': ['Maecenas'], + '/database': ['nczitzk'], + '/k': ['boypt'], + '/latest': ['tpnonthealps'], + '/:column/:category': ['idealclover'], +}; diff --git a/lib/v2/caixin/radar.js b/lib/v2/caixin/radar.js new file mode 100644 index 000000000..646797052 --- /dev/null +++ b/lib/v2/caixin/radar.js @@ -0,0 +1,43 @@ +module.exports = { + 'caixin.com': { + _name: '财新网', + '.': [ + { + title: '新闻分类', + docs: 'https://docs.rsshub.app/traditional-media.html#cai-xin-wang', + }, + { + title: '首页新闻', + docs: 'https://docs.rsshub.app/traditional-media.html#cai-xin-wang', + source: ['/'], + target: '/caixin/article', + }, + { + title: '最新文章', + docs: 'https://docs.rsshub.app/traditional-media.html#cai-xin-wang', + source: ['/'], + target: '/caixin/latest', + }, + { + title: '用户博客', + docs: 'https://docs.rsshub.app/blog.html#cai-xin-bo-ke', + }, + ], + database: [ + { + title: '财新数据通', + docs: 'https://docs.rsshub.app/traditional-media.html#cai-xin-wang', + source: ['/news', '/'], + target: '/caixin/database', + }, + ], + k: [ + { + title: '财新一线', + docs: 'https://docs.rsshub.app/traditional-media.html#cai-xin-wang', + source: ['/web', '/'], + target: '/caixin/database', + }, + ], + }, +}; diff --git a/lib/v2/caixin/router.js b/lib/v2/caixin/router.js new file mode 100644 index 000000000..a35fb44f7 --- /dev/null +++ b/lib/v2/caixin/router.js @@ -0,0 +1,8 @@ +module.exports = (router) => { + router.get('/article', require('./article')); + router.get('/blog/:column', require('./blog')); + router.get('/database', require('./database')); + router.get('/k', require('./k')); + router.get('/latest', require('./latest')); + router.get('/:column/:category', require('./category')); +}; diff --git a/lib/v2/caixin/templates/article.art b/lib/v2/caixin/templates/article.art new file mode 100644 index 000000000..6b33c31ea --- /dev/null +++ b/lib/v2/caixin/templates/article.art @@ -0,0 +1,38 @@ +{{ if item.audio }} +
+{{ /if }} +{{ if $('.article .subhead').length }} +

{{@ $('.article .subhead').html() }}

+
+{{ /if }} + +{{ if $('.article .media').length }} + {{@ $('.article .media').html() }} +
+{{ /if }} + +{{ if $('.article .content_video').length }} + <% const video = $('script').text().match(/initPlayer\('(.*?)','(.*?)'\)/); %> + <% const videoUrl = video[1]; %> + <% const poster = video[2]; %> + +
+{{ /if }} + +{{ if $('div#Main_Content_Val.text').length }} + {{@ $('div#Main_Content_Val.text').html() }} +{{ else }} + {{ if item.summary }} +

{{ item.summary }}

+
+ {{ /if }} + {{ if item.pics?.includes('#') }} + {{ each item.pics.split('#') pic }} + +
+ {{ /each }} + {{ else }} + +
+ {{ /if }} +{{ /if }} diff --git a/lib/v2/caixin/utils.js b/lib/v2/caixin/utils.js new file mode 100644 index 000000000..ab788bbda --- /dev/null +++ b/lib/v2/caixin/utils.js @@ -0,0 +1,52 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { art } = require('@/utils/render'); +const path = require('path'); + +const parseArticle = (item, tryGet) => { + if (new URL(item.link).hostname.match(/\.blog\.caixin\.com$/)) { + return parseBlogArticle(item, tryGet); + } else { + return tryGet(item.link, async () => { + const { data: response } = await got(item.link); + + const $ = cheerio.load(response); + + item.description = art(path.join(__dirname, 'templates/article.art'), { + item, + $, + }); + + if (item.audio) { + item.itunes_item_image = item.audio_image_url; + item.enclosure_url = item.audio; + item.enclosure_type = 'audio/mpeg'; + } + + return item; + }); + } +}; + +const parseBlogArticle = (item, tryGet) => + tryGet(item.link, async () => { + const response = await got(item.link); + const $ = cheerio.load(response.data); + const article = $('#the_content').removeAttr('style'); + article.find('img').removeAttr('style'); + article + .find('p') + // Non-breaking space U+00A0, ` ` in html + // element.children[0].data === $(element, article).text() + .filter((_, element) => element.children[0].data === String.fromCharCode(160)) + .remove(); + + item.description = article.html(); + + return item; + }); + +module.exports = { + parseArticle, + parseBlogArticle, +};