Josedcape commited on
Commit
5d1253a
·
verified ·
1 Parent(s): 2600208

Upload 3 files

Browse files
Files changed (2) hide show
  1. src/custom_browser.py +120 -0
  2. src/custom_context.py +88 -0
src/custom_browser.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import pdb
3
+
4
+ from playwright.async_api import Browser as PlaywrightBrowser
5
+ from playwright.async_api import (
6
+ BrowserContext as PlaywrightBrowserContext,
7
+ )
8
+ from playwright.async_api import (
9
+ Playwright,
10
+ async_playwright,
11
+ )
12
+ from browser_use.browser.browser import Browser
13
+ from browser_use.browser.context import BrowserContext, BrowserContextConfig
14
+ from playwright.async_api import BrowserContext as PlaywrightBrowserContext
15
+ import logging
16
+
17
+ from .custom_context import CustomBrowserContext
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ class CustomBrowser(Browser):
22
+
23
+ async def new_context(
24
+ self,
25
+ config: BrowserContextConfig = BrowserContextConfig()
26
+ ) -> CustomBrowserContext:
27
+ return CustomBrowserContext(config=config, browser=self)
28
+
29
+ async def _setup_browser(self, playwright: Playwright) -> PlaywrightBrowser:
30
+ """Sets up and returns a Playwright Browser instance with anti-detection measures."""
31
+ if self.config.wss_url:
32
+ browser = await playwright.chromium.connect(self.config.wss_url)
33
+ return browser
34
+ elif self.config.chrome_instance_path:
35
+ import subprocess
36
+
37
+ import requests
38
+
39
+ try:
40
+ # Check if browser is already running
41
+ response = requests.get('http://localhost:9222/json/version', timeout=2)
42
+ if response.status_code == 200:
43
+ logger.info('Reusing existing Chrome instance')
44
+ browser = await playwright.chromium.connect_over_cdp(
45
+ endpoint_url='http://localhost:9222',
46
+ timeout=20000, # 20 second timeout for connection
47
+ )
48
+ return browser
49
+ except requests.ConnectionError:
50
+ logger.debug('No existing Chrome instance found, starting a new one')
51
+
52
+ # Start a new Chrome instance
53
+ subprocess.Popen(
54
+ [
55
+ self.config.chrome_instance_path,
56
+ '--remote-debugging-port=9222',
57
+ ],
58
+ stdout=subprocess.DEVNULL,
59
+ stderr=subprocess.DEVNULL,
60
+ )
61
+
62
+ # Attempt to connect again after starting a new instance
63
+ for _ in range(10):
64
+ try:
65
+ response = requests.get('http://localhost:9222/json/version', timeout=2)
66
+ if response.status_code == 200:
67
+ break
68
+ except requests.ConnectionError:
69
+ pass
70
+ await asyncio.sleep(1)
71
+
72
+ try:
73
+ browser = await playwright.chromium.connect_over_cdp(
74
+ endpoint_url='http://localhost:9222',
75
+ timeout=20000, # 20 second timeout for connection
76
+ )
77
+ return browser
78
+ except Exception as e:
79
+ logger.error(f'Failed to start a new Chrome instance.: {str(e)}')
80
+ raise RuntimeError(
81
+ ' To start chrome in Debug mode, you need to close all existing Chrome instances and try again otherwise we can not connect to the instance.'
82
+ )
83
+
84
+ else:
85
+ try:
86
+ disable_security_args = []
87
+ if self.config.disable_security:
88
+ disable_security_args = [
89
+ '--disable-web-security',
90
+ '--disable-site-isolation-trials',
91
+ '--disable-features=IsolateOrigins,site-per-process',
92
+ ]
93
+
94
+ browser = await playwright.chromium.launch(
95
+ headless=self.config.headless,
96
+ args=[
97
+ '--no-sandbox',
98
+ '--disable-blink-features=AutomationControlled',
99
+ '--disable-infobars',
100
+ '--disable-background-timer-throttling',
101
+ '--disable-popup-blocking',
102
+ '--disable-backgrounding-occluded-windows',
103
+ '--disable-renderer-backgrounding',
104
+ '--disable-window-activation',
105
+ '--disable-focus-on-load',
106
+ '--no-first-run',
107
+ '--no-default-browser-check',
108
+ '--no-startup-window',
109
+ '--window-position=0,0',
110
+ # '--window-size=1280,1000',
111
+ ]
112
+ + disable_security_args
113
+ + self.config.extra_chromium_args,
114
+ proxy=self.config.proxy,
115
+ )
116
+
117
+ return browser
118
+ except Exception as e:
119
+ logger.error(f'Failed to initialize Playwright browser: {str(e)}')
120
+ raise
src/custom_context.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import logging
3
+ import os
4
+
5
+ from browser_use.browser.browser import Browser
6
+ from browser_use.browser.context import BrowserContext, BrowserContextConfig
7
+ from playwright.async_api import Browser as PlaywrightBrowser
8
+ from playwright.async_api import BrowserContext as PlaywrightBrowserContext
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class CustomBrowserContext(BrowserContext):
14
+ def __init__(
15
+ self,
16
+ browser: "Browser",
17
+ config: BrowserContextConfig = BrowserContextConfig()
18
+ ):
19
+ super(CustomBrowserContext, self).__init__(browser=browser, config=config)
20
+
21
+ async def _create_context(self, browser: PlaywrightBrowser) -> PlaywrightBrowserContext:
22
+ """Creates a new browser context with anti-detection measures and loads cookies if available."""
23
+ # If we have a context, return it directly
24
+
25
+ # Check if we should use existing context for persistence
26
+ if self.browser.config.chrome_instance_path and len(browser.contexts) > 0:
27
+ # Connect to existing Chrome instance instead of creating new one
28
+ context = browser.contexts[0]
29
+ else:
30
+ # Original code for creating new context
31
+ context = await browser.new_context(
32
+ viewport=self.config.browser_window_size,
33
+ no_viewport=False,
34
+ user_agent=(
35
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
36
+ "(KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36"
37
+ ),
38
+ java_script_enabled=True,
39
+ bypass_csp=self.config.disable_security,
40
+ ignore_https_errors=self.config.disable_security,
41
+ record_video_dir=self.config.save_recording_path,
42
+ record_video_size=self.config.browser_window_size,
43
+ )
44
+
45
+ if self.config.trace_path:
46
+ await context.tracing.start(screenshots=True, snapshots=True, sources=True)
47
+
48
+ # Load cookies if they exist
49
+ if self.config.cookies_file and os.path.exists(self.config.cookies_file):
50
+ with open(self.config.cookies_file, "r") as f:
51
+ cookies = json.load(f)
52
+ logger.info(
53
+ f"Loaded {len(cookies)} cookies from {self.config.cookies_file}"
54
+ )
55
+ await context.add_cookies(cookies)
56
+
57
+ # Expose anti-detection scripts
58
+ await context.add_init_script(
59
+ """
60
+ // Webdriver property
61
+ Object.defineProperty(navigator, 'webdriver', {
62
+ get: () => undefined
63
+ });
64
+
65
+ // Languages
66
+ Object.defineProperty(navigator, 'languages', {
67
+ get: () => ['en-US', 'en']
68
+ });
69
+
70
+ // Plugins
71
+ Object.defineProperty(navigator, 'plugins', {
72
+ get: () => [1, 2, 3, 4, 5]
73
+ });
74
+
75
+ // Chrome runtime
76
+ window.chrome = { runtime: {} };
77
+
78
+ // Permissions
79
+ const originalQuery = window.navigator.permissions.query;
80
+ window.navigator.permissions.query = (parameters) => (
81
+ parameters.name === 'notifications' ?
82
+ Promise.resolve({ state: Notification.permission }) :
83
+ originalQuery(parameters)
84
+ );
85
+ """
86
+ )
87
+
88
+ return context