Nodejs解析网站网址内容并获取标题图标
作者:XY笔记
cheerio类似于jQuery的API,让我们可以方便地操作HTML文档,下面我们就来看看在Node.js中如何借助cheerio库高效地解析和提取HTML内容吧
介绍
在很多应用场景中,我们需要从一个网页中提取信息,比如标题(title)、网站图标(favicon)以及简介(description)。这些信息常用于以下场景:
分享功能:当用户在社交平台分享链接时,展示链接的标题、缩略图和描述内容。
数据抓取:用于分析网页信息,生成报告或构建爬虫应用。
预览功能:为用户提供链接的简要信息,提升交互体验。
在Node.js中,可以借助cheerio库高效地解析和提取HTML内容。cheerio类似于jQuery的API,让我们可以方便地操作HTML文档,而无需启动浏览器环境(如Puppeteer)。
代码实现
异步获取指定URL的内容
代码定义了一个异步函数 fetchUrlContent,用于获取指定 URL 的内容。主要功能如下:
- 发送 HEAD 请求:首先发送一个 HEAD 请求来获取响应头信息,检查内容长度是否超过限制。
- 检查内容长度:如果内容长度超过限制,记录日志并返回错误。
- 检查内容类型:如果内容类型是 HTML,则发送 GET 请求获取实际内容。
- 再次检查内容长度:在获取到实际内容后,再次检查内容长度是否超过限制。
- 记录日志并返回结果:如果一切正常,记录日志并返回内容;否则记录错误并抛出异常。
/** * 异步获取指定URL的内容 * 该函数首先发送一个HTTP HEAD请求,以检查URL的内容类型和大小 * 如果内容类型为HTML且大小在允许范围内,则进一步发送GET请求获取实际内容 * * @param url 目标URL地址 * @returns Promise对象,解析后返回URL的内容,如果发生错误则拒绝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) => { // 检查内容大小是否超出限制 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内容解析')); } // 检查内容类型是否为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); // 再次检查内容大小是否超出限制 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', '内容过大解析失败')); } 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内容解析'); }); } return Promise.reject(new CustomError('URL_UNVALID_ERROR', '不支持该url内容解析')); }) .catch((e) => { logger.error('[url] fetch head', url, e.message); throw new CustomError('URL_HEAD_FETCH_ERROR', '不支持该url内容解析'); }); }
解析网址内容
具体实现
/** * 解析URL内容 * @param url 页面URL * @param html 页面HTML内容 * @returns 返回包含URL、图标、简介和标题的对象 */ 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 = ''; // 获取标题节点 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; } // 如果没有标题 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] 爬取结果', { url, title, intro, icon }); return { url, title: title?.trim() || '', intro: intro?.trim() || '', icon }; }
代码解释
这段 TypeScript 代码定义了一个异步函数 parseUrlContent,用于解析 HTML 内容并提取 URL 的标题、图标、简介和原始 URL。具体步骤如下:
- 加载 HTML:使用 load 函数加载传入的 HTML 字符串。
- 获取标题:从 <title> 标签中提取页面标题。
- 获取图标:从 <link> 标签中提取 favicon 图标。
- 获取元数据:定义一个辅助函数 getPropertyContent 用于从 <meta> 标签中提取特定属性的内容。
- 提取详情:从 <meta> 标签中提取标题、简介和图标。
- 处理简介:如果没有提取到简介,则从 <body> 中提取部分内容作为简介。
开发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); });
到此这篇关于Nodejs解析网站网址内容并获取标题图标的文章就介绍到这了,更多相关Nodejs解析网站网址内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!