feat: add 观察者网首页 (#5980)

This commit is contained in:
Ethan Shen 2020-10-30 17:23:07 +08:00 committed by GitHub
parent a48022af2b
commit 193ef153ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 162 additions and 280 deletions

View File

@ -729,19 +729,35 @@ area 分区选项
<Route author="zoenglinghou" example="/google/news/要闻/hl=zh-CN&gl=CN&ceid=CN:zh-Hans" path="/google/news/:category/:locale" :paramsDesc="['子分类标题', '地区语言设置,在地址栏 `?` 后,包含 `hl``gl`,以及 `ceid` 参数']"/>
## 观察者网 - 中国关怀 全球视野
## 观察者网
### 观察者首页
### 首页
<Route author="Jeason0228" example="/guanchazhe/index/all" path="/guanchazhe/index/:type" :paramsDesc="['新闻汇总:默认home输出头条+3列新闻,others则为滚动新闻+热点+观察者付费,all则包括以上']" />
<Route author="nczitzk Jeason0228" example="/guancha" path="/guancha/:caty?" :paramsDesc="['分类,见下表,默认为全部']">
| 全部 | 评论 & 研究 | 要闻 | 风闻 | 热点新闻 | 滚动新闻 |
| ---- | ----------- | ----- | ------- | -------- | -------- |
| all | review | story | fengwen | redian | gundong |
home = 评论 & 研究 + 要闻 + 风闻
others = 热点新闻 + 滚动新闻
::: tip 提示
观察者网首页左中右的三个 column 分别对应 **评论 & 研究**、**要闻**、**风闻** 三个部分。
:::
</Route>
### 观察者风闻话题
<Route author="occupy5" example="/guanchazhe/topic/113" path="/guanchazhe/topic/:id" :paramsDesc="['话题id 可在URL中找到']" />
<Route author="occupy5" example="/guancha/topic/113" path="/guancha/topic/:id" :paramsDesc="['话题id 可在URL中找到']" />
### 个人主页文章
<Route author="Jeason0228" example="/guanchazhe/personalpage/243983" path="/guanchazhe/personalpage/:uid" :paramsDesc="['用户id 可在URL中找到']" />
<Route author="Jeason0228" example="/guancha/personalpage/243983" path="/guancha/personalpage/:uid" :paramsDesc="['用户id 可在URL中找到']" />
## 广告门

View File

@ -1681,10 +1681,14 @@ router.get('/manhuadb/comics/:id', require('./routes/manhuadb/comics'));
router.get('/zfrontier/postlist/:type', require('./routes/zfrontier/postlist'));
router.get('/zfrontier/board/:boardId', require('./routes/zfrontier/board_postlist'));
// 观察者风闻话题
router.get('/guanchazhe/topic/:id', require('./routes/guanchazhe/topic'));
router.get('/guanchazhe/personalpage/:uid', require('./routes/guanchazhe/personalpage'));
router.get('/guanchazhe/index/:type', require('./routes/guanchazhe/index'));
// 观察者网
router.get('/guancha/topic/:id', require('./routes/guancha/topic'));
router.get('/guancha/personalpage/:uid', require('./routes/guancha/personalpage'));
router.get('/guancha/:caty?', require('./routes/guancha/index'));
router.get('/guanchazhe/topic/:id', require('./routes/guancha/topic'));
router.get('/guanchazhe/personalpage/:uid', require('./routes/guancha/personalpage'));
router.get('/guanchazhe/index/:caty?', require('./routes/guancha/index'));
// Hpoi 手办维基
router.get('/hpoi/info/:type?', require('./routes/hpoi/info'));

133
lib/routes/guancha/index.js Normal file
View File

@ -0,0 +1,133 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const date = require('@/utils/date');
const config = {
review: { title: '评论 & 研究', query: '.module-news-main' },
story: { title: '要闻', query: '.img-List' },
fengwen: { title: '风闻', query: '.fengwen-list' },
redian: { title: '热点' },
gundong: { title: '滚动' },
all: { title: '全部' },
home: { title: '首页' },
others: { title: '热点 & 滚动' },
};
module.exports = async (ctx) => {
const total = 10;
const caty = ctx.params.caty || 'all';
const rootUrl = 'https://www.guancha.cn';
let newsList = [],
redianList = [],
gundongList = [];
// 'review', 'story' and 'fengwen' come from homepage.
if (caty === 'review' || caty === 'story' || caty === 'fengwen' || caty === 'all' || caty === 'home') {
const response = await got({
method: 'get',
url: rootUrl,
});
const $ = cheerio.load(response.data);
const fetchPost = (slice) =>
slice
.find('h4.module-title a')
// Filter some blank links which lead to no contents but 'https://user.guancha.cn'.
.filter((_, item) => $(item).attr('href') !== 'https://user.guancha.cn')
.map((_, item) => {
item = $(item);
const link = item.attr('href');
return {
title: item.text(),
link: `${(link.indexOf('http') === 0 ? '' : rootUrl) + link}`,
};
})
.get();
if (caty === 'all' || caty === 'home') {
newsList = fetchPost($(config.review.query))
.slice(0, total / 3)
.concat(fetchPost($(config.story.query)).slice(0, total / 3), fetchPost($(config.fengwen.query)).slice(0, total / 3));
} else {
newsList = fetchPost($(config[caty].query)).slice(0, total);
}
}
// 'redian' and 'gundong' come from api.
if (caty === 'redian' || caty === 'all' || caty === 'others') {
const response = await got({
method: 'get',
url: `${rootUrl}/api/redian.htm`,
});
redianList = response.data.items
.map((item) => ({
title: item.TITLE,
link: `${rootUrl}${item.HTTP_URL}`,
}))
.slice(0, caty === 'all' ? total / 3 : total);
}
if (caty === 'gundong' || caty === 'all' || caty === 'others') {
const response = await got({
method: 'get',
url: `${rootUrl}/api/gundong.htm`,
});
gundongList = response.data.items
.map((item) => ({
title: item.TITLE,
link: `${rootUrl}${item.HTTP_URL}`,
}))
.slice(0, caty === 'all' ? total / 3 : total);
}
const items = await Promise.all(
newsList.concat(redianList, gundongList).map(
async (item) =>
await ctx.cache.tryGet(item.link, async () => {
let detailResponse = await got({
method: 'get',
url: item.link,
});
// Some links to posts in 'fengwen' must be redirected in order to fetch content.
// eg. https://www.guancha.cn/politics/2020_10_23_569021.shtml
// => https://user.guancha.cn/main/content?id=399176
const jumpMatch = detailResponse.data.match(/user.guancha.cn\/main\/content\?id=(.*)";/);
if (jumpMatch !== null) {
item.link = `https://user.guancha.cn/main/content?id=${jumpMatch[1]}`;
detailResponse = await got({
method: 'get',
url: item.link,
});
}
const content = cheerio.load(detailResponse.data);
const dateMatch = detailResponse.data.match(/"pubDate": "(.*)"/);
if (dateMatch === null) {
// PubDates of posts in 'fengwen' are in an informal format.
item.pubDate = date(content('.time1').text(), 8);
} else {
item.pubDate = new Date(dateMatch[1]).toUTCString();
}
item.description = content('.all-txt').html() || content('.article-txt-content').html();
return item;
})
)
);
ctx.state.data = {
title: `观察者网 - ${config[caty].title}`,
link: rootUrl,
item: items,
};
};

View File

@ -1,271 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const type = ctx.params.type;
const host = 'https://www.guancha.cn';
const ptype = {
all: { name: '首页Feeds', url: 'https://www.guancha.cn/' },
redian: { name: '热点新闻', url: 'https://www.guancha.cn/api/redian.htm' },
member: { name: '观察者', url: 'https://www.guancha.cn/api/member.htm' },
gundong: { name: '滚动新闻', url: 'https://www.guancha.cn/api/new_gundong.htm' },
};
// 定义list存放请求数据,htmlarr暂存html数据组,outList存放输出的数据组
const list = [],
htmlarr = [],
temp = [],
HomeFeeds = [],
RedianFeeds = [],
MemberFeeds = [],
GundongFeeds = [];
let AllFeeds = [];
// 获取新闻url过来的时间/ID
function getData(jscontent, option) {
switch (option) {
case 'date': {
const jsoutput = String(jscontent).substr(-23, 10).replace(/_/g, '-');
return new Date(jsoutput).toLocaleDateString();
}
case 'urlid': {
const jsoutput = String(jscontent).substr(-12, 6);
return jsoutput;
}
default: {
break;
}
}
}
// 循环获取ptype每个请求的数据内容
for (const key in ptype) {
const link = ptype[key].url;
// eslint-disable-next-line no-await-in-loop
const response = await got({
method: 'get',
url: link,
headers: {
Referer: host,
},
});
// 由于数据结构不太一致,将所有的数据内容抽离提取成统一的对象
if (key === 'all') {
list[0] = response.data;
}
const $ = cheerio.load(list[0]);
switch (key) {
case 'all':
// 获取头条 + 清理不带有h4标题的li
htmlarr[0] = $('.content-headline').first();
htmlarr[1] = $('.Review-item li h4').parent();
htmlarr[2] = $('.img-List li h4').parent();
$('.author-intro img').parent().remove();
$('.module-img-head img').parent().remove();
htmlarr[3] = $('[class="module-news gray"] .module-news-main').first().children();
htmlarr[4] = $('[class="module-news-main mt15"]').children();
temp.length = 0;
temp.push(
htmlarr[0].map((index, item) => {
item = $(item);
const herfString = item.find('h3>a').attr('href');
const feed = {
category: ptype[key].name + '头条',
id: getData(herfString, 'urlid'),
title: item.find('h3').text().trim(),
pageurl: host + herfString,
picurl: item.find('a>img').first().attr('src'),
description: item.find('h3').text().trim() + `<img src="${item.find('a>img').first().attr('src')}" referrerpolicy="no-referrer" />`,
date: getData(herfString, 'date'),
};
return feed;
})
);
temp.push(
htmlarr[1].map((index, item) => {
item = $(item);
const herfString = item.find('h4.module-title>a').attr('href');
const feed = {
category: ptype[key].name + '左1列',
id: getData(herfString, 'urlid'),
title: item.find('h4.module-title').text().trim(),
pageurl: host + herfString,
picurl: item.find('a.module-img>img').attr('src'),
description:
item.find('p.module-artile').text().trim() +
`<br/><a href=${host}${herfString} target="_blank"><img src="` +
item.find('a.module-img>img').attr('src') +
`" referrerpolicy="no-referrer" /><br/>【全文阅读】</a>`,
date: getData(herfString, 'date'),
};
return feed;
})
);
temp.push(
htmlarr[2].map((index, item) => {
item = $(item);
const herfString = item.find('h4.module-title>a').attr('href');
const feed = {
category: ptype[key].name + '中间+右边2列',
id: getData(herfString, 'urlid'),
title: item.find('h4.module-title').text().trim(),
pageurl: host + herfString,
picurl: item.find('a>img').attr('src'),
description:
item.find('.resemble-art').html() +
`<br/><a href=${host}${herfString} target="_blank"><img src="` +
item.find('div.fastRead-img a>img').attr('src') +
`" referrerpolicy="no-referrer" /><br/>【全文阅读】</a>`,
date: getData(herfString, 'date'),
};
return feed;
})
);
temp.push(
htmlarr[3].map((index, item) => {
item = $(item);
const herfString = item.find('a').first().attr('href');
const feed = {
category: ptype[key].name + '左列: 访谈、论坛',
id: getData(herfString, 'urlid'),
title: item.find('a').first().text(),
pageurl: host + herfString,
picurl: `0`,
description: item.find('a').first().text() + item.find('p').first().text() + `<br/><a href=${host}${herfString} target="_blank"><br/>【全文阅读】</a>`,
date: getData(herfString, 'date'),
};
return feed;
})
);
temp.push(
htmlarr[4].map((index, item) => {
item = $(item);
const herfString = item.find('a').first().attr('href');
const feed = {
category: ptype[key].name + '右列: 历史、深度',
id: getData(herfString, 'urlid'),
title: item.find('a').first().text(),
pageurl: host + herfString,
picurl: `0`,
description: item.find('a').first().text() + `<br/><a href=${host}${herfString} target="_blank"><br/>【全文阅读】</a>`,
date: getData(herfString, 'date'),
};
return feed;
})
);
// 集结所有feeds对象给到HomeFeeds;
for (let i = 0; i < temp.length; i++) {
for (let k = 0; k < temp[i].length; k++) {
HomeFeeds.push(temp[i][k]);
}
}
break;
case 'redian':
list[1] = response.data.items;
temp.length = 0;
temp.push(
list[1].map((item) => ({
category: ptype[key].name + '_热点',
id: '0',
title: '[热点]' + item.TITLE,
pageurl: host + '/' + item.HTTP_URL,
date: getData(item.HTTP_URL, 'date'),
picurl: '0',
}))
);
// 集结所有feeds对象给到RedianFeeds;
for (let i = 0; i < temp.length; i++) {
for (let k = 0; k < temp[i].length; k++) {
RedianFeeds.push(temp[i][k]);
}
}
break;
case 'member':
list[2] = response.data.items;
temp.length = 0;
temp.push(
list[2].map((item) => ({
category: ptype[key].name + '_右列观察员',
id: item.id,
title: '[付费]' + item.title,
pageurl: 'https://member.guancha.cn/post/view?id=' + item.id,
picurl: item.h_pic,
description: item.title + `<img src="${item.h_pic}" referrerpolicy="no-referrer" />`,
date: item.created_at,
}))
);
// 集结所有feeds对象给到MemberFeeds;
for (let i = 0; i < temp.length; i++) {
for (let k = 0; k < temp[i].length; k++) {
MemberFeeds.push(temp[i][k]);
}
}
break;
case 'gundong':
list[3] = response.data.fenghot;
list[4] = response.data.member;
list[5] = response.data.kuaixun;
temp.length = 0;
temp.push(
list[5].map((item) => ({
category: ptype[key].name + '_滚动新闻',
id: '0',
title: '[滚动新闻]' + item.TITLE,
pageurl: host + '/' + item.HTTP_URL,
picurl: '0',
date: getData(item.HTTP_URL, 'date'),
}))
);
temp.push(
list[3].map((item) => ({
category: ptype[key].name + '_风闻7天最热',
id: item.id,
title: '[风闻7天最热]' + item.title,
pageurl: 'https://user.guancha.cn/main/content?id=' + item.id,
picurl: '0',
}))
);
temp.push(
list[4].map((item) => ({
category: ptype[key].name + '_观察员',
id: item.id,
title: '[观察员付费]' + item.title,
pageurl: 'https://member.guancha.cn/post/view?id=' + item.id,
picurl: '0',
}))
);
for (let i = 0; i < temp.length; i++) {
for (let k = 0; k < temp[i].length; k++) {
GundongFeeds.push(temp[i][k]);
}
}
break;
}
}
// 集合所有数据,控制分类输出
switch (type) {
case 'all':
AllFeeds = HomeFeeds.concat(RedianFeeds, MemberFeeds, GundongFeeds);
break;
case 'home':
AllFeeds = HomeFeeds;
break;
case 'others':
AllFeeds = RedianFeeds.concat(MemberFeeds, GundongFeeds);
break;
default:
AllFeeds = HomeFeeds;
break;
}
ctx.state.data = {
title: `观察者-首页新闻`,
link: host,
description: `观察者网,致力于荟萃中外思想者精华,鼓励青年学人探索,建中西文化交流平台,为崛起中的精英提供决策参考。`,
allowEmpty: true,
item: AllFeeds.map((item) => ({
title: item.title,
description: item.description || item.title + `<br/><a href=${item.pageurl} target="_blank"><br/>【全文阅读】</a>`,
pubDate: item.date || new Date().toLocaleDateString(),
link: item.pageurl,
category: item.category,
author: item.category,
})),
};
};