feat(route): add 酷安热榜/头条 (#6967)

This commit is contained in:
Xizeyoupan 2021-03-07 18:07:46 +08:00 committed by GitHub
parent e397e11fbd
commit e492277271
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 252 additions and 95 deletions

View File

@ -475,37 +475,61 @@ type 为 all 时category 参数不支持 cost 和 free
## 酷安
### 图文 - 编辑精选
### 图文
<Route author="xizeyoupan" example="/coolapk/tuwen" path="/coolapk/tuwen">
<Route author="xizeyoupan" example="/coolapk/tuwen" path="/coolapk/tuwen/:type?" :paramsDesc="['默认为hot']">
::: tip
每晚更新,更新较慢。
:::
| 参数名称 | 编辑精选 | 最新 |
| -------- | -------- | ------ |
| type | hot | latest |
</Route>
### 新鲜图文
### 头条
<Route author="xizeyoupan" example="/coolapk/tuwen-xinxian" path="/coolapk/tuwen-xinxian"/>
<Route author="xizeyoupan" example="/coolapk/toutiao" path="/coolapk/toutiao/:type?" :paramsDesc="['默认为history']">
### 看看号 - 图文 & 动态
| 参数名称 | 历史头条 | 最新 |
| -------- | -------- | ------ |
| type | history | latest |
</Route>
### 看看号
<Route author="xizeyoupan" example="/coolapk/dyh/1524" path="/coolapk/dyh/:dyhId" :paramsDesc="['看看号ID']">
::: tip
仅限于采集**站内订阅**的看看号的**图文及动态**内容。看看号 ID 可在看看号界面右上分享 - 复制链接得到。
仅限于采集**站内订阅**的看看号的内容。看看号 ID 可在看看号界面右上分享 - 复制链接得到。
:::
</Route>
### 话题 - 图文 & 动态
### 话题
<Route author="xizeyoupan" example="/coolapk/huati/酷安夜话" path="/coolapk/huati/:tag" :paramsDesc="['话题名称']"/>
### 用户 - 图文 & 动态
### 用户
<Route author="xizeyoupan" example="/coolapk/user/3177668/dynamic" path="/coolapk/user/:uid/dynamic" :paramsDesc="['uid,在个人界面右上分享-复制链接获取']"/>
<Route author="xizeyoupan" example="/coolapk/user/3177668/dynamic" path="/coolapk/user/:uid/dynamic" :paramsDesc="['在个人界面右上分享-复制链接获取']"/>
### 热榜
<Route author="xizeyoupan" example="/coolapk/hot" path="/coolapk/hot/:type?/:period?" :paramsDesc="['默认为`jrrm`','默认为`daily`']">
| 参数名称 | 今日热门 | 点赞榜 | 评论榜 | 收藏榜 | 酷图榜 |
| -------- | -------- | ------ | ------ | ------ | ------ |
| type | jrrm | dzb | plb | scb | ktb |
| 参数名称 | 日榜 | 周榜 |
| -------- | ----- | ------ |
| period | daily | weekly |
::: tip
今日热门没有周榜,酷图榜日榜的参数会变成周榜,周榜的参数会变成月榜。
:::
</Route>
## 快递 100

View File

@ -2027,11 +2027,13 @@ router.get('/hinatazaka46/blog', require('./routes/hinatazaka46/blog'));
router.get('/sakurazaka46/blog', require('./routes/sakurazaka46/blog'));
// 酷安
router.get('/coolapk/tuwen', require('./routes/coolapk/tuwen'));
router.get('/coolapk/tuwen/:type?', require('./routes/coolapk/tuwen'));
router.get('/coolapk/tuwen-xinxian', require('./routes/coolapk/tuwen'));
router.get('/coolapk/toutiao/:type?', require('./routes/coolapk/toutiao'));
router.get('/coolapk/huati/:tag', require('./routes/coolapk/huati'));
router.get('/coolapk/user/:uid/dynamic', require('./routes/coolapk/userDynamic'));
router.get('/coolapk/dyh/:dyhId', require('./routes/coolapk/dyh'));
router.get('/coolapk/hot/:type?/:period?', require('./routes/coolapk/hot'));
// 模型网
router.get('/moxingnet', require('./routes/moxingnet'));

View File

@ -24,14 +24,7 @@ module.exports = async (ctx) => {
targetTitle = item.targetRow.title;
}
if (item.type === 12) {
// 图文内容
return utils.parseTuwen(item, ctx);
} else if (String(item.type) in utils.dynamicTpye) {
return utils.parseDongtai(item, ctx);
} else {
return null;
}
return utils.parseDynamic(item, ctx);
})
);

99
lib/routes/coolapk/hot.js Normal file
View File

@ -0,0 +1,99 @@
const got = require('@/utils/got');
const utils = require('./utils');
const getLinkAndTitle = (type, period) => {
const baseURL = 'https://api.coolapk.com/v6/page/dataList?url=';
let link;
const res = {};
const types = {
jrrm: {
title: '今日热门',
url: baseURL + '%2Ffeed%2FstatList%3FcacheExpires%3D300%26statType%3Dday%26sortField%3Ddetailnum%26title%3D%E4%BB%8A%E6%97%A5%E7%83%AD%E9%97%A8&title=%E4%BB%8A%E6%97%A5%E7%83%AD%E9%97%A8&subTitle=&page=1',
},
dzb: {
title: '点赞榜',
sortField: 'likenum',
},
scb: {
title: '收藏榜',
sortField: 'favnum',
},
plb: {
title: '评论榜',
sortField: 'replynum',
},
ktb: {
title: '酷图榜',
sortField: 'likenum',
},
};
const periods = {
daily: {
description: '日榜',
statType: 'day',
},
weekly: {
description: '周榜',
statType: '7days',
},
};
if (type === 'jrrm') {
res.link = types.jrrm.url;
res.title = types.jrrm.title;
return res;
} else if (type === 'ktb') {
const trans = {
daily: {
description: '周榜',
statDays: '7days',
},
weekly: {
description: '月榜',
statDays: '30days',
},
};
link = `#/feed/coolPictureList?statDays=` + trans[period].statDays + `&listType=statFavNum&buildCard=1&title=` + trans[period].description + `&page=1`;
res.title = '酷图榜-' + trans[period].description;
} else {
link = `#/feed/statList?statType=` + periods[period].statType + `&sortField=` + types[type].sortField + `&title=` + periods[period].description + `&page=1`;
res.title = types[type].title + `-` + periods[period].description;
}
res.link = baseURL + encodeURIComponent(link);
return res;
};
module.exports = async (ctx) => {
const type = ctx.params.type || 'jrrm';
const period = ctx.params.period || 'daily';
const { link, title } = getLinkAndTitle(type, period);
const r = await got({
method: 'get',
url: link,
headers: utils.getHeaders(),
});
const data = r.data.data;
const t = [];
for (const i of data) {
if (i.entityType === 'card') {
for (const k of i.entities) {
t.push(k);
}
} else {
t.push(i);
}
}
let out = await Promise.all(t.map(async (item) => utils.parseDynamic(item, ctx)));
out = out.filter((i) => i);
ctx.state.data = {
title: title,
link: 'https://www.coolapk.com/',
description: `热榜-` + title,
item: out,
};
};

View File

@ -11,18 +11,7 @@ module.exports = async (ctx) => {
});
const data = response.data.data;
let out = await Promise.all(
data.map(async (item) => {
if (item.type === 12) {
// 图文内容
return utils.parseTuwen(item, ctx);
} else if (String(item.type) in utils.dynamicTpye) {
return utils.parseDongtai(item, ctx);
} else {
return null;
}
})
);
let out = await Promise.all(data.map(async (item) => utils.parseDynamic(item, ctx)));
out = out.filter((i) => i); // 去除空值
if (out.length === 0) {

View File

@ -0,0 +1,31 @@
const got = require('@/utils/got');
const utils = require('./utils');
module.exports = async (ctx) => {
const type = ctx.params.type || 'history';
const urls = {
history: {
url: 'https://api.coolapk.com/v6/page/dataList?url=%23%2ffeed%2fheadlineV8List%3ftype%3d0%2c5%2c9%2c8%2c12%2c10%2c11%2c13%26title%3d%e5%8e%86%e5%8f%b2%e5%a4%b4%e6%9d%a1%26page%3d1',
title: '历史头条',
},
latest: {
url: 'https://api.coolapk.com/v6/page/dataList?url=%23%2ffeed%2fdigestList%3ftype%3d0%2c5%2c12%2c10%2c11%2c13%2c8%2c9%26title%3d%e6%9c%80%e6%96%b0%e5%8a%a8%e6%80%81%26page%3d1',
title: '最新动态',
},
};
const response = await got({
method: 'get',
url: urls[type].url,
headers: utils.getHeaders(),
});
const data = response.data.data;
const out = await Promise.all(data.map(async (item) => utils.parseDynamic(item, ctx)));
ctx.state.data = {
title: urls[type].title,
link: 'https://www.coolapk.com/',
description: urls[type].title,
item: out,
};
};

View File

@ -2,8 +2,9 @@ const got = require('@/utils/got');
const utils = require('./utils');
module.exports = async (ctx) => {
const type = ctx.params.type || 'hot';
let full_url, feedTitle;
if (ctx.originalUrl.startsWith(`/coolapk/tuwen-xinxian`)) {
if (ctx.originalUrl.startsWith(`/coolapk/tuwen-xinxian`) || type === 'latest') {
// 实时
full_url =
utils.base_url +
@ -21,7 +22,7 @@ module.exports = async (ctx) => {
});
const data = response.data.data;
const out = await Promise.all(data.map(async (item) => utils.parseTuwen(item, ctx)));
const out = await Promise.all(data.map(async (item) => utils.parseDynamic(item, ctx)));
ctx.state.data = {
title: `${feedTitle}`,

View File

@ -3,7 +3,7 @@ const utils = require('./utils');
module.exports = async (ctx) => {
const uid = ctx.params.uid;
const full_url = utils.base_url + `/v6/user/feedList?uid=${uid}&page=1&showAnonymous=0`;
const full_url = utils.base_url + `/v6/user/feedList?uid=${uid}&page=1&showAnonymous=0&isIncludeTop=1&showDoing=1`;
let username;
const response = await got({
method: 'get',
@ -20,14 +20,7 @@ module.exports = async (ctx) => {
username = item.username;
}
if (item.type === 12) {
// 图文内容
return utils.parseTuwen(item, ctx);
} else if (String(item.type) in utils.dynamicTpye) {
return utils.parseDongtai(item, ctx);
} else {
return null;
}
return utils.parseDynamic(item, ctx);
})
);

View File

@ -2,7 +2,7 @@ const md5 = require('@/utils/md5');
const cheerio = require('cheerio');
const got = require('@/utils/got');
const dynamicTpye = { 0: '基本动态', 9: '评论', 20: '交易动态' };
const dynamicTpye = { 0: '基本动态', 8: '酷图', 9: '评论', 10: '提问', 11: '回答', 12: '图文', 15: '二手', 17: '观点', 20: '交易动态' };
const getRandomDEVICE_ID = () => {
let id = [8, 4, 4, 4, 12];
@ -29,9 +29,10 @@ const getHeaders = () => ({
'X-App-Token': get_app_token(),
'X-Sdk-Int': '29',
'X-Sdk-Locale': 'zh-CN',
'X-App-Version': '10.4',
'X-App-Code': '2007081',
'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 10; Redmi K30 5G MIUI/V12.0.3.0.QGICMXM) (#Build; Redmi; Redmi K30 5G; QKQ1.191222.002 test-keys; 10) +CoolMarket/10.4-2007081',
'X-App-Version': '11.0',
'X-Api-Version': '11',
'X-App-Code': '2101202',
'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 10; Redmi K30 5G MIUI/V12.0.3.0.QGICMXM) (#Build; Redmi; Redmi K30 5G; QKQ1.191222.002 test-keys; 10) +CoolMarket/11.0-2101202',
});
const parseTuwenFromRaw = (raw) =>
@ -48,64 +49,89 @@ const parseTuwenFromRaw = (raw) =>
}
});
const parseDongtai = async (item, ctx) => {
const author = item.username;
const parseDynamic = async (item, ctx) => {
const pubdate = new Date(item.lastupdate * 1000).toUTCString();
const itemUrl = item.shareUrl.split('?')[0];
let description;
if (item.issummary) {
// 需要爬内容
description = await ctx.cache.tryGet(itemUrl, async () => {
const result = await got({
method: 'get',
url: itemUrl,
headers: getHeaders(),
});
const message = `<p>` + result.data.data.message.split('\n').join('<br>') + `</p>`;
const picArr = item.picArr.filter((i) => i).map((i) => `<img src="${i}">`); // 若无图片item.picArr=[""]
return message + picArr.join('');
});
} else {
const picArr = item.picArr.filter((i) => i).map((i) => `<img src="${i}">`);
description = `<p>` + item.message + `</p>` + picArr.join('');
if (item.entityType === 'sponsorCard') {
return;
}
const $ = cheerio.load('<div class="title-filter">' + description + '</div>');
const title = $('.title-filter').text().slice(0, 30).trim() + '...';
return {
title: title,
description: description,
pubDate: pubdate,
link: itemUrl,
author: author,
};
};
const itemUrl = item.shareUrl.split('?')[0];
let description, title;
const type = parseInt(item.type);
switch (type) {
case 0:
case 8:
case 9:
case 10:
case 11:
case 15:
case 17:
case 20: {
// //////////////////////////////////////////// 基本内容 ////////////////////////////////////////////
if (item.issummary) {
// 需要爬内容
description = await ctx.cache.tryGet(itemUrl, async () => {
const result = await got({
method: 'get',
url: itemUrl,
headers: getHeaders(),
});
const message = `<p>` + result.data.data.message.split('\n').join('<br>') + `</p>`;
const picArr = item.picArr.filter((i) => i).map((i) => `<img src="${i}">`); // 若无图片item.picArr=[""]
return message + picArr.join('');
});
} else {
const picArr = item.picArr.filter((i) => i).map((i) => `<img src="${i}">`);
description = `<p>` + item.message + `</p>` + picArr.join('');
}
const $ = cheerio.load('<div class="title-filter">' + description + '</div>');
title = $('.title-filter').text().slice(0, 30).trim() + '...';
const parseTuwen = async (item, ctx) => {
const title = item.title;
const pubdate = new Date(item.lastupdate * 1000).toUTCString();
const author = item.username;
// //////////////////////////////////////////// 基本内容结束 ////////////////////////////////////////////
const description = await ctx.cache.tryGet(item.shareUrl, async () => {
const result = await got({
method: 'get',
url: item.shareUrl,
headers: getHeaders(),
});
if (type === 17) {
const keys = item.extra_key.split(',');
description += `<p>` + item.vote.message_title + ` 已选${keys.length}项</p>`;
for (const i of item.vote.options) {
if (keys.includes(String(i.id))) {
description += `<p>${i.title}√</p>`;
}
}
} else if (type === 10 || type === 11) {
title = `${item.message_title} 更多:` + title;
}
const raw = JSON.parse(result.data.data.message_raw_output);
break;
}
case 12: {
title = item.title;
description = await ctx.cache.tryGet(itemUrl, async () => {
const result = await got({
method: 'get',
url: itemUrl,
headers: getHeaders(),
});
const tags = parseTuwenFromRaw(raw);
const raw = JSON.parse(result.data.data.message_raw_output);
return `<img src="${result.data.data.pic}"><hr>` + tags.join('');
});
const tags = parseTuwenFromRaw(raw);
return `<img src="${result.data.data.pic}"><hr>` + tags.join('');
});
break;
}
default:
// console.log(item.type);
// console.log(item);
break;
}
return {
title: title,
description: description,
pubDate: pubdate,
link: item.shareUrl,
guid: item.shareUrl.split('?')[0],
author: author,
guid: itemUrl,
author: item.username,
};
};
@ -113,7 +139,6 @@ module.exports = {
get_app_token,
base_url,
getHeaders,
parseTuwen,
parseDongtai,
parseDynamic,
dynamicTpye,
};