undetected-chromedriver-js
Version:
Node.js wrapper for undetected-chromedriver with automatic setup and cross-platform support
262 lines (217 loc) • 7.5 kB
JavaScript
const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');
const os = require('os');
class SetupManager {
constructor() {
this.platform = os.platform();
this.setupPythonScript = path.join(__dirname, 'generate_executable.py');
this.venvPath = path.join(process.cwd(), '.venv');
this.pythonCmd = this.platform === 'win32' ? 'python' : 'python3';
this.pipCmd = this.platform === 'win32' ?
path.join(this.venvPath, 'Scripts', 'pip') :
path.join(this.venvPath, 'bin', 'pip');
this.venvPythonCmd = this.platform === 'win32' ?
path.join(this.venvPath, 'Scripts', 'python') :
path.join(this.venvPath, 'bin', 'python');
}
async checkPython() {
return new Promise((resolve) => {
const child = spawn(this.pythonCmd, ['--version']);
child.on('close', (code) => {
if (code === 0) {
console.log('✓ Python found');
resolve(true);
} else {
console.error('✗ Python not found. Please install Python first.');
resolve(false);
}
});
});
}
async createVirtualEnv() {
return new Promise((resolve) => {
console.log('Creating Python virtual environment...');
// Check if venv already exists
if (fs.existsSync(this.venvPath)) {
console.log('✓ Virtual environment already exists');
resolve(true);
return;
}
const child = spawn(this.pythonCmd, ['-m', 'venv', this.venvPath]);
child.stdout.on('data', (data) => {
process.stdout.write(data);
});
child.stderr.on('data', (data) => {
process.stderr.write(data);
});
child.on('close', (code) => {
if (code === 0) {
console.log('✓ Virtual environment created');
resolve(true);
} else {
console.error('✗ Failed to create virtual environment');
resolve(false);
}
});
});
}
async installSetuptools() {
return new Promise((resolve) => {
console.log('Installing setuptools for Python 3.13+ compatibility...');
const child = spawn(this.pipCmd, ['install', 'setuptools']);
child.stdout.on('data', (data) => {
process.stdout.write(data);
});
child.stderr.on('data', (data) => {
process.stderr.write(data);
});
child.on('close', (code) => {
if (code === 0) {
console.log('✓ setuptools installed');
resolve(true);
} else {
console.error('✗ Failed to install setuptools');
resolve(false);
}
});
});
}
async installUndetectedChromedriver() {
return new Promise((resolve) => {
console.log('Installing undetected-chromedriver...');
const child = spawn(this.pipCmd, ['install', 'undetected-chromedriver']);
child.stdout.on('data', (data) => {
process.stdout.write(data);
});
child.stderr.on('data', (data) => {
process.stderr.write(data);
});
child.on('close', (code) => {
if (code === 0) {
console.log('✓ undetected-chromedriver installed');
resolve(true);
} else {
console.error('✗ Failed to install undetected-chromedriver');
resolve(false);
}
});
});
}
createPythonScript() {
const pythonCode = `
# -*- coding: utf-8 -*-
import undetected_chromedriver as uc
from multiprocessing import freeze_support
import os
import sys
import platform
def find_chrome_binary():
"""Find Chrome binary on Windows"""
if platform.system() == "Windows":
possible_paths = [
r"C:\Program Files\Google\Chrome\Application\chrome.exe",
r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
os.path.expanduser(r"~\AppData\Local\Google\Chrome\Application\chrome.exe")
]
for path in possible_paths:
if os.path.exists(path):
return path
return None
if __name__ == '__main__':
try:
freeze_support()
print("Creating undetected ChromeDriver executable...")
# Find Chrome binary on Windows
chrome_binary = find_chrome_binary()
if chrome_binary:
print(f"Using Chrome at: {chrome_binary}")
# Create a temporary driver instance with Chrome path
driver = uc.Chrome(browser_executable_path=chrome_binary, headless=True, use_subprocess=False)
else:
print("Chrome not found, using default...")
# Try without specifying Chrome path
driver = uc.Chrome(headless=True, use_subprocess=False)
# Get the executable path
executable_path = driver.service.path
print(f"Executable created at: {executable_path}")
# Close the driver
driver.quit()
# Write the path to a file for Node.js to read
with open('chromedriver_path.txt', 'w', encoding='utf-8') as f:
f.write(executable_path)
print("Setup completed successfully!")
except Exception as e:
print(f"Error during setup: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
`;
fs.writeFileSync(this.setupPythonScript, pythonCode);
}
async generateExecutable() {
return new Promise((resolve) => {
console.log('Generating undetected ChromeDriver executable...');
this.createPythonScript();
// Set environment for proper UTF-8 encoding on Windows
const env = { ...process.env };
if (this.platform === 'win32') {
env.PYTHONIOENCODING = 'utf-8';
env.PYTHONUTF8 = '1';
}
const child = spawn(this.venvPythonCmd, [this.setupPythonScript], {
cwd: process.cwd(),
env: env
});
child.stdout.on('data', (data) => {
process.stdout.write(data);
});
child.stderr.on('data', (data) => {
process.stderr.write(data);
});
child.on('close', (code) => {
// Clean up temporary Python script
if (fs.existsSync(this.setupPythonScript)) {
fs.unlinkSync(this.setupPythonScript);
}
if (code === 0) {
console.log('✓ Executable generated successfully');
resolve(true);
} else {
console.error('✗ Failed to generate executable');
resolve(false);
}
});
});
}
async run() {
console.log('🔧 Setting up undetected-chromedriver-js...\n');
const pythonOk = await this.checkPython();
if (!pythonOk) {
process.exit(1);
}
const venvOk = await this.createVirtualEnv();
if (!venvOk) {
process.exit(1);
}
const setuptoolsOk = await this.installSetuptools();
if (!setuptoolsOk) {
process.exit(1);
}
const installOk = await this.installUndetectedChromedriver();
if (!installOk) {
process.exit(1);
}
const executableOk = await this.generateExecutable();
if (!executableOk) {
process.exit(1);
}
console.log('\n🎉 Setup completed! You can now use undetected-chromedriver-js');
}
}
// Run setup if this script is executed directly
if (require.main === module) {
const setup = new SetupManager();
setup.run().catch(console.error);
}
module.exports = SetupManager;