diff --git a/lib/router.js b/lib/router.js index 5958b06b04..9ba588b478 100644 --- a/lib/router.js +++ b/lib/router.js @@ -376,7 +376,7 @@ router.get('/autotrader/:query', lazyloadRouteHandler('./routes/autotrader')); router.get('/geekpark/breakingnews', lazyloadRouteHandler('./routes/geekpark/breakingnews')); // 搜狗 -router.get('/sogou/doodles', lazyloadRouteHandler('./routes/sogou/doodles')); +// router.get('/sogou/doodles', lazyloadRouteHandler('./routes/sogou/doodles')); // 香港天文台 router.get('/hko/weather', lazyloadRouteHandler('./routes/hko/weather')); @@ -690,7 +690,7 @@ router.get('/digitaling/articles/:category/:subcate?', lazyloadRouteHandler('./r router.get('/digitaling/projects/:category', lazyloadRouteHandler('./routes/digitaling/project')); // Bing壁纸 -router.get('/bing', lazyloadRouteHandler('./routes/bing/index')); +// router.get('/bing', lazyloadRouteHandler('./routes/bing/index')); // AlgoCasts router.get('/algocasts', lazyloadRouteHandler('./routes/algocasts/all')); diff --git a/lib/v2/baidu/maintainer.js b/lib/v2/baidu/maintainer.js index 7cbf506d11..b196204cb6 100644 --- a/lib/v2/baidu/maintainer.js +++ b/lib/v2/baidu/maintainer.js @@ -1,5 +1,6 @@ module.exports = { '/gushitong/index': ['CaoMeiYouRen'], + '/search/:keyword': ['CaoMeiYouRen'], '/tieba/forum/good/:kw/:cid?/:sortBy?': ['u3u'], '/tieba/forum/:kw/:sortBy?': ['u3u'], '/tieba/post/:id': ['u3u'], diff --git a/lib/v2/baidu/radar.js b/lib/v2/baidu/radar.js index 20b2b9e809..5d91512855 100644 --- a/lib/v2/baidu/radar.js +++ b/lib/v2/baidu/radar.js @@ -1,6 +1,17 @@ module.exports = { 'baidu.com': { _name: '百度', + www: [ + { + title: '搜索', + docs: 'https://docs.rsshub.app/routes/other#bai-du-sou-suo', + source: ['/'], + target: (params, url) => { + const keyword = new URL(url).searchParams.get('wd'); + return `/baidu/search/${keyword}`; + }, + }, + ], gushitong: [ { title: '首页指数', @@ -73,7 +84,7 @@ module.exports = { top: [ { title: '热搜榜单', - docs: 'https://docs.rsshub.app/routes/other#bai-du-re-sou', + docs: 'https://docs.rsshub.app/routes/other#bai-du-re-sou-bang-dan', source: ['/board'], target: (_, url) => `/baidu/top/${new URL(url).searchParams.get('tab')}`, }, diff --git a/lib/v2/baidu/router.js b/lib/v2/baidu/router.js index e838568aab..3ccdbd2025 100644 --- a/lib/v2/baidu/router.js +++ b/lib/v2/baidu/router.js @@ -1,5 +1,6 @@ module.exports = (router) => { router.get('/gushitong/index', require('./gushitong')); + router.get('/search/:keyword', require('./search')); router.get('/tieba/forum/good/:kw/:cid?/:sortBy?', require('./tieba/forum')); router.get('/tieba/forum/:kw/:sortBy?', require('./tieba/forum')); router.get('/tieba/post/:id', require('./tieba/post')); diff --git a/lib/v2/baidu/search.js b/lib/v2/baidu/search.js new file mode 100644 index 0000000000..6c8def76a5 --- /dev/null +++ b/lib/v2/baidu/search.js @@ -0,0 +1,54 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { art } = require('@/utils/render'); +const path = require('path'); +const renderDescription = (description, images) => art(path.join(__dirname, './templates/description.art'), { description, images }); +const config = require('@/config').value; + +module.exports = async (ctx) => { + const { keyword } = ctx.params; + const url = `https://www.baidu.com/s?wd=${encodeURIComponent(keyword)}`; + const key = `baidu-search:${url}`; + + const items = await ctx.cache.tryGet( + key, + async () => { + const response = (await got(url)).data; + const visitedLinks = new Set(); + const $ = cheerio.load(response); + const contentLeft = $('#content_left'); + const containers = contentLeft.find('.c-container'); + return containers + .map((i, el) => { + const element = $(el); + const link = element.find('h3 a').first().attr('href'); + if (link && !visitedLinks.has(link)) { + visitedLinks.add(link); + const imgs = element + .find('img') + .map((_j, _el) => $(_el).attr('src')) + .toArray(); + const description = element.find('.c-gap-top-small [class^="content-right_"]').first().text() || element.find('.c-row').first().text() || element.find('.cos-row').first().text(); + return { + title: element.find('h3').first().text(), + description: renderDescription(description, imgs), + link: element.find('h3 a').first().attr('href'), + author: element.find('.c-row .c-color-gray').first().text() || '', + }; + } + return null; + }) + .toArray() + .filter((e) => e?.link); + }, + config.cache.routeExpire, + false + ); + + ctx.state.data = { + title: `${keyword} - 百度搜索`, + description: `${keyword} - 百度搜索`, + link: url, + item: items, + }; +}; diff --git a/lib/v2/baidu/templates/description.art b/lib/v2/baidu/templates/description.art new file mode 100644 index 0000000000..5f98f4ca33 --- /dev/null +++ b/lib/v2/baidu/templates/description.art @@ -0,0 +1,6 @@ +{{@ description }} +{{if images}} + {{each images}} + + {{/each}} +{{/if}} diff --git a/lib/routes/bing/index.js b/lib/v2/bing/daily-wallpaper.js similarity index 78% rename from lib/routes/bing/index.js rename to lib/v2/bing/daily-wallpaper.js index cf69bda81c..2eaf345ff3 100644 --- a/lib/routes/bing/index.js +++ b/lib/v2/bing/daily-wallpaper.js @@ -1,22 +1,21 @@ const got = require('@/utils/got'); -const queryString = require('query-string'); module.exports = async (ctx) => { const response = await got({ method: 'get', prefixUrl: 'https://cn.bing.com', url: 'HPImageArchive.aspx', - searchParams: queryString.stringify({ + searchParams: { format: 'js', idx: 0, - n: 7, + n: ctx.query.limit ? parseInt(ctx.query.limit, 10) : 7, mkt: 'zh-CN', - }), + }, }); const data = response.data; ctx.state.data = { title: 'Bing每日壁纸', - link: `https://cn.bing.com/`, + link: 'https://cn.bing.com/', item: data.images.map((item) => ({ title: item.copyright, description: ``, diff --git a/lib/v2/bing/maintainer.js b/lib/v2/bing/maintainer.js new file mode 100644 index 0000000000..e20ecae190 --- /dev/null +++ b/lib/v2/bing/maintainer.js @@ -0,0 +1,4 @@ +module.exports = { + '/': ['FHYunCai'], + '/search/:keyword': ['CaoMeiYouRen'], +}; diff --git a/lib/v2/bing/radar.js b/lib/v2/bing/radar.js new file mode 100644 index 0000000000..9023c3cf4b --- /dev/null +++ b/lib/v2/bing/radar.js @@ -0,0 +1,22 @@ +module.exports = { + 'bing.com': { + _name: 'Bing', + cn: [ + { + title: '每日壁纸', + docs: 'https://docs.rsshub.app/routes/picture#bing', + source: '/', + target: '/bing', + }, + { + title: '搜索', + docs: 'https://docs.rsshub.app/routes/other#bing', + source: '/', + target: (params, url) => { + const q = new URL(url).searchParams.get('q'); + return `/bing/search/${q}`; + }, + }, + ], + }, +}; diff --git a/lib/v2/bing/router.js b/lib/v2/bing/router.js new file mode 100644 index 0000000000..da2dfe81c1 --- /dev/null +++ b/lib/v2/bing/router.js @@ -0,0 +1,4 @@ +module.exports = function (router) { + router.get('/', require('./daily-wallpaper')); + router.get('/search/:keyword', require('./search')); +}; diff --git a/lib/v2/bing/search.js b/lib/v2/bing/search.js new file mode 100644 index 0000000000..41c021da78 --- /dev/null +++ b/lib/v2/bing/search.js @@ -0,0 +1,28 @@ +const parser = require('@/utils/rss-parser'); +const { parseDate } = require('@/utils/parse-date'); +const dayjs = require('dayjs'); +const localizedFormat = require('dayjs/plugin/localizedFormat'); +require('dayjs/locale/zh-cn'); +dayjs.extend(localizedFormat); + +module.exports = async (ctx) => { + const q = ctx.params.keyword; + const searchParams = new URLSearchParams({ + format: 'rss', + q, + }); + const url = new URL('https://cn.bing.com/search'); + url.search = searchParams.toString(); + const data = await parser.parseURL(url.toString()); + ctx.state.data = { + title: data.title, + link: data.link, + description: data.description + ' - ' + data.copyright, + image: data.image.url, + item: data.items.map((e) => ({ + ...e, + description: e.content, + pubDate: parseDate(e.pubDate, 'dddd, DD MMM YYYY HH:mm:ss [GMT]', 'zh-cn'), + })), + }; +}; diff --git a/lib/v2/google/maintainer.js b/lib/v2/google/maintainer.js index cbdd6ddd34..6abbdd362c 100644 --- a/lib/v2/google/maintainer.js +++ b/lib/v2/google/maintainer.js @@ -6,6 +6,7 @@ module.exports = { '/fonts/:sort?': ['Fatpandac'], '/news/:category/:locale': ['zoenglinghou'], '/scholar/:query': ['HenryQW'], + '/search/:keyword/:language?': ['CaoMeiYouRen'], '/sites/:id': ['hoilc'], '/sites/recentChanges/:id': ['nczitzk'], }; diff --git a/lib/v2/google/radar.js b/lib/v2/google/radar.js index 11a15acbb6..998151b5ba 100644 --- a/lib/v2/google/radar.js +++ b/lib/v2/google/radar.js @@ -1,6 +1,18 @@ module.exports = { 'google.com': { _name: '谷歌', + www: [ + { + title: '搜索', + docs: 'https://docs.rsshub.app/routes/other#google', + source: '/', + target: (params, url, document) => { + const q = new URL(url).searchParams.get('q'); + const lang = document.documentElement.lang; + return `/google/search/${q}/${lang}`; + }, + }, + ], chrome: [ { title: '插件更新', diff --git a/lib/v2/google/router.js b/lib/v2/google/router.js index b3064cf1cc..b4f2327971 100644 --- a/lib/v2/google/router.js +++ b/lib/v2/google/router.js @@ -6,4 +6,5 @@ module.exports = function (router) { router.get('/fonts/:sort?', require('./fonts')); router.get('/news/:category/:locale', require('./news')); router.get('/scholar/:query', require('./scholar')); + router.get('/search/:keyword/:language?', require('./search')); }; diff --git a/lib/v2/google/search.js b/lib/v2/google/search.js new file mode 100644 index 0000000000..da45fb8868 --- /dev/null +++ b/lib/v2/google/search.js @@ -0,0 +1,62 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { art } = require('@/utils/render'); +const path = require('path'); +const config = require('@/config').value; + +const renderDescription = (description, images) => art(path.join(__dirname, './templates/description.art'), { description, images }); + +module.exports = async (ctx) => { + const { keyword, language } = ctx.params; + const searchParams = new URLSearchParams({ + q: keyword, + }); + const tempUrl = new URL('https://www.google.com/search'); + tempUrl.search = searchParams.toString(); + const url = tempUrl.toString(); + const key = `google-search:${language}:${url}`; + const items = await ctx.cache.tryGet( + key, + async () => { + const response = ( + await got(url, { + headers: { + 'Accept-Language': language, + }, + }) + ).data; + const $ = cheerio.load(response); + const content = $('#rso'); + return content + .find('> div') + .map((i, el) => { + const element = $(el); + const link = element.find('div > div > div > div > div > span > a').first().attr('href'); + const title = element.find('div > div > div> div > div > span > a > h3').first().text(); + const imgs = element + .find('img') + .map((_j, _el) => $(_el).attr('src')) + .toArray(); + const description = element.find('div[style="-webkit-line-clamp:2"]').first().text() || element.find('div[role="heading"]').first().text(); + const author = element.find('div > div > div > div > div > span > a > div > div > span').first().text() || ''; + return { + link, + title, + description: renderDescription(description, imgs), + author, + }; + }) + .toArray() + .filter((e) => e?.link); + }, + config.cache.routeExpire, + false + ); + + ctx.state.data = { + title: `${keyword} - Google Search`, + description: `${keyword} - Google Search`, + link: url, + item: items, + }; +}; diff --git a/lib/v2/google/templates/description.art b/lib/v2/google/templates/description.art new file mode 100644 index 0000000000..5f98f4ca33 --- /dev/null +++ b/lib/v2/google/templates/description.art @@ -0,0 +1,6 @@ +{{@ description }} +{{if images}} + {{each images}} + + {{/each}} +{{/if}} diff --git a/lib/routes/sogou/doodles.js b/lib/v2/sogou/doodles.js similarity index 100% rename from lib/routes/sogou/doodles.js rename to lib/v2/sogou/doodles.js diff --git a/lib/v2/sogou/maintainer.js b/lib/v2/sogou/maintainer.js new file mode 100644 index 0000000000..b94d21c1b2 --- /dev/null +++ b/lib/v2/sogou/maintainer.js @@ -0,0 +1,4 @@ +module.exports = { + '/doodles': ['xyqfer'], + '/search/:keyword': ['CaoMeiYouRen'], +}; diff --git a/lib/v2/sogou/radar.js b/lib/v2/sogou/radar.js new file mode 100644 index 0000000000..a548c7fad7 --- /dev/null +++ b/lib/v2/sogou/radar.js @@ -0,0 +1,16 @@ +module.exports = { + 'sogou.com': { + _name: '搜狗', + www: [ + { + title: '搜索', + docs: 'https://docs.rsshub.app/routes/other#sou-gou-sou-suo', + source: '/', + target: (params, url) => { + const keyword = new URL(url).searchParams.get('query'); + return `/sogou/search/${keyword}`; + }, + }, + ], + }, +}; diff --git a/lib/v2/sogou/router.js b/lib/v2/sogou/router.js new file mode 100644 index 0000000000..6dbfd72eb5 --- /dev/null +++ b/lib/v2/sogou/router.js @@ -0,0 +1,4 @@ +module.exports = function (router) { + router.get('/doodles', require('./doodles')); + router.get('/search/:keyword', require('./search')); +}; diff --git a/lib/v2/sogou/search.js b/lib/v2/sogou/search.js new file mode 100644 index 0000000000..9cdb634548 --- /dev/null +++ b/lib/v2/sogou/search.js @@ -0,0 +1,54 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { art } = require('@/utils/render'); +const path = require('path'); +const { parseDate } = require('@/utils/parse-date'); +const config = require('@/config').value; + +const renderDescription = (description, images) => art(path.join(__dirname, './templates/description.art'), { description, images }); + +module.exports = async (ctx) => { + const { keyword } = ctx.params; + const url = `https://www.sogou.com/web?query=${encodeURIComponent(keyword)}`; + const key = `sogou-search:${url}`; + const items = await ctx.cache.tryGet( + key, + async () => { + const response = (await got(url)).data; + const $ = cheerio.load(response); + const result = $('#main'); + return result + .find('.vrwrap') + .map((i, el) => { + const element = $(el); + const imgs = element + .find('img') + .map((j, el2) => $(el2).attr('src')) + .toArray(); + const link = element.find('h3 a').first().attr('href'); + const title = element.find('h3').first().text(); + const description = element.find('.text-layout').first().text() || element.find('.space-txt').first().text() || element.find('[class^="translate"]').first().text(); + const author = element.find('.citeurl span').first().text() || ''; + const pubDate = parseDate(element.find('.citeurl .cite-date').first().text().trim()); + return { + link, + title, + description: renderDescription(description, imgs), + author, + pubDate, + }; + }) + .toArray() + .filter((e) => e?.link); + }, + config.cache.routeExpire, + false + ); + + ctx.state.data = { + title: `${keyword} - 搜狗搜索`, + description: `${keyword} - 搜狗搜索`, + link: url, + item: items, + }; +}; diff --git a/lib/v2/sogou/templates/description.art b/lib/v2/sogou/templates/description.art new file mode 100644 index 0000000000..5f98f4ca33 --- /dev/null +++ b/lib/v2/sogou/templates/description.art @@ -0,0 +1,6 @@ +{{@ description }} +{{if images}} + {{each images}} + + {{/each}} +{{/if}} diff --git a/website/docs/routes/other.mdx b/website/docs/routes/other.mdx index ebce0337e7..42aeebd693 100644 --- a/website/docs/routes/other.mdx +++ b/website/docs/routes/other.mdx @@ -91,6 +91,12 @@ See [#app-store-mac-app-store](/routes/program-update#app-store-mac-app-store) 2. Copy everything in the URL after `?`, for example: `https://www.autotrader.co.uk/car-search?radius=50&postcode=sw1a1aa&onesearchad=Used&onesearchad=Nearly%20New&onesearchad=New&price-to=9000&year-from=2012&body-type=Hatchback&transmission=Automatic&exclude-writeoff-categories=on` will produce `radius=50&postcode=sw1a1aa&onesearchad=Used&onesearchad=Nearly%20New&onesearchad=New&price-to=9000&year-from=2012&body-type=Hatchback&transmission=Automatic&exclude-writeoff-categories=on` +## Bing {#bing} + +### 搜索 {#bing-sou-suo} + + + ## BOOKWALKERTW {#bookwalkertw} ### 热门新书 {#bookwalkertw-re-men-xin-shu} @@ -181,6 +187,10 @@ Official Website: [https://news.yahoo.co.jp/pages/article/20200207](https://news +### Search {#google-search} + + + ## Grand-Challenge {#grand-challenge} ### Challenge 列表 {#grand-challenge-challenge-lie-biao} @@ -597,9 +607,13 @@ Specify options (in the format of query string) in parameter `routeParams` param -## 百度热搜 {#bai-du-re-sou} +## 百度 {#bai-du} -### 榜单 {#bai-du-re-sou-bang-dan} +### 搜索 {#bai-du-sou-suo} + + + +### 热搜榜单 {#bai-du-re-sou-bang-dan} | 热搜榜 | 小说榜 | 电影榜 | 电视剧榜 | 汽车榜 | 游戏榜 | @@ -851,10 +865,14 @@ Specify options (in the format of query string) in parameter `routeParams` param ## 搜狗 {#sou-gou} -### 搜狗特色 LOGO {#sou-gou-sou-gou-te-se-logo} +### 特色 LOGO {#sou-gou-te-se-logo} +### 搜索 {#sou-gou-sou-suo} + + + ## 台灣事實查核中心 {#tai-wan-shi-shi-cha-he-zhong-xin} ### 最新相關資訊 / 最新查核報告 {#tai-wan-shi-shi-cha-he-zhong-xin-zui-xin-xiang-guan-zi-xun-zui-xin-cha-he-bao-gao} diff --git a/website/docs/routes/picture.mdx b/website/docs/routes/picture.mdx index b86598e338..d0bb94d886 100644 --- a/website/docs/routes/picture.mdx +++ b/website/docs/routes/picture.mdx @@ -165,9 +165,9 @@ -## Bing Wallpaper {#bing-wallpaper} +## Bing {#bing} -### Daily Wallpaper {#bing-wallpaper-daily-wallpaper} +### Daily Wallpaper {#bing-daily-wallpaper}