首先说一下这是我自己封装的一个puppteer的浏览器实例池,代码如下
import puppeteer from "puppeteer";
import {EventEmitter} from 'events';
import {isImageOrVideo} from "../utils/utils.js";
import {config as browserPoolConfig} from "../config/spider.js";
const {headless, poolSize} = browserPoolConfig;
EventEmitter.setMaxListeners(0);
const queue = [];
for (let i = 0; i < poolSize; i++) {
const browser = await puppeteer.launch({headless});
queue.push(browser);
}
/**
* 获取一个新的浏览器实例
* @returns {Promise<Browser|*>}
*/
const getBrowser = async () => {
if (queue.length === 0) {
return await puppeteer.launch({headless});
}
let res = queue.shift();
return res;
};
/**
* 获取浏览器的网页并阻止该网页内的图片和视频请求这样可以防止超时问题
* @param browser
* @returns {Promise<*>}
*/
const getBrowserPageWithoutPicAndVideo = async (browser) => {
// 获取page
let page = await browser.newPage();
await page.setRequestInterception(true);
page.on('request', (request) => {
const url = request.url();
if (isImageOrVideo(url)) {
request.abort();
} else {
request.continue();
}
});
return page;
};
/**
* 归还浏览器重新返回内存中,方便复用
* @param browser
*/
const backBrowser = async (browser) => {
let pages = await browser.pages();
// 要留一个不然browser就关闭了 后面newPage就卡死了 之前写的是0
if (pages.length > 1) {
// 关闭页面
for (let i = 1; i < pages.length; i++) {
let page = pages[i];
await page.close();
}
}
if (queue.length >= poolSize) {
await browser.close();
return;
}
queue.push(browser);
};
/**
* 销毁原来的出错的浏览器实例并创建一个新的放入缓存中
* @param browser
* @returns {Promise<void>}
*/
const browserUpdate = async (browser) => {
await browser.close();
if (queue.length >= poolSize) {
return;
}
const browserNew = await puppeteer.launch({headless});
queue.push(browserNew);
};
export default {
getBrowser,
backBrowser,
browserUpdate,
getBrowserPageWithoutPicAndVideo
};
截图看一下bug是什么。
ProtocolError: Target.createTarget timed out. Increase the 'protocolTimeout' setting in launch/connect calls for a higher timeout if needed.
file:///C:/Users/WuFeng/Desktop/project/gupiaoTips/Server/node_modules/.pnpm/puppeteer-core@21.7.0/node_modules/puppeteer-core/lib/esm/puppeteer/common/CallbackRegistry.js:72
this._reject(callback, new TargetCloseError('Target closed'));
^
TargetCloseError: Protocol error (Target.createTarget): Target closed
上面两个问题都是同一个地方产生的。
然后我的调试最后发现是getBrowserPageWithoutPicAndVideo函数里面的browser.newPage()这个函数卡死在这里了,然后导致超时了。
后面分析查看打印日志都发现不了bug,然后newPage函数的前后打印,browser的数据的时候想到打印一下pages。最后发现在browser实例生成的时候就会自带一个page,所以想到在归还browser实例的时候是不是不能全部清空pages,后面测试发现就是这个问题,各种地方搜索挺久没发现这个答案,对于不明白原理的我和朋友确实挺难发现的。