如何实现复杂网页结构的递归DOM爬取与内容解析?
递归爬取复杂网页DOM的实现思路
先理清楚你要解决的两个核心点:一是静态链接的递归遍历,二是像angular.html这类依赖前端渲染的动态页面的DOM解析,还要避免重复爬取相同链接。下面是具体的实现思路和代码示例:
一、基础静态页面递归爬取框架
先完善你现有的链接收集逻辑,加上递归和去重机制:
// 用Set存储已爬取链接,快速去重 const crawledLinks = new Set(); // 限定爬取范围的基础URI白名单,避免爬到外部网站 const allowedBaseURIs = ['https://website.com/page/', 'https://website.com/page/1']; // 单页面爬取+DOM处理函数 async function crawlPage(url) { // 跳过已爬取或不在白名单内的链接 if (crawledLinks.has(url) || !isAllowedUrl(url)) return; crawledLinks.add(url); console.log(`正在爬取: ${url}`); try { // 1. 获取页面静态HTML并解析为DOM const response = await fetch(url); const html = await response.text(); const domParser = new DOMParser(); const pageDoc = domParser.parseFromString(html, 'text/html'); // 2. 打印DOM内容(按需调整,比如只打印特定区域或整个body) console.log(`页面${url}的DOM内容:\n`, pageDoc.body.innerHTML); // 3. 收集当前页面所有链接,递归爬取 const allLinks = Array.from(pageDoc.querySelectorAll('a[href]')).map(link => link.href); for (const link of allLinks) { await crawlPage(link); } } catch (error) { console.error(`爬取${url}失败:`, error); } } // 判断链接是否在允许的爬取范围内 function isAllowedUrl(url) { return allowedBaseURIs.some(base => url.startsWith(base)); } // 启动爬取,传入初始入口链接 crawlPage('https://website.com/page/');
二、处理动态加载页面(如angular.html)
上面的fetch只能拿到静态HTML,没法处理Angular渲染的动态内容,得用模拟浏览器加载的方式:
如果是在浏览器控制台或油猴脚本里运行,可以用iframe加载页面,等渲染完成后再解析DOM:
async function crawlDynamicPage(url) { if (crawledLinks.has(url) || !isAllowedUrl(url)) return; crawledLinks.add(url); console.log(`正在爬取动态页面: ${url}`); return new Promise((resolve, reject) => { const iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = url; iframe.onload = async () => { try { // 等待页面内的JS渲染完成(根据页面实际情况调整等待时间,或监听特定元素) await new Promise(resolve => setTimeout(resolve, 2000)); // 获取iframe里的完整DOM const pageDoc = iframe.contentDocument || iframe.contentWindow.document; console.log(`动态页面${url}的DOM内容:\n`, pageDoc.body.innerHTML); // 收集链接并递归爬取 const allLinks = Array.from(pageDoc.querySelectorAll('a[href]')).map(link => link.href); for (const link of allLinks) { await crawlDynamicPage(link); } document.body.removeChild(iframe); resolve(); } catch (error) { reject(error); } }; iframe.onerror = (error) => { console.error(`加载动态页面${url}失败:`, error); document.body.removeChild(iframe); reject(error); }; document.body.appendChild(iframe); }); } // 启动动态页面爬取 crawlDynamicPage('https://website.com/page/1/angular.html');
三、必须注意的几个点
- 跨域问题:浏览器的同源策略会阻止你加载不同域名的页面,如果目标网站没开跨域权限,建议用Node.js配合Puppeteer(无头浏览器)来爬取,完全规避跨域限制。
- 爬取频率控制:递归爬取太频繁容易被服务器拦截,建议在每次递归调用前加个延迟,比如
await new Promise(resolve => setTimeout(resolve, 1000)),降低请求速率。 - 动态渲染等待:如果页面加载时间不确定,不要硬写等待时间,可以用
MutationObserver监听页面内特定元素的出现,确认渲染完成后再解析DOM。
内容的提问来源于stack exchange,提问作者Paul9002
相关产品推荐
相关产品推荐

