|
1 | 1 | import { Route } from '@/types';
|
| 2 | +import { getCurrentPath } from '@/utils/helpers'; |
| 3 | +const __dirname = getCurrentPath(import.meta.url); |
| 4 | + |
2 | 5 | import cache from '@/utils/cache';
|
3 | 6 | import got from '@/utils/got';
|
4 | 7 | import { load } from 'cheerio';
|
5 | 8 | import timezone from '@/utils/timezone';
|
6 | 9 | import { parseDate } from '@/utils/parse-date';
|
| 10 | +import { art } from '@/utils/render'; |
| 11 | +import path from 'node:path'; |
7 | 12 |
|
8 |
| -export const route: Route = { |
9 |
| - path: '/zt/:id', |
10 |
| - categories: ['new-media'], |
11 |
| - example: '/ithome/zt/xijiayi', |
12 |
| - parameters: { id: '专题 id' }, |
13 |
| - features: { |
14 |
| - requireConfig: false, |
15 |
| - requirePuppeteer: false, |
16 |
| - antiCrawler: false, |
17 |
| - supportBT: false, |
18 |
| - supportPodcast: false, |
19 |
| - supportScihub: false, |
20 |
| - }, |
21 |
| - radar: [ |
22 |
| - { |
23 |
| - source: ['ithome.com/zt/:id'], |
24 |
| - }, |
25 |
| - ], |
26 |
| - name: '专题', |
27 |
| - maintainers: ['nczitzk'], |
28 |
| - handler, |
29 |
| - description: `所有专题请见[此处](https://www.ithome.com/zt)`, |
30 |
| -}; |
31 |
| - |
32 |
| -async function handler(ctx) { |
33 |
| - const id = ctx.req.param('id'); |
| 13 | +export const handler = async (ctx) => { |
| 14 | + const { id = 'xijiayi' } = ctx.req.param(); |
| 15 | + const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; |
34 | 16 |
|
35 | 17 | const rootUrl = 'https://www.ithome.com';
|
36 |
| - const currentUrl = `${rootUrl}/zt/${id}`; |
| 18 | + const currentUrl = new URL(`zt/${id}`, rootUrl).href; |
| 19 | + |
| 20 | + const { data: response } = await got(currentUrl); |
37 | 21 |
|
38 |
| - const response = await got({ |
39 |
| - method: 'get', |
40 |
| - url: currentUrl, |
41 |
| - }); |
| 22 | + const $ = load(response); |
42 | 23 |
|
43 |
| - const $ = load(response.data); |
| 24 | + const author = 'IT之家'; |
| 25 | + const language = 'zh'; |
44 | 26 |
|
45 |
| - const list = $('.newsbody a') |
46 |
| - .map((_, item) => { |
| 27 | + let items = $('div.newsbody') |
| 28 | + .slice(0, limit) |
| 29 | + .toArray() |
| 30 | + .map((item) => { |
47 | 31 | item = $(item);
|
48 | 32 |
|
| 33 | + const title = item.find('h2').text(); |
| 34 | + const image = item.find('img').prop('data-original') ?? item.find('img').prop('src'); |
| 35 | + |
49 | 36 | return {
|
50 |
| - title: item.text(), |
51 |
| - link: item.attr('href'), |
| 37 | + title, |
| 38 | + pubDate: timezone( |
| 39 | + parseDate( |
| 40 | + item |
| 41 | + .find('span.time script') |
| 42 | + .text() |
| 43 | + .match(/'(.*?)'/) |
| 44 | + ), |
| 45 | + +8 |
| 46 | + ), |
| 47 | + link: item.find('a').first().prop('href'), |
| 48 | + author: item.find('div.editor').contents().first().text(), |
| 49 | + image, |
| 50 | + banner: image, |
| 51 | + language, |
52 | 52 | };
|
53 |
| - }) |
54 |
| - .get(); |
| 53 | + }); |
55 | 54 |
|
56 |
| - const items = await Promise.all( |
57 |
| - list.map((item) => |
| 55 | + items = await Promise.all( |
| 56 | + items.map((item) => |
58 | 57 | cache.tryGet(item.link, async () => {
|
59 |
| - const detailResponse = await got({ |
60 |
| - method: 'get', |
61 |
| - url: item.link, |
62 |
| - }); |
| 58 | + const { data: detailResponse } = await got(item.link); |
| 59 | + |
| 60 | + const $$ = load(detailResponse); |
| 61 | + |
| 62 | + $$('p.ad-tips, a.topic-bar').remove(); |
63 | 63 |
|
64 |
| - const content = load(detailResponse.data); |
65 |
| - const post = content('.post_content'); |
| 64 | + $$('div#paragraph p img').each((_, el) => { |
| 65 | + el = $$(el); |
66 | 66 |
|
67 |
| - post.find('img[data-original]').each((_, ele) => { |
68 |
| - ele = $(ele); |
69 |
| - ele.attr('src', ele.attr('data-original')); |
70 |
| - ele.removeAttr('class'); |
71 |
| - ele.removeAttr('data-original'); |
| 67 | + const src = el.prop('data-original'); |
| 68 | + |
| 69 | + if (src) { |
| 70 | + el.replaceWith( |
| 71 | + art(path.join(__dirname, 'templates/description.art'), { |
| 72 | + images: [ |
| 73 | + { |
| 74 | + src, |
| 75 | + alt: el.prop('alt'), |
| 76 | + }, |
| 77 | + ], |
| 78 | + }) |
| 79 | + ); |
| 80 | + } |
72 | 81 | });
|
73 | 82 |
|
74 |
| - item.description = post.html(); |
75 |
| - item.author = content('#author_baidu').text().replace('作者:', ''); |
76 |
| - item.pubDate = timezone(parseDate(content('#pubtime_baidu').text()), +8); |
| 83 | + const title = $$('h1').text(); |
| 84 | + const description = $$('div#paragraph').html(); |
| 85 | + const image = $$('div#paragraph img').first().prop('src'); |
| 86 | + |
| 87 | + item.title = title; |
| 88 | + item.description = description; |
| 89 | + item.pubDate = timezone(parseDate($$('span#pubtime_baidu').text()), +8); |
| 90 | + item.category = $$('div.cv a') |
| 91 | + .toArray() |
| 92 | + .map((c) => $$(c).text()) |
| 93 | + .slice(1); |
| 94 | + item.author = $$('span#author_baidu').contents().last().text() || $$('span#source_baidu').contents().last().text() || $$('span#editor_baidu').contents().last().text(); |
| 95 | + item.content = { |
| 96 | + html: description, |
| 97 | + text: $$('div#paragraph').text(), |
| 98 | + }; |
| 99 | + item.image = image; |
| 100 | + item.banner = image; |
| 101 | + item.language = language; |
77 | 102 |
|
78 | 103 | return item;
|
79 | 104 | })
|
80 | 105 | )
|
81 | 106 | );
|
82 | 107 |
|
| 108 | + const image = new URL($('meta[property="og:image"]').prop('content'), rootUrl).href; |
| 109 | + |
83 | 110 | return {
|
84 |
| - title: `${$('title').text()} - IT之家`, |
| 111 | + title: `${author} - ${$('title').text()}`, |
| 112 | + description: $('meta[name="description"]').prop('content'), |
85 | 113 | link: currentUrl,
|
86 | 114 | item: items,
|
| 115 | + allowEmpty: true, |
| 116 | + image, |
| 117 | + author, |
| 118 | + language, |
87 | 119 | };
|
88 |
| -} |
| 120 | +}; |
| 121 | + |
| 122 | +export const route: Route = { |
| 123 | + path: '/zt/:id?', |
| 124 | + name: '专题', |
| 125 | + url: 'ithome.com', |
| 126 | + maintainers: ['nczitzk'], |
| 127 | + handler, |
| 128 | + example: '/ithome/zt/xijiayi', |
| 129 | + parameters: { category: '专题 id,默认为 xijiayi,即 [喜加一](https://www.ithome.com/zt/xijiayi),可在对应专题页 URL 中找到' }, |
| 130 | + description: `:::tip |
| 131 | + 更多专题请见 [IT之家专题](https://www.ithome.com/zt) |
| 132 | + :::`, |
| 133 | + categories: ['new-media'], |
| 134 | + |
| 135 | + features: { |
| 136 | + requireConfig: false, |
| 137 | + requirePuppeteer: false, |
| 138 | + antiCrawler: false, |
| 139 | + supportRadar: true, |
| 140 | + supportBT: false, |
| 141 | + supportPodcast: false, |
| 142 | + supportScihub: false, |
| 143 | + }, |
| 144 | + radar: [ |
| 145 | + { |
| 146 | + source: ['ithome.com/zt/:id'], |
| 147 | + target: '/zt/:id', |
| 148 | + }, |
| 149 | + ], |
| 150 | +}; |
0 commit comments