| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 | const net = require('net')const CDP = require('chrome-remote-interface')const chromeFinder = require('chrome-finder')const {    exec,    spawn,} = require('child_process')const path = require('node:path')const os = require('node:os')class WebDriver {    constructor (params = {        args: [],        host: '127.0.0.1',        port: 0,        chromePath: '',        userDataDir: '',    }) {        this.args = params.args || []        this.host = params.host || '127.0.0.1'        this.port = params.port || 0        this.chromePath = params.chromePath || ''        this.userDataDir = params.userDataDir || ''    }    headless () {        this.args.push('--headless=old')    }    getCDP () {        return this.cdp    }    setProxy (proxy) {        this.args.push(`--proxy-server=${proxy}`)    }    addArguments (arg) {        if (arg) {            this.args.push(arg)        } else {            throw new Error('Arguments cannot be null')        }    }    setChromeBinaryPath (binaryPath) {        this.chromePath = binaryPath    }    setUserDataDirPath (binaryPath) {        this.userDataDir = binaryPath    }    async builder () {        try {            if (!this.port) {                this.port = await this.isPortTaken(this.getRandomNumber())            }            if (!this.chromePath) {                this.chromePath = chromeFinder()            }            if (!this.userDataDir) {                this.userDataDir = path.join(os.tmpdir(), `scoped_dir${new Date().getTime()}`)            }            console.info('启动端口:', this.port)            const command = `${this.chromePath}`            const args = [                `--user-data-dir=${this.userDataDir}`,                `--disk-cache-dir=${this.userDataDir}`,                `--remote-debugging-port=${this.port}`,                '--no-first-run',                '--no-dleck',                '--disable-plugins',                '--no-default-browser-check',                '--new-tab',                // 'data:,',            ]            const mergedArray = args.concat(this.args)            const { spawn } = require('child_process')            return new Promise((resolve, reject) => {                const childProcess = spawn(command, mergedArray)                childProcess.stderr.on('data', async (data) => {                    await this.connectToChrome(1, this.port)                    resolve(true)                })                childProcess.on('close', (code) => {                    console.info(`子进程退出成功,退出码: ${code}`)                })                childProcess.on('error', (err) => {                    console.error(`子进程错误:${err.message}`)                })            })        } catch (e) {            console.error(e)        }    }    createCDPConnection () {        return {            host: this.host,            port: this.port,        }    }    checkBrowser () {        return !!(this.chromePath || chromeFinder())    }    isPortTaken (port) {        return new Promise((resolve) => {            const server = net.createServer()            server.on('listening', () => {                server.close(() => {                    resolve(port)                })            })            server.on('error', async () => {                this.isPortTaken(port + 1).then(resolve)            })            server.listen(port, '127.0.0.1')        })    }    async connectToChrome (time, port) {        const waitingTime = {            1: 1000,            2: 3000,            3: 5000,            4: 10000,        }        if (!this.cdp) {            try {                this.cdp = await CDP({ port })                console.info('CDP 连接成功')            } catch (error) {                console.error('连接失败,正在重试...')                await new Promise(resolve => setTimeout(resolve, waitingTime[time]))                if (time < 4) {                    await this.connectToChrome(time + 1, port)                } else {                    await this.quit()                    await this.builder()                }            }        }    }    getRandomNumber () {        return Math.floor(Math.random() * (65535 - 10000 + 1)) + 10000    }    quit () {        const command = `netstat -ano | findstr LISTENING | findstr :${this.port}`        return new Promise((resolve, reject) => {            exec(command, (error, stdout, stderr) => {                if (error) {                    resolve(`执行命令时出错: ${error}`)                    return                }                const lines = stdout.split('\n')                const processIds = []                lines.forEach((line) => {                    const parts = line.trim().split(/\s+/)                    if (parts.length >= 2) {                        const pid = parts[parts.length - 1]                        processIds.push(pid)                    }                })                if (processIds.length === 0) {                    resolve(`未找到在端口 ${this.port} 上运行的进程`)                    return                }                if (processIds.length > 0) {                    processIds.forEach((pid) => {                        exec(`taskkill /F /PID ${pid}`, (error) => {                            if (error) {                                resolve(`关闭进程时出错: ${error}`)                            } else {                                resolve(this.port)                            }                        })                    })                }            })        })    }}module.exports = { WebDriver: WebDriver }
 |