File size: 5,377 Bytes
827ba75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
494adc9
 
 
827ba75
 
 
 
 
 
 
494adc9
 
 
 
827ba75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0ab6052
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""
Chrome Wrapper for Pinterest Automation
Provides easy access to portable Chrome setup for the automation script
"""

import json
import os
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service

class PortableChromeManager:
    def __init__(self):
        self.root_dir = Path(__file__).parent
        self.config_path = self.root_dir / "chrome_config.json"
        self.config = self._load_config()
    
    def _load_config(self):
        """Load Chrome configuration"""
        if not self.config_path.exists():
            raise FileNotFoundError(
                "Chrome configuration not found. Chrome setup is missing."
            )
        
        with open(self.config_path) as f:
            config = json.load(f)
        
        # Convert relative paths to absolute paths
        chrome_binary = self.root_dir / config["chrome_binary"]
        chromedriver_path = self.root_dir / config["chromedriver_path"]
        
        if not chrome_binary.exists():
            raise FileNotFoundError(f"Chrome binary not found: {chrome_binary}")
        
        if not chromedriver_path.exists():
            raise FileNotFoundError(f"ChromeDriver not found: {chromedriver_path}")
        
        # Update config with absolute paths
        config["chrome_binary"] = str(chrome_binary)
        config["chromedriver_path"] = str(chromedriver_path)
        
        return config
    
    def get_chrome_options(self, headless=False, additional_options=None):
        """Get Chrome options configured for portable Chrome"""
        options = Options()
        
        # Set the binary location
        options.binary_location = self.config["chrome_binary"]
        
        # Essential options for reliability
        options.add_argument('--no-sandbox')
        options.add_argument('--disable-dev-shm-usage')
        options.add_argument('--disable-gpu')
        options.add_argument('--window-size=1920,1080')
        options.add_argument('--start-maximized')
        
        # Additional options for containerized environments
        options.add_argument('--disable-software-rasterizer')
        options.add_argument('--disable-background-timer-throttling')
        options.add_argument('--disable-backgrounding-occluded-windows')
        options.add_argument('--disable-renderer-backgrounding')
        options.add_argument('--disable-features=TranslateUI')
        options.add_argument('--disable-ipc-flooding-protection')
        options.add_argument('--no-first-run')
        options.add_argument('--no-default-browser-check')
        options.add_argument('--disable-default-apps')
        options.add_argument('--remote-debugging-port=9222')
        options.add_argument('--disable-web-security')
        options.add_argument('--disable-features=VizDisplayCompositor')
        
        # Headless mode
        if headless:
            options.add_argument('--headless')
        
        # Anti-detection measures
        options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')
        options.add_argument('--disable-blink-features=AutomationControlled')
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        options.add_experimental_option('useAutomationExtension', False)
        
        # Performance optimizations
        options.add_argument('--disable-extensions')
        options.add_argument('--disable-plugins')
        options.add_argument('--disable-images')  # Faster loading
        
        # Additional options for cloud deployment
        options.add_argument('--disable-logging')
        options.add_argument('--disable-dev-tools')
        
        # Add any additional options
        if additional_options:
            for option in additional_options:
                options.add_argument(option)
        
        return options
    
    def get_chrome_service(self):
        """Get Chrome service configured for portable ChromeDriver"""
        return Service(executable_path=self.config["chromedriver_path"])
    
    def create_driver(self, headless=False, additional_options=None):
        """Create and return a configured Chrome WebDriver"""
        options = self.get_chrome_options(headless=headless, additional_options=additional_options)
        service = self.get_chrome_service()
        
        driver = webdriver.Chrome(service=service, options=options)
        
        # Remove automation indicators
        driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
        
        return driver
    
    def get_config_info(self):
        """Get information about the current Chrome setup"""
        return {
            "chrome_binary": self.config["chrome_binary"],
            "chromedriver_path": self.config["chromedriver_path"],
            "version": self.config["version"],
            "platform": self.config["platform"]
        }

# Global instance for easy import - use lazy initialization
chrome_manager = None

def get_chrome_manager():
    """Get or create the global chrome manager instance"""
    global chrome_manager
    if chrome_manager is None:
        chrome_manager = PortableChromeManager()
    return chrome_manager