Nodejs解析網(wǎng)站網(wǎng)址內(nèi)容并獲取標(biāo)題圖標(biāo)
介紹
在很多應(yīng)用場景中,我們需要從一個網(wǎng)頁中提取信息,比如標(biāo)題(title)、網(wǎng)站圖標(biāo)(favicon)以及簡介(description)。這些信息常用于以下場景:
分享功能:當(dāng)用戶在社交平臺分享鏈接時,展示鏈接的標(biāo)題、縮略圖和描述內(nèi)容。
數(shù)據(jù)抓?。河糜诜治鼍W(wǎng)頁信息,生成報告或構(gòu)建爬蟲應(yīng)用。
預(yù)覽功能:為用戶提供鏈接的簡要信息,提升交互體驗。
在Node.js中,可以借助cheerio庫高效地解析和提取HTML內(nèi)容。cheerio類似于jQuery的API,讓我們可以方便地操作HTML文檔,而無需啟動瀏覽器環(huán)境(如Puppeteer)。
代碼實現(xiàn)
異步獲取指定URL的內(nèi)容
代碼定義了一個異步函數(shù) fetchUrlContent,用于獲取指定 URL 的內(nèi)容。主要功能如下:
- 發(fā)送 HEAD 請求:首先發(fā)送一個 HEAD 請求來獲取響應(yīng)頭信息,檢查內(nèi)容長度是否超過限制。
- 檢查內(nèi)容長度:如果內(nèi)容長度超過限制,記錄日志并返回錯誤。
- 檢查內(nèi)容類型:如果內(nèi)容類型是 HTML,則發(fā)送 GET 請求獲取實際內(nèi)容。
- 再次檢查內(nèi)容長度:在獲取到實際內(nèi)容后,再次檢查內(nèi)容長度是否超過限制。
- 記錄日志并返回結(jié)果:如果一切正常,記錄日志并返回內(nèi)容;否則記錄錯誤并拋出異常。
/** * 異步獲取指定URL的內(nèi)容 * 該函數(shù)首先發(fā)送一個HTTP HEAD請求,以檢查URL的內(nèi)容類型和大小 * 如果內(nèi)容類型為HTML且大小在允許范圍內(nèi),則進一步發(fā)送GET請求獲取實際內(nèi)容 * * @param url 目標(biāo)URL地址 * @returns Promise對象,解析后返回URL的內(nèi)容,如果發(fā)生錯誤則拒絕Promise */ export async function fetchUrlContent(url: string) { return axios .head(url, { validateStatus: () => true, maxContentLength: configs.FETCH_URL_INFO.MAX_RESPONSE_SIZE, headers: { 'Content-Type': 'charset:utf-8', Accept: 'application/json, text/plain, */*', 'accept-encoding': 'gzip, deflate, br' }, timeout: configs.FETCH_URL_INFO.TIMEOUT }) .then((res) => { // 檢查內(nèi)容大小是否超出限制 if (res?.headers?.['content-length'] && parseInt(res?.headers['content-length']) > configs.FETCH_URL_INFO.MAX_RESPONSE_SIZE) { logger.log('[url] 限制:', url, res?.headers['content-length'], res?.headers['content-type']); return Promise.reject(new CustomError('URL_CONTENT_ERROR', '不支持該url內(nèi)容解析')); } // 檢查內(nèi)容類型是否為HTML if (res?.headers['content-type']?.includes('text/html')) { return axios .get(url, { headers: { accept: 'text/html', 'Content-Type': 'text/html;charset:utf-8', 'User-Agent': configs.FETCH_URL_INFO.USER_AGENT }, timeout: configs.FETCH_URL_INFO.TIMEOUT }) .then((res) => { if (res) { logger.log('[url] 爬取成功 axios', url); // 再次檢查內(nèi)容大小是否超出限制 if (res.data?.length > configs.FETCH_URL_INFO.MAX_RESPONSE_SIZE) { logger.log('[url] buffer大小: ', url, res.data?.length); return Promise.reject(new CustomError('URL_CONTENT_ERROR', '內(nèi)容過大解析失敗')); } return res.data; } return Promise.reject(res); }) .catch((e) => { logger.error('[url] fetch get', url, e.message); throw new CustomError('URL_GET_FETCH_ERROR', '不支持該url內(nèi)容解析'); }); } return Promise.reject(new CustomError('URL_UNVALID_ERROR', '不支持該url內(nèi)容解析')); }) .catch((e) => { logger.error('[url] fetch head', url, e.message); throw new CustomError('URL_HEAD_FETCH_ERROR', '不支持該url內(nèi)容解析'); }); }
解析網(wǎng)址內(nèi)容
具體實現(xiàn)
/** * 解析URL內(nèi)容 * @param url 頁面URL * @param html 頁面HTML內(nèi)容 * @returns 返回包含URL、圖標(biāo)、簡介和標(biāo)題的對象 */ export async function parseUrlContent(url: string, html: string): Promise<{ url: string; icon: string; intro: string; title: string }> { const $ = load(html); let title = ''; let intro = ''; let icon = ''; // 獲取標(biāo)題節(jié)點 const titleEl = $('title'); if (titleEl?.text()) { title = titleEl?.text(); } // 獲取icon const linkEl = $('link'); const links: string[] = []; if (linkEl) { linkEl.each((_i, el) => { const rel = $(el).attr('rel'); const href = $(el).attr('href'); if (rel?.includes('icon') && href) { links.push(href); } }); } logger.log('[url] 獲取icon', links); if (links.length) { icon = resolveUrl(url, links[0]); } /** * 獲取meta屬性 * @param metaElement * @param name * @returns */ const getPropertyContent = (Element, name: string) => { const propertyName = $(Element)?.attr('property') || $(Element)?.attr('name'); return propertyName === name ? $(Element)?.attr('content') || '' : ''; }; // 獲取詳情 const metas = $('meta'); for (const meta of metas) { if (title && intro) { break; } // 如果沒有標(biāo)題 if (!title) { const titleoAttr = ['og:title', 'twitter:title']; for (const attr of titleoAttr) { const text = getPropertyContent(meta, attr); if (text) { title = text; break; } } } // 簡介 if (!intro) { const introAttr = ['description', 'og:description', 'twitter:description']; for (const attr of introAttr) { const description = getPropertyContent(meta, attr); if (description) { intro = description; break; } } } // icon if (!icon) { const imageAttr = ['og:image', 'twitter:image']; for (const attr of imageAttr) { const image = getPropertyContent(meta, attr); if (image) { intro = resolveUrl(url, image); break; } } } } // 沒有簡介提取全部 if (!intro) { const body = $('body').html(); intro = body ? htmlStringReplace(body, configs.FETCH_URL_INFO.MAX_INTRO_LENGTH) : ''; } logger.log('[url] 爬取結(jié)果', { url, title, intro, icon }); return { url, title: title?.trim() || '', intro: intro?.trim() || '', icon }; }
代碼解釋
這段 TypeScript 代碼定義了一個異步函數(shù) parseUrlContent,用于解析 HTML 內(nèi)容并提取 URL 的標(biāo)題、圖標(biāo)、簡介和原始 URL。具體步驟如下:
- 加載 HTML:使用 load 函數(shù)加載傳入的 HTML 字符串。
- 獲取標(biāo)題:從 <title> 標(biāo)簽中提取頁面標(biāo)題。
- 獲取圖標(biāo):從 <link> 標(biāo)簽中提取 favicon 圖標(biāo)。
- 獲取元數(shù)據(jù):定義一個輔助函數(shù) getPropertyContent 用于從 <meta> 標(biāo)簽中提取特定屬性的內(nèi)容。
- 提取詳情:從 <meta> 標(biāo)簽中提取標(biāo)題、簡介和圖標(biāo)。
- 處理簡介:如果沒有提取到簡介,則從 <body> 中提取部分內(nèi)容作為簡介。
開發(fā)API的用法
// js演示 var axios = require('axios'); var data = JSON.stringify({ url: 'https://xygeng.cn/post/200' }); // 注意只支持post請求 var config = { method: 'post', url: 'https://api.xygeng.cn/openapi/url/info', headers: { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', 'Content-Type': 'application/json', Accept: '*/*', Connection: 'keep-alive' }, data: data }; axios(config) .then(function (response) { console.log(JSON.stringify(response.data)); }) .catch(function (error) { console.log(error); });
到此這篇關(guān)于Nodejs解析網(wǎng)站網(wǎng)址內(nèi)容并獲取標(biāo)題圖標(biāo)的文章就介紹到這了,更多相關(guān)Nodejs解析網(wǎng)站網(wǎng)址內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
webpack打包、編譯、熱更新Node內(nèi)存不足問題解決
Webpack是現(xiàn)在主流的功能強大的模塊化打包工具,在使用Webpack時,如果不注意性能優(yōu)化,有非常大的可能會產(chǎn)生性能問題,下面這篇文章主要給大家介紹了關(guān)于webpack打包、編譯、熱更新Node內(nèi)存不足問題解決的相關(guān)資料,需要的朋友可以參考下2023-03-03詳解如何在NodeJS應(yīng)用程序中處理多個API請求
NodeJS默認(rèn)是異步的,這意味著它已經(jīng)能夠同時處理多個請求,但它只適用于I/O操作,如HTTP請求、文件系統(tǒng)操作、數(shù)據(jù)庫查詢、實時聊天應(yīng)用等,在處理CPU密集型任務(wù)時,可能需要很長時間,這就是為什么NodeJS提供了一些我們將在下面介紹的特定包2023-12-12了不起的node.js讀書筆記之node的學(xué)習(xí)總結(jié)
這篇文章主要介紹了了不起的node.js讀書筆記之node的學(xué)習(xí)總結(jié),需要的朋友可以參考下2014-12-12nodejs個人博客開發(fā)第六步 數(shù)據(jù)分頁
這篇文章主要為大家詳細(xì)介紹了nodejs個人博客開發(fā)的數(shù)據(jù)分頁,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04node使用promise替代回調(diào)函數(shù)
這篇文章主要介紹了node使用promise替代回調(diào)函數(shù),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05