fix(route): 贴吧帖子动态功能出错 (#10605)

* 修复选择器问题和部分楼层,日期获取问题

* 修复某些特殊情况

* refactor: migrate to v2

* fix: use art-template for rendering
This commit is contained in:
Axojhf
2022-08-25 21:39:01 +08:00
committed by GitHub
parent 7b3b4b6935
commit 51a294f322
14 changed files with 231 additions and 262 deletions
-40
View File
@@ -117,46 +117,6 @@
'tw.manhuagui.com': { _name: '漫画柜台湾', www: [{ title: '漫画更新', docs: 'https://docs.rsshub.app/anime.html#kan-man-hua-tai-wan', source: '/comic/:id/', target: '/twmanhuagui/comic/:id' }] },
'copymanga.com': { _name: '拷贝漫画', www: [{ title: '漫画更新', docs: 'https://docs.rsshub.app/anime.html#kao-bei-man-hua', source: '/comic/:id/', target: '/manhuagui/comic/:id/5' }] },
'pgyer.com': { _name: '蒲公英应用分发', www: [{ title: 'app更新', docs: 'https://docs.rsshub.app/program-update.html#pu-gong-ying-ying-yong-fen-fa', source: '/:app', target: '/pgyer/:app' }] },
'baidu.com': {
_name: '贴吧',
tieba: [
{
title: '帖子列表',
docs: 'https://docs.rsshub.app/bbs.html#tie-ba',
source: 'f',
target: (params, url) => {
const type = new URL(url).searchParams.get('tab');
if (!type || type === 'main') {
return `/tieba/forum/${new URL(url).searchParams.get('kw')}`;
}
},
},
{
title: '精品帖子',
docs: 'https://docs.rsshub.app/bbs.html#tie-ba',
source: 'f',
target: (params, url) => {
const type = new URL(url).searchParams.get('tab');
if (type === 'good') {
return `/tieba/forum/good/${new URL(url).searchParams.get('kw')}`;
}
},
},
{ title: '帖子动态', docs: 'https://docs.rsshub.app/bbs.html#tie-ba', source: '/p/:id', target: '/tieba/post/:id' },
{ title: '只看楼主', docs: 'https://docs.rsshub.app/bbs.html#tie-ba', source: '/p/:id', target: '/tieba/post/lz/:id' },
{
title: '用户帖子',
docs: 'https://docs.rsshub.app/bbs.html#tie-ba',
source: '/home/main',
target: (params, url) => {
const uid = new URL(url).searchParams.get('un');
if (uid) {
return `/tieba/user/${uid}`;
}
},
},
],
},
'wineyun.com': { _name: '酒云网', www: [{ title: '最新商品', docs: 'https://docs.rsshub.app/other.html#jiu-yun-wang', source: ['/:category'], target: '/wineyun/:category' }] },
'epicgames.com': { _name: 'Epic Games', www: [{ title: '每周免费游戏', docs: 'https://docs.rsshub.app/game.html#epicgames-freegame', source: '/store/zh-CN/free-games', target: '/epicgames/freegames' }] },
'playstation.com': {
+26 -26
View File
@@ -410,6 +410,32 @@ pageClass: routes
<Route author="nczitzk" example="/gamer/hot/47157" path="/gamer/hot/:bsn" :paramsDesc="['板块 id,在 URL 可以找到']"/>
## 百度贴吧
### 帖子列表
<Route author="u3u" example="/baidu/tieba/forum/女图" path="/baidu/tieba/forum/:kw" :paramsDesc="['吧名']" radar="1"/>
### 精品帖子
<Route author="u3u" example="/baidu/tieba/forum/good/女图" path="/baidu/tieba/forum/good/:kw/:cid?" :paramsDesc="['吧名', '精品分类, 如果不传 `cid` 则获取全部分类']" radar="1"/>
### 帖子动态
<Route author="u3u" example="/baidu/tieba/post/5853240586" path="/baidu/tieba/post/:id" :paramsDesc="['帖子 ID']" radar="1"/>
### 楼主动态
<Route author="u3u" example="/baidu/tieba/post/lz/5853240586" path="/baidu/tieba/post/lz/:id" :paramsDesc="['帖子 ID']" radar="1"/>
### 用户帖子
<Route author="igxlin nczitzk" example="/baidu/tieba/user/斗鱼游戏君" path="/baidu/tieba/user/:uid" :paramsDesc="['用户 ID']" radar="1">
用户 ID 可以通过打开用户的主页后查看地址栏的 `un` 字段来获取。
</Route>
## 才符
### 用户动态
@@ -824,32 +850,6 @@ pageClass: routes
<Route author="a14907" example="/tianya/comments/11488997" path="/tianya/comments/:userid" :paramsDesc="['用户id userid,可在 URL 找到 例如,用户苕木匠的地址是http://www.tianya.cn/11488997/bbs, 苕木匠的userid就是11488997']"/>
## 贴吧
### 帖子列表
<Route author="u3u" example="/tieba/forum/女图" path="/tieba/forum/:kw" :paramsDesc="['吧名']" radar="1"/>
### 精品帖子
<Route author="u3u" example="/tieba/forum/good/女图" path="/tieba/forum/good/:kw/:cid?" :paramsDesc="['吧名', '精品分类, 如果不传 `cid` 则获取全部分类']" radar="1"/>
### 帖子动态
<Route author="u3u" example="/tieba/post/5853240586" path="/tieba/post/:id" :paramsDesc="['帖子 ID']" radar="1"/>
### 楼主动态
<Route author="u3u" example="/tieba/post/lz/5853240586" path="/tieba/post/lz/:id" :paramsDesc="['帖子 ID']" radar="1"/>
### 用户帖子
<Route author="igxlin nczitzk" example="/tieba/user/斗鱼游戏君" path="/tieba/user/:uid" :paramsDesc="['用户 ID']" radar="1">
用户 ID 可以通过打开用户的主页后查看地址栏的 `un` 字段来获取。
</Route>
## 通信人家园
### 论坛 频道
-50
View File
@@ -246,56 +246,6 @@ module.exports = {
},
],
},
'baidu.com': {
_name: '贴吧',
tieba: [
{
title: '帖子列表',
docs: 'https://docs.rsshub.app/bbs.html#tie-ba',
source: 'f',
target: (params, url) => {
const type = new URL(url).searchParams.get('tab');
if (!type || type === 'main') {
return `/tieba/forum/${new URL(url).searchParams.get('kw')}`;
}
},
},
{
title: '精品帖子',
docs: 'https://docs.rsshub.app/bbs.html#tie-ba',
source: 'f',
target: (params, url) => {
const type = new URL(url).searchParams.get('tab');
if (type === 'good') {
return `/tieba/forum/good/${new URL(url).searchParams.get('kw')}`;
}
},
},
{
title: '帖子动态',
docs: 'https://docs.rsshub.app/bbs.html#tie-ba',
source: '/p/:id',
target: '/tieba/post/:id',
},
{
title: '只看楼主',
docs: 'https://docs.rsshub.app/bbs.html#tie-ba',
source: '/p/:id',
target: '/tieba/post/lz/:id',
},
{
title: '用户帖子',
docs: 'https://docs.rsshub.app/bbs.html#tie-ba',
source: '/home/main',
target: (params, url) => {
const uid = new URL(url).searchParams.get('un');
if (uid) {
return `/tieba/user/${uid}`;
}
},
},
],
},
'wineyun.com': {
_name: '酒云网',
www: [
+6 -6
View File
@@ -54,12 +54,12 @@ router.get('/weibo/search/hot', lazyloadRouteHandler('./routes/weibo/search/hot'
router.get('/weibo/super_index/:id/:type?/:routeParams?', lazyloadRouteHandler('./routes/weibo/super_index'));
router.get('/weibo/oasis/user/:userid', lazyloadRouteHandler('./routes/weibo/oasis/user'));
// 贴吧
router.get('/tieba/forum/:kw', lazyloadRouteHandler('./routes/tieba/forum'));
router.get('/tieba/forum/good/:kw/:cid?', lazyloadRouteHandler('./routes/tieba/forum'));
router.get('/tieba/post/:id', lazyloadRouteHandler('./routes/tieba/post'));
router.get('/tieba/post/lz/:id', lazyloadRouteHandler('./routes/tieba/post'));
router.get('/tieba/user/:uid', lazyloadRouteHandler('./routes/tieba/user'));
// 贴吧 migrated to v2
// router.get('/tieba/forum/:kw', lazyloadRouteHandler('./routes/tieba/forum'));
// router.get('/tieba/forum/good/:kw/:cid?', lazyloadRouteHandler('./routes/tieba/forum'));
// router.get('/tieba/post/:id', lazyloadRouteHandler('./routes/tieba/post'));
// router.get('/tieba/post/lz/:id', lazyloadRouteHandler('./routes/tieba/post'));
// router.get('/tieba/user/:uid', lazyloadRouteHandler('./routes/tieba/user'));
// 网易云音乐
router.get('/ncm/playlist/:id', lazyloadRouteHandler('./routes/ncm/playlist'));
-88
View File
@@ -1,88 +0,0 @@
const cheerio = require('cheerio');
const qs = require('querystring');
const got = require('@/utils/got');
function isNormalTime(time) {
return /^(\d{2}):(\d{2})$/.test(time);
}
function isNormalDate(time) {
return /^(\d{1,2})-(\d{1,2})$/.test(time);
}
function isDate(time) {
return /^(\d{4})-(\d{1,2})-(\d{1,2})$/.test(time);
}
function getPubDate(time) {
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth() + 1;
const date = now.getDate();
if (isNormalTime(time)) {
return new Date(`${year}-${month}-${date} ${time}`);
}
if (isNormalDate(time)) {
return new Date(`${year}-${time}`) > now ? new Date(`${year - 1}-${time}`) : new Date(`${year}-${time}`);
}
if (isDate(time)) {
return new Date(time);
}
return now;
}
module.exports = async (ctx) => {
const { kw, cid } = ctx.params;
// PC端:https://tieba.baidu.com/f?kw=${encodeURIComponent(kw)}
// 移动端接口:https://tieba.baidu.com/mo/q/m?kw=${encodeURIComponent(kw)}&lp=5024&forum_recommend=1&lm=0&cid=0&has_url_param=1&pn=0&is_ajax=1
const params = { kw: encodeURIComponent(kw) };
ctx._matchedRoute.includes('good') && (params.tab = 'good');
cid && (params.cid = cid);
const { data } = await got({
method: 'get',
url: `https://tieba.baidu.com/f?${qs.stringify(params)}`,
headers: {
Referer: 'https://tieba.baidu.com/',
},
});
const threadListHTML = cheerio
.load(data)('code[id="pagelet_html_frs-list/pagelet/thread_list"]')
.html()
.replace(/<!--|-->/g, '');
const $ = cheerio.load(threadListHTML);
const list = $('#thread_list > .j_thread_list[data-field]');
ctx.state.data = {
title: `${kw}吧`,
link: `https://tieba.baidu.com/f?kw=${encodeURIComponent(kw)}`,
item:
list &&
list
.map((index, element) => {
const item = $(element);
const { id, author_name } = item.data('field');
const time = item.find('.threadlist_reply_date').text().trim(); // prettier-ignore
const title = item.find('a.j_th_tit').text().trim(); // prettier-ignore
const details = item.find('.threadlist_abs').text().trim(); // prettier-ignore
const medias = item
.find('.threadlist_media img')
.map((index, element) => {
const item = $(element);
return `<img src="${item.attr('bpic')}">`; // prettier-ignore
})
.get()
.join('');
return {
title,
description: `<p>${details}</p><p>${medias}</p><p>作者:${author_name}</p>`,
pubDate: getPubDate(time).toUTCString(),
link: `https://tieba.baidu.com/p/${id}`,
};
})
.get(),
};
};
-36
View File
@@ -1,36 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const uid = ctx.params.uid;
const response = await got({
method: 'get',
url: `https://tieba.baidu.com/home/main?un=${uid}`,
});
const data = response.data;
const $ = cheerio.load(data);
const name = $('span.userinfo_username').text();
const list = $('div.n_right.clearfix');
let imgurl;
ctx.state.data = {
title: `${name} 的贴吧`,
link: `https://tieba.baidu.com/home/main?un=${uid}`,
item:
list &&
list
.map((index, item) => {
item = $(item).find('.n_contain');
imgurl = item.find('ul.n_media.clearfix img').attr('original');
return {
title: item.find('div.thread_name a').attr('title'),
pubDate: Date.parse(item.parent().find('div .n_post_time').text()),
description: `${item.find('div.n_txt').text()}<br><img src="${imgurl}">`,
link: item.find('div.thread_name a').attr('href'),
};
})
.get(),
};
};
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
'/tieba/forum/:kw': ['u3u'],
'/tieba/forum/good/:kw/:cid?': ['u3u'],
'/tieba/post/:id': ['u3u'],
'/tieba/post/lz/:id': ['u3u'],
'/tieba/user/:uid': ['igxlin', 'nczitzk'],
};
+52
View File
@@ -0,0 +1,52 @@
module.exports = {
'baidu.com': {
_name: '贴吧',
tieba: [
{
title: '帖子列表',
docs: 'https://docs.rsshub.app/bbs.html#bai-du-tie-ba',
source: 'f',
target: (params, url) => {
const type = new URL(url).searchParams.get('tab');
if (!type || type === 'main') {
return `/tieba/forum/${new URL(url).searchParams.get('kw')}`;
}
},
},
{
title: '精品帖子',
docs: 'https://docs.rsshub.app/bbs.html#bai-du-tie-ba',
source: 'f',
target: (params, url) => {
const type = new URL(url).searchParams.get('tab');
if (type === 'good') {
return `/tieba/forum/good/${new URL(url).searchParams.get('kw')}`;
}
},
},
{
title: '帖子动态',
docs: 'https://docs.rsshub.app/bbs.html#bai-du-tie-ba',
source: '/p/:id',
target: '/tieba/post/:id',
},
{
title: '只看楼主',
docs: 'https://docs.rsshub.app/bbs.html#bai-du-tie-ba',
source: '/p/:id',
target: '/tieba/post/lz/:id',
},
{
title: '用户帖子',
docs: 'https://docs.rsshub.app/bbs.html#tie-ba',
source: '/home/main',
target: (params, url) => {
const uid = new URL(url).searchParams.get('un');
if (uid) {
return `/tieba/user/${uid}`;
}
},
},
],
},
};
+7
View File
@@ -0,0 +1,7 @@
module.exports = (router) => {
router.get('/tieba/forum/:kw', require('./tieba/forum'));
router.get('/tieba/forum/good/:kw/:cid?', require('./tieba/forum'));
router.get('/tieba/post/:id', require('./tieba/post'));
router.get('/tieba/post/lz/:id', require('./tieba/post'));
router.get('/tieba/user/:uid', require('./tieba/user'));
};
+1
View File
@@ -0,0 +1 @@
<p>{{ details }}</p><p>{{@ medias }}</p><p>作者:{{ author_name }}</p>
+4
View File
@@ -0,0 +1,4 @@
<p>{{@ pubContent }}</p><br>
作者:{{ author }}<br>
楼层:{{ num }}<br>
{{ from }}
+65
View File
@@ -0,0 +1,65 @@
const cheerio = require('cheerio');
const got = require('@/utils/got');
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 { kw, cid } = ctx.params;
// PC端:https://tieba.baidu.com/f?kw=${encodeURIComponent(kw)}
// 移动端接口:https://tieba.baidu.com/mo/q/m?kw=${encodeURIComponent(kw)}&lp=5024&forum_recommend=1&lm=0&cid=0&has_url_param=1&pn=0&is_ajax=1
const params = { kw: encodeURIComponent(kw) };
ctx._matchedRoute.includes('good') && (params.tab = 'good');
cid && (params.cid = cid);
const { data } = await got(`https://tieba.baidu.com/f`, {
headers: {
Referer: 'https://tieba.baidu.com/',
},
searchParams: params,
});
const threadListHTML = cheerio
.load(data)('code[id="pagelet_html_frs-list/pagelet/thread_list"]')
.contents()
.filter((e) => e.nodeType === '8');
const $ = cheerio.load(threadListHTML.prevObject[0].data);
const list = $('#thread_list > .j_thread_list[data-field]')
.toArray()
.map((element) => {
const item = $(element);
const { id, author_name } = item.data('field');
const time = item.find('.threadlist_reply_date').text().trim();
const title = item.find('a.j_th_tit').text().trim();
const details = item.find('.threadlist_abs').text().trim();
const medias = item
.find('.threadlist_media img')
.toArray()
.map((element) => {
const item = $(element);
return `<img src="${item.attr('bpic').split('?tbpicau')[0]}">`;
})
.join('');
return {
title,
// description: `<p>${details}</p><p>${medias}</p><p>作者:${author_name}</p>`,
description: art(path.join(__dirname, '../templates/forum.art'), {
details,
medias,
author_name,
}),
pubDate: timezone(parseDate(time, ['HH:mm', 'M-D']), +8),
link: `https://tieba.baidu.com/p/${id}`,
};
});
ctx.state.data = {
title: `${kw}吧`,
description: cheerio.load(data)('meta[name="description"]').attr('content'),
link: `https://tieba.baidu.com/f?kw=${encodeURIComponent(kw)}`,
item: list,
};
};
@@ -1,5 +1,9 @@
const cheerio = require('cheerio');
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
const timezone = require('@/utils/timezone');
const { art } = require('@/utils/render');
const path = require('path');
/**
* 获取最新的帖子回复(倒序查看)
@@ -11,15 +15,13 @@ const got = require('@/utils/got');
* @returns
*/
async function getPost(id, lz = 0, pn = 7e6) {
const { data } = await got({
method: 'get',
url: `https://tieba.baidu.com/p/${id}?see_lz=${lz}&pn=${pn}&ajax=1`,
const { data } = await got(`https://tieba.baidu.com/p/${id}?see_lz=${lz}&pn=${pn}&ajax=1`, {
headers: {
Referer: 'https://tieba.baidu.com/',
},
});
const $ = cheerio.load(data);
const max = Number.parseInt($('[max-page]').attr('max-page'));
const max = parseInt($('[max-page]').attr('max-page'));
if (max > pn) {
return getPost(id, max);
}
@@ -33,7 +35,7 @@ module.exports = async (ctx) => {
const $ = cheerio.load(html);
const title = $('.core_title_txt').attr('title');
// .substr(3);
const list = $('.p_postlist > [data-field]:not(:has(".ad_bottom_view"))');
const list = $('.p_postlist > [data-field]:not(:has(.ad_bottom_view))');
ctx.state.data = {
title: lz ? `【只看楼主】${title}` : title,
@@ -42,22 +44,34 @@ module.exports = async (ctx) => {
item:
list &&
list
.map((index, element) => {
.map((_, element) => {
const item = $(element);
const { author, content } = item.data('field');
const [from, num, time] = item
const tempList = item
.find('.post-tail-wrap > .tail-info')
.map((index, element) => $(element).text())
.get();
.toArray()
.map((element) => $(element).text());
let [pubContent, from, num, time] = ['', '', '', ''];
if (0 === tempList.length && 'date' in content) {
num = `${content.post_no}楼`;
time = content.date;
pubContent = item.find('.j_d_post_content').html();
} else if (2 === tempList.length) {
[num, time] = tempList;
pubContent = content.content;
} else if (3 === tempList.length) {
[from, num, time] = tempList;
pubContent = content.content;
}
return {
title: `${author.user_name}回复了帖子《${title}》`,
description: `
<p>${content.content}</p><br>
作者:${author.user_name}<br>
楼层:${num}<br>
${from}
`, // prettier-ignore
pubDate: new Date(time),
description: art(path.join(__dirname, '../templates/post.art'), {
pubContent,
author: author.user_name,
num,
from,
}),
pubDate: timezone(parseDate(time, 'YYYY-MM-DD hh:mm'), +8),
link: `https://tieba.baidu.com/p/${id}?pid=${content.post_id}#${content.post_id}`,
};
})
+33
View File
@@ -0,0 +1,33 @@
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 uid = ctx.params.uid;
const response = await got(`https://tieba.baidu.com/home/main?un=${uid}`);
const data = response.data;
const $ = cheerio.load(data);
const name = $('span.userinfo_username').text();
const list = $('div.n_right.clearfix');
let imgurl;
ctx.state.data = {
title: `${name} 的贴吧`,
link: `https://tieba.baidu.com/home/main?un=${uid}`,
item:
list &&
list.toArray().map((item) => {
item = $(item).find('.n_contain');
imgurl = item.find('ul.n_media.clearfix img').attr('original');
return {
title: item.find('div.thread_name a').attr('title'),
pubDate: timezone(parseDate(item.parent().find('div .n_post_time').text(), ['YYYY-MM-DD', 'HH:mm']), +8),
description: `${item.find('div.n_txt').text()}<br><img src="${imgurl}">`,
link: item.find('div.thread_name a').attr('href'),
};
}),
};
};