2023-05-12 18:16:25 +09:00
|
|
|
import puppeteer, {Browser, Page, PuppeteerLaunchOptions} from "puppeteer";
|
|
|
|
import fs from 'fs';
|
|
|
|
import path from "path";
|
|
|
|
import run from "node:test";
|
2023-06-04 14:14:28 +09:00
|
|
|
import {v4} from "uuid";
|
2023-05-12 18:16:25 +09:00
|
|
|
|
|
|
|
const runPath = path.join(__dirname, 'run');
|
|
|
|
|
|
|
|
export class FreeBrowser {
|
|
|
|
private browser: Browser | undefined = undefined;
|
|
|
|
private readonly options: PuppeteerLaunchOptions | undefined;
|
|
|
|
private urls: Set<string> = new Set<string>();
|
|
|
|
private pages: Record<string, Page> = {};
|
2023-06-04 14:14:28 +09:00
|
|
|
public readonly id: string;
|
2023-05-12 18:16:25 +09:00
|
|
|
|
|
|
|
constructor(id: string, options?: PuppeteerLaunchOptions) {
|
|
|
|
this.options = {
|
2023-06-04 13:43:13 +09:00
|
|
|
// userDataDir: path.join(runPath, id),
|
2023-05-12 18:16:25 +09:00
|
|
|
...options
|
|
|
|
};
|
|
|
|
this.id = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async init() {
|
|
|
|
this.browser = await puppeteer.launch(this.options)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async getPage(url: string): Promise<Page> {
|
|
|
|
if (!this.browser) {
|
|
|
|
throw new Error('Browser must init first')
|
|
|
|
}
|
|
|
|
if (this.pages[url]) {
|
|
|
|
return this.pages[url];
|
|
|
|
}
|
|
|
|
const page = await this.browser.newPage();
|
|
|
|
await page.goto(url)
|
|
|
|
|
|
|
|
this.pages[url] = page;
|
|
|
|
return page;
|
|
|
|
}
|
2023-06-04 14:14:28 +09:00
|
|
|
|
|
|
|
public async close() {
|
|
|
|
this.browser?.close();
|
|
|
|
}
|
2023-05-12 18:16:25 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class FreeBrowserPool {
|
|
|
|
private size: number = 0;
|
|
|
|
private readonly pool: FreeBrowser[];
|
2023-06-04 14:14:28 +09:00
|
|
|
private debug: boolean = false;
|
2023-05-12 18:16:25 +09:00
|
|
|
|
|
|
|
constructor() {
|
|
|
|
this.pool = [];
|
|
|
|
}
|
|
|
|
|
2023-06-05 17:00:29 +09:00
|
|
|
public async init(size: number, debug: boolean) {
|
2023-06-04 14:14:28 +09:00
|
|
|
this.debug = debug;
|
2023-05-12 18:16:25 +09:00
|
|
|
console.log(`browser pool init size:${size}`)
|
|
|
|
if (!fs.existsSync(runPath)) {
|
|
|
|
fs.mkdirSync(runPath);
|
|
|
|
}
|
|
|
|
this.size = size;
|
|
|
|
for (let i = 0; i < size; i++) {
|
2023-06-04 14:14:28 +09:00
|
|
|
this.pool.push(await this.newBrowser());
|
2023-05-12 18:16:25 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public getRandom(): FreeBrowser {
|
|
|
|
return this.pool[Math.floor(Math.random() * this.pool.length)]
|
|
|
|
}
|
2023-06-04 14:14:28 +09:00
|
|
|
|
|
|
|
private async newBrowser(): Promise<FreeBrowser> {
|
|
|
|
const options: PuppeteerLaunchOptions = {
|
2023-06-05 12:08:47 +09:00
|
|
|
headless: this.debug ? false : 'new',
|
2023-06-05 17:00:29 +09:00
|
|
|
args: ['--no-sandbox']
|
2023-06-04 14:14:28 +09:00
|
|
|
};
|
2023-06-05 17:00:29 +09:00
|
|
|
if (process.env.http_proxy) {
|
|
|
|
options.args?.push(`--proxy-server=${process.env.http_proxy}`);
|
|
|
|
}
|
2023-06-04 14:14:28 +09:00
|
|
|
const browser = new FreeBrowser(v4(), options);
|
|
|
|
await browser.init();
|
|
|
|
return browser;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async remove(id: string) {
|
|
|
|
let removed = false;
|
|
|
|
this.pool.filter(item => {
|
|
|
|
if (item.id === id) {
|
|
|
|
item.close();
|
|
|
|
removed = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
})
|
|
|
|
if (removed) {
|
|
|
|
this.pool.push(await this.newBrowser());
|
|
|
|
}
|
|
|
|
}
|
2023-05-12 18:16:25 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
export const freeBrowserPool = new FreeBrowserPool();
|