Nodejs解析网站网址内容并获取标题图标

 更新时间:2024年11月26日 09:39:15   作者: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解析网站网址内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 独立部署小程序基于nodejs的服务器过程详解

    独立部署小程序基于nodejs的服务器过程详解

    这篇文章主要介绍了独立部署小程序基于nodejs的服务器过程详解,完全自定义的部署小程序服务器, 不依托于腾讯云服务器体系. 以阿里云服务器为基础建立.服务器语言选用nodejs.,需要的朋友可以参考下
    2019-06-06
  • 使用node打造自己的命令行工具方法教程

    使用node打造自己的命令行工具方法教程

    这篇文章主要介绍了使用node打造自己的命令行工具方法教程,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • 用Nodejs实现在终端中炒股的实现

    用Nodejs实现在终端中炒股的实现

    这篇文章主要介绍了用Nodejs实现在终端中炒股的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • 详解Node.js模板引擎Jade入门

    详解Node.js模板引擎Jade入门

    这篇文章主要介绍了详解Node.js模板引擎Jade入门,Jade是Node.js的一个模板引擎,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • 浅谈Node异步编程的机制

    浅谈Node异步编程的机制

    本篇文章主要介绍了浅谈Node异步编程的机制,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • 使用NODE.JS创建一个WEBSERVER(服务器)的步骤

    使用NODE.JS创建一个WEBSERVER(服务器)的步骤

    在 node.js 中创建一个服务器非常简单,只需要使用 node.js 为我们提供的 http 模块及相关 API 即可创建一个麻雀虽小但五脏俱全的web 服务器,相比 Java/Python/Ruby 搭建web服务器的过程简单的很。本文简单的讲解下实现步骤
    2021-06-06
  • 把Node.js程序加入服务实现随机启动

    把Node.js程序加入服务实现随机启动

    这篇文章主要介绍了把Node.js程序加入服务实现随机启动,本文使用qckwinsvc实现这个需求,讲解了qckwinsvc的安装和使用,需要的朋友可以参考下
    2015-06-06
  • Node 升级到最新稳定版的方法分享

    Node 升级到最新稳定版的方法分享

    今天小编就为大家分享一篇Node 升级到最新稳定版的方法分享,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-05-05
  • 初识Node.js

    初识Node.js

    本文给大家介绍的是node.js的初体验,从最简单的内容开始,慢慢的深入分析node.js。并附上一则很不错的文章《7天学会nodeJS》更加详细的向我们展示了node.js的学习步骤。
    2015-03-03
  • Node.js中同步和异步编程的区别及使用方法

    Node.js中同步和异步编程的区别及使用方法

    在Node.js中,同步和异步编程是两种不同的处理方式。同步方式会阻塞程序的执行,而异步方式则不会。通过掌握它们的区别和使用方法,可以更好地实现程序的性能优化和功能扩展。同时,需要注意异步编程中的回调地狱问题,使用Promise可以更好地处理异步编程
    2023-05-05

最新评论