0

0

优化循环网页抓取:实现健壮的Fetch请求重试机制

碧海醫心

碧海醫心

发布时间:2025-11-10 19:50:01

|

757人浏览过

|

来源于php中文网

原创

优化循环网页抓取:实现健壮的Fetch请求重试机制

在批量循环抓取网页时,网络不稳定常导致`fetch`请求失败并中断整个过程。本文旨在提供一个实用的解决方案,通过构建一个带有重试机制的异步`fetch`函数,确保即使面对瞬时网络故障,也能自动尝试重新获取网页内容。该策略显著提升了数据抓取任务的健壮性和完成率,避免因偶发网络问题导致整体流程中断。

在Web开发中,尤其是在需要从多个URL抓取内容的应用场景下,例如遍历一个NodeList并对每个元素对应的URL发起fetch请求,网络的不稳定性是一个常见的挑战。一个简单的fetch调用在遇到网络问题(如连接超时、DNS解析失败等)时,会立即抛出错误,导致后续代码无法执行,进而中断整个循环过程。这对于需要处理大量请求且要求高成功率的任务来说,是不可接受的。

问题分析

考虑以下典型的网页抓取循环:

for (const el of NodeList) {
  const url = el.getAttribute('href');
  // 如果此处fetch失败,后续代码将不会执行,且循环可能中断
  const res = await fetch(url); 
  const html = await res.text();
  const parser = new DOMParser();
  const doc = parser.parseFromString(html, 'text/html');
  alert('parsed successfully');
}

这段代码在理想网络环境下工作良好。然而,一旦fetch(url)因网络问题未能获取响应,await fetch(url)这一行就会抛出异常,导致整个循环中断,或者至少当前迭代无法完成。为了增强程序的健壮性,我们需要一种机制来自动处理这类瞬时错误,即在请求失败时自动进行重试。

实现Fetch请求重试机制

解决上述问题的核心在于引入一个重试机制。我们可以封装一个异步函数,该函数在fetch失败时捕获错误,并根据预设的重试次数再次尝试请求。

核心重试函数:fetchWithRetry

我们将创建一个名为fetchWithRetry的异步函数,它接受目标URL和最大重试次数作为参数。

ReRoom AI
ReRoom AI

专为室内设计打造的AI渲染工具,可以将模型图、平面图、草图、照片转换为高质量设计效果图。

下载
/**
 * 带有重试机制的异步Fetch函数
 * @param {string} url - 需要请求的URL
 * @param {number} numberOfRetries - 最大重试次数
 * @returns {Promise} - 解析后的DOM文档对象
 */
async function fetchWithRetry(url, numberOfRetries) {
  try {
    const response = await fetch(url);
    // 检查HTTP状态码,确保请求成功(例如2xx范围)
    if (!response.ok) {
        // 如果HTTP状态码表示失败,也视为错误并重试
        throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const html = await response.text();
    const parser = new DOMParser();
    const doc = parser.parseFromString(html, 'text/html');
    console.log(`Successfully parsed: ${url}`); // 使用console.log代替alert
    return doc;
  } catch (error) {
    if (numberOfRetries > 0) {
      console.warn(`Error fetching ${url}. Retrying... Attempts left: ${numberOfRetries - 1}`, error.message);
      // 递归调用自身进行重试,并递减重试次数
      // 可以选择在此处添加一个延迟,例如 await new Promise(resolve => setTimeout(resolve, 1000));
      return fetchWithRetry(url, numberOfRetries - 1);
    } else {
      console.error(`Error fetching ${url}. Maximum retries exceeded.`, error);
      // 重试次数用尽后,抛出原始错误,以便上层调用者处理
      throw error;
    }
  }
}

函数解析:

  1. try...catch块: 这是错误处理的核心。try块包含正常的fetch和解析逻辑。
  2. response.ok检查: 除了网络错误,HTTP状态码(如404, 500)也可能表示请求失败。response.ok属性(true表示状态码在200-299之间)可以帮助我们捕获这类逻辑错误,并触发重试。
  3. 递归重试: 在catch块中,如果numberOfRetries大于0,表示还有重试机会。函数会打印警告信息,然后递归调用自身,并将numberOfRetries减1。
  4. 重试次数限制: 当numberOfRetries减至0时,表示已达到最大重试次数。此时,函数将不再重试,而是抛出错误,让上层调用者知晓请求最终失败。
  5. 延迟重试(可选但推荐): 在实际应用中,为了避免对服务器造成过大压力,并在网络问题可能需要时间恢复的情况下,通常会在重试前引入一个短时间的延迟(例如,使用setTimeout)。更高级的策略是实现指数退避(Exponential Backoff),即每次重试的延迟时间逐渐增加。

集成到现有循环中

现在,我们可以将原始循环中的fetch调用替换为fetchWithRetry:

async function processNodeList(NodeList) {
  for (const el of NodeList) {
    const url = el.getAttribute('href');
    try {
      // 调用带有重试机制的函数,例如最多重试3次
      const doc = await fetchWithRetry(url, 3); 
      // 在这里处理成功解析的文档
      console.log(`Processed URL: ${url}`);
      // 示例:获取标题
      const title = doc.querySelector('title')?.textContent || 'No Title';
      console.log(`Title: ${title}`);
    } catch (error) {
      // 捕获fetchWithRetry最终抛出的错误,处理所有重试失败的情况
      console.error(`Failed to process URL after multiple retries: ${url}`, error);
      // 可以记录日志,或者将失败的URL添加到列表中稍后处理
    }
  }
}

// 假设NodeList已经定义并填充
// processNodeList(myNodeList); 

集成说明:

  1. 外部循环也应包含try...catch块,以捕获fetchWithRetry在所有重试失败后最终抛出的错误。这确保了即使某个URL最终无法获取,也不会中断整个NodeList的处理过程。
  2. fetchWithRetry的第二个参数3表示每个URL最多会尝试请求4次(1次初始请求 + 3次重试)。

注意事项与最佳实践

  1. 设置合理的重试次数: 过多的重试可能会导致程序长时间阻塞或对目标服务器造成不必要的负担。根据应用场景和预期的网络稳定性,选择一个合适的重试次数。
  2. 指数退避(Exponential Backoff): 为了更优雅地处理网络拥塞,并避免“雷鸣峡谷”效应(Thundering Herd),建议在每次重试之间增加一个逐渐增长的延迟。例如,第一次重试等待1秒,第二次等待2秒,第三次等待4秒。
    async function fetchWithRetryWithBackoff(url, numberOfRetries, delay = 1000) {
      try {
        const response = await fetch(url);
        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }
        const html = await response.text();
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');
        console.log(`Successfully parsed: ${url}`);
        return doc;
      } catch (error) {
        if (numberOfRetries > 0) {
          console.warn(`Error fetching ${url}. Retrying in ${delay / 1000}s... Attempts left: ${numberOfRetries - 1}`, error.message);
          await new Promise(resolve => setTimeout(resolve, delay)); // 引入延迟
          return fetchWithRetryWithBackoff(url, numberOfRetries - 1, delay * 2); // 延迟加倍
        } else {
          console.error(`Error fetching ${url}. Maximum retries exceeded.`, error);
          throw error;
        }
      }
    }
    // 使用示例
    // const doc = await fetchWithRetryWithBackoff(url, 3, 500); // 初始延迟0.5秒
  3. 区分错误类型: 并非所有错误都适合重试。例如,404 Not Found或401 Unauthorized通常表示资源不存在或权限不足,重试是无意义的。可以根据error对象的类型或response.status来决定是否重试。
  4. 日志记录: 详细的日志记录对于调试和监控至关重要。记录每次重试尝试、成功和失败的信息,有助于分析问题。
  5. 并发控制: 如果需要同时处理大量URL,直接使用for...of循环结合await会导致请求串行执行。为了提高效率,可以考虑使用Promise.all或Promise.allSettled来并发处理请求,但需要注意控制并发数量,避免对服务器造成过大压力或超出浏览器/Node.js的并发限制。

总结

通过实现一个带有重试机制的fetch函数,我们能够显著提高批量网页抓取任务的鲁棒性。这种方法使得应用程序能够优雅地处理瞬时网络故障,减少因偶发问题导致的数据丢失或流程中断。结合适当的重试策略(如指数退避)和错误处理,可以构建出更加稳定和高效的网络数据抓取应用。

相关专题

更多
scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

188

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

291

2023.10.25

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

510

2023.06.20

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

244

2023.07.28

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

278

2023.08.03

js是什么意思
js是什么意思

JS是JavaScript的缩写,它是一种广泛应用于网页开发的脚本语言。JavaScript是一种解释性的、基于对象和事件驱动的编程语言,通常用于为网页增加交互性和动态性。它可以在网页上实现复杂的功能和效果,如表单验证、页面元素操作、动画效果、数据交互等。

5298

2023.08.17

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

479

2023.09.01

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

212

2023.09.04

c++空格相关教程合集
c++空格相关教程合集

本专题整合了c++空格相关教程,阅读专题下面的文章了解更多详细内容。

0

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3万人学习

AngularJS教程
AngularJS教程

共24课时 | 2.9万人学习

CSS教程
CSS教程

共754课时 | 23.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号