-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #707 from Tverous/firefox-patch
feat: add support for Firefox browser compatibility
- Loading branch information
Showing
10 changed files
with
266 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import os | ||
|
||
from abc import ABC, abstractmethod | ||
from loguru import logger | ||
|
||
|
||
class BrowserProfile: | ||
"""Manages browser profile creation and configuration""" | ||
def __init__(self, browser_type: str): | ||
self.browser_type: str = browser_type.lower() | ||
self.profile_path = os.path.join( | ||
os.getcwd(), | ||
f"{self.browser_type}_profile", | ||
"linkedin_profile" | ||
) | ||
|
||
def ensure_profile_exists(self) -> str: | ||
""" | ||
Ensures the browser profile directory exists | ||
Returns: Path to the profile directory | ||
""" | ||
logger.debug(f"Ensuring {self.browser_type} profile exists at path: {self.profile_path}") | ||
profile_dir = os.path.dirname(self.profile_path) | ||
|
||
if not os.path.exists(profile_dir): | ||
os.makedirs(profile_dir) | ||
logger.debug(f"Created directory for {self.browser_type} profile: {profile_dir}") | ||
|
||
if not os.path.exists(self.profile_path): | ||
os.makedirs(self.profile_path) | ||
logger.debug(f"Created {self.browser_type} profile directory: {self.profile_path}") | ||
|
||
return self.profile_path | ||
|
||
class Browser(ABC): | ||
"""Abstract base class for browser implementations""" | ||
def __init__(self): | ||
self.profile = BrowserProfile(self.browser_type) | ||
|
||
@property | ||
def browser_type(self) -> str: | ||
"""Return the browser type identifier""" | ||
return self.__class__.browser_type | ||
|
||
@abstractmethod | ||
def create_options(self): | ||
"""Create and return browser-specific options""" | ||
|
||
@abstractmethod | ||
def create_service(self): | ||
"""Create and return browser-specific service""" | ||
|
||
def create_driver(self): | ||
"""Create and return browser-specific WebDriver instance""" | ||
try: | ||
options = self.create_options() | ||
service = self.create_service() | ||
driver = self._create_driver_instance(service, options) | ||
logger.debug(f"{self.browser_type} WebDriver instance created successfully") | ||
return driver | ||
except Exception as e: | ||
logger.error(f"Failed to create {self.browser_type} WebDriver: {e}") | ||
raise RuntimeError(f"Failed to initialize {self.browser_type} browser: {str(e)}") | ||
|
||
@abstractmethod | ||
def _create_driver_instance(self, service, options): | ||
"""Create the specific driver instance""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
from typing import Union | ||
|
||
from selenium import webdriver | ||
from loguru import logger | ||
|
||
from config import BROWSER_TYPE_CONFIG | ||
from src.webdrivers.browser_type import BrowserType | ||
|
||
|
||
class BrowserFactory: | ||
"""Factory class for creating browser instances""" | ||
_browser_type: BrowserType = BROWSER_TYPE_CONFIG | ||
@classmethod | ||
def get_browser_type(cls) -> BrowserType: | ||
"""Get current browser type""" | ||
return cls._browser_type | ||
|
||
@classmethod | ||
def set_browser_type(cls, browser_type: BrowserType) -> None: | ||
"""Set browser type""" | ||
# safety check additional to type check. | ||
if browser_type not in BrowserType: | ||
raise ValueError(f"Unsupported browser type: {browser_type}") | ||
cls._browser_type = browser_type | ||
logger.debug(f"Browser type set to: {browser_type}") | ||
|
||
@classmethod | ||
def get_browser(cls) -> Union[webdriver.Chrome, webdriver.Firefox]: | ||
""" | ||
Create and return a WebDriver instance for the specified browser type | ||
Args: | ||
browser_type: BrowserType enum value | ||
Returns: | ||
WebDriver instance | ||
Raises: | ||
RuntimeError: If browser initialization fails | ||
""" | ||
if cls._browser_type not in BrowserType: | ||
raise ValueError("Unsupported browser type: {cls._browser_type}") | ||
|
||
browser = cls._browser_type.value() | ||
|
||
return browser.create_driver() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from enum import Enum | ||
|
||
from src.webdrivers.chrome import Chrome | ||
from src.webdrivers.firefox import Firefox | ||
|
||
|
||
class BrowserType(Enum): | ||
"""Enum for supported browser types""" | ||
CHROME = Chrome | ||
FIREFOX = Firefox |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import os | ||
|
||
from loguru import logger | ||
from selenium import webdriver | ||
from selenium.webdriver.chrome.service import Service as ChromeService | ||
from webdriver_manager.chrome import ChromeDriverManager | ||
|
||
from src.webdrivers.base_browser import Browser | ||
|
||
|
||
class Chrome(Browser): | ||
"""Chrome browser implementation""" | ||
browser_type: str = "chrome" | ||
|
||
def create_options(self) -> webdriver.ChromeOptions: | ||
"""Create Chrome-specific options""" | ||
self.profile.ensure_profile_exists() | ||
options = webdriver.ChromeOptions() | ||
|
||
chrome_arguments = [ | ||
"--start-maximized", "--no-sandbox", "--disable-dev-shm-usage", | ||
"--ignore-certificate-errors", "--disable-extensions", "--disable-gpu", | ||
"window-size=1200x800", "--disable-background-timer-throttling", | ||
"--disable-backgrounding-occluded-windows", "--disable-translate", | ||
"--disable-popup-blocking", "--no-first-run", "--no-default-browser-check", | ||
"--disable-logging", "--disable-autofill", "--disable-plugins", | ||
"--disable-animations", "--disable-cache" | ||
] | ||
|
||
for arg in chrome_arguments: | ||
options.add_argument(arg) | ||
|
||
options.add_experimental_option("excludeSwitches", ["enable-automation", "enable-logging"]) | ||
|
||
prefs = { | ||
"profile.default_content_setting_values.images": 2, | ||
"profile.managed_default_content_settings.stylesheets": 2, | ||
} | ||
options.add_experimental_option("prefs", prefs) | ||
|
||
if self.profile.profile_path: | ||
initial_path = os.path.dirname(self.profile.profile_path) | ||
profile_dir = os.path.basename(self.profile.profile_path) | ||
options.add_argument('--user-data-dir=' + initial_path) | ||
options.add_argument("--profile-directory=" + profile_dir) | ||
logger.debug(f"Using Chrome profile directory: {self.profile.profile_path}") | ||
else: | ||
options.add_argument("--incognito") | ||
logger.debug("Using Chrome in incognito mode") | ||
|
||
return options | ||
|
||
def create_service(self) -> ChromeService: | ||
"""Create Chrome-specific service""" | ||
return ChromeService(ChromeDriverManager().install()) | ||
|
||
def _create_driver_instance(self, service, options): | ||
"""Create Chrome WebDriver instance""" | ||
return webdriver.Chrome(service=service, options=options) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from loguru import logger | ||
from selenium import webdriver | ||
from selenium.webdriver.firefox.service import Service as FirefoxService | ||
from webdriver_manager.firefox import GeckoDriverManager | ||
|
||
from src.webdrivers.base_browser import Browser | ||
|
||
|
||
class Firefox(Browser): | ||
"""Firefox browser implementation""" | ||
browser_type: str = "firefox" | ||
|
||
def create_options(self) -> webdriver.FirefoxOptions: | ||
"""Create Firefox-specific options""" | ||
self.profile.ensure_profile_exists() | ||
options = webdriver.FirefoxOptions() | ||
|
||
firefox_arguments = [ | ||
"--start-maximized", "--no-sandbox", "--disable-dev-shm-usage", | ||
"--ignore-certificate-errors", "--disable-extensions", "--disable-gpu", | ||
"--disable-background-timer-throttling", | ||
"--disable-backgrounding-occluded-windows", "--disable-translate", | ||
"--disable-popup-blocking", "--no-first-run", "--no-default-browser-check", | ||
"--disable-logging", "--disable-autofill", "--disable-plugins", | ||
"--disable-animations", "--disable-cache" | ||
] | ||
|
||
for arg in firefox_arguments: | ||
options.add_argument(arg) | ||
|
||
prefs = { | ||
"permissions.default.image": 2, | ||
"permissions.default.stylesheet": 2, | ||
} | ||
for key, value in prefs.items(): | ||
options.set_preference(key, value) | ||
|
||
if self.profile.profile_path: | ||
options.set_preference("profile", self.profile.profile_path) | ||
logger.debug(f"Using Firefox profile directory: {self.profile.profile_path}") | ||
else: | ||
options.set_preference("browser.privatebrowsing.autostart", True) | ||
logger.debug("Using Firefox in private browsing mode") | ||
|
||
return options | ||
|
||
def create_service(self) -> FirefoxService: | ||
"""Create Firefox-specific service""" | ||
return FirefoxService(GeckoDriverManager().install()) | ||
|
||
def _create_driver_instance(self, service, options): | ||
"""Create Firefox WebDriver instance""" | ||
return webdriver.Firefox(service=service, options=options) |
Oops, something went wrong.