使用Puppeteer批量更新重定向URL时迭代异常的解决方法
循环调用Puppeteer更新JSON链接时返回错误URL的解决思路
我有一个JSON数组,需要通过循环调用Puppeteer函数更新其中的链接,但迭代若干次后,Puppeteer函数无法正常工作,返回错误的URL。单独运行单个对象时,函数能正常获取正确URL,求可行的解决思路。
遍历更新的函数代码
async function iterateToUpdateLinks(jsonArray) { for (let i = 0; i < 5; i++) { jsonArray[i].link = await urlUpdate(jsonArray[i].link) } return jsonArray }
待处理的JSON数组
[ {"title": "abc", "link":"https://news.google.com/articles/CAIiEHxSZ7LBmHeDe2YaolWN_CAqFwgEKg8IACoHCAowjuuKAzCWrzwwt4QY?uo=CAUiSGh0dHBzOi8vd3d3Lm55dGltZXMuY29tLzIwMjIvMTAvMjMvbnlyZWdpb24vbWlncmFudHMtbnljLWVyaWMtYWRhbXMuaHRtbNIBAA&hl=en-US&gl=US&ceid=US%3Aen"}, {"title": "def", "link":"https://news.google.com/articles/CBMifmh0dHBzOi8vdHlsZXJwYXBlci5jb20vbGlmZXN0eWxlL2hlYWx0aC9hZ2UtbWF0dGVycy1mb3ItdXNlLW9mLWF0LWhvbWUtdGVzdHMvYXJ0aWNsZV85NzUwYTRjZS1iZTFhLTVkNTgtYmRlMC00OGU5MTk2NWIwMmIuaHRtbNIBggFodHRwczovL3R5bGVycGFwZXIuY29tL2xpZmVzdHlsZS9oZWFsdGgvYWdlLW1hdHRlcnMtZm9yLXVzZS1vZi1hdC1ob21lLXRlc3RzL2FydGljbGVfOTc1MGE0Y2UtYmUxYS01ZDU4LWJkZTAtNDhlOTE5NjViMDJiLmFtcC5odG1s?hl=en-US&gl=US&ceid=US%3Aen"}, {"title": "ghi", "link":"https://news.google.com/articles/CBMie2h0dHBzOi8vd3d3LmNvbW1lcmNpYWxhcHBlYWwuY29tL3N0b3J5L25ld3MvMjAyMi8xMC8yMy9zdGFkaXVtLXJlcXVlc3QtZm9yLW1lbXBoaXMtYS10ZXN0LW9mLXN0cmlja2xhbmRzLWJvbmRzLzY5NTcyMjgyMDA3L9IBAA?hl=en-US&gl=US&ceid=US%3Aen"} ]
当前使用的Puppeteer函数
async function urlUpdate(url) { const browser = await puppeteer.launch() const page = await browser.newPage() await page.setRequestInterception(true) page.setDefaultTimeout(6000) page.on('request', (request) => { // 获取重定向后的URL if (request.resourceType() === 'document' && request.url() !== url) { url = request.url() } request.continue() }) try { await page.goto(url,{ waitUntil: 'networkidle2'}) } finally { await browser.close() return url } }
解决思路
1. 修复变量作用域竞态问题
urlUpdate函数中,请求回调里直接修改入参url会导致作用域竞态——异步回调的修改可能赶不上函数返回逻辑,最终返回错误的地址。改用独立局部变量存储最终URL:
async function urlUpdate(originalUrl) { const browser = await puppeteer.launch() const page = await browser.newPage() let finalUrl = originalUrl await page.setRequestInterception(true) page.setDefaultTimeout(6000) page.on('request', (request) => { if (request.resourceType() === 'document' && request.url() !== finalUrl) { finalUrl = request.url() } request.continue() }) try { await page.goto(originalUrl,{ waitUntil: 'networkidle2'}) } finally { await browser.close() return finalUrl } }
2. 复用浏览器实例减少资源消耗
每次调用urlUpdate都启动新浏览器,会快速耗尽系统资源,导致后续迭代出现异常。复用一个浏览器实例,仅每次创建新页面:
// 提前创建全局浏览器实例 const browser = await puppeteer.launch() async function urlUpdate(originalUrl) { const page = await browser.newPage() let finalUrl = originalUrl await page.setRequestInterception(true) page.setDefaultTimeout(6000) page.on('request', (request) => { if (request.resourceType() === 'document' && request.url() !== finalUrl) { finalUrl = request.url() } request.continue() }) try { await page.goto(originalUrl,{ waitUntil: 'networkidle2'}) } finally { await page.close() // 仅关闭页面,保留浏览器 return finalUrl } } // 遍历完成后统一关闭浏览器 async function iterateToUpdateLinks(jsonArray) { // 改用数组长度遍历,避免固定值导致越界 for (let i = 0; i < jsonArray.length; i++) { jsonArray[i].link = await urlUpdate(jsonArray[i].link) } await browser.close() return jsonArray }
3. 增加异常捕获避免迭代中断
单个链接处理失败会导致整个循环终止,在遍历中加入异常捕获,保证后续链接正常处理:
async function iterateToUpdateLinks(jsonArray) { for (let i = 0; i < jsonArray.length; i++) { try { jsonArray[i].link = await urlUpdate(jsonArray[i].link) } catch (err) { console.error(`处理第${i}个链接失败:`, err) // 可选:保留原链接或标记错误状态 jsonArray[i].error = err.message } } await browser.close() return jsonArray }
4. 简化重定向URL获取逻辑
不需要通过请求拦截获取重定向地址,直接用page.url()获取当前页面最终地址,逻辑更稳定:
async function urlUpdate(originalUrl) { const browser = await puppeteer.launch() const page = await browser.newPage() page.setDefaultTimeout(10000) // 适当延长超时时间 try { await page.goto(originalUrl,{ waitUntil: 'networkidle2'}) return page.url() // 直接获取页面最终URL } finally { await browser.close() } }
内容的提问来源于stack exchange,提问作者Raphael Lima
相关产品推荐
相关产品推荐

