Spaces:
Runtime error
Runtime error
import random | |
from typing import Optional, Literal, List, Dict, Tuple | |
import re | |
class UserAgentGenerator: | |
""" | |
Generate random user agents with specified constraints. | |
Attributes: | |
desktop_platforms (dict): A dictionary of possible desktop platforms and their corresponding user agent strings. | |
mobile_platforms (dict): A dictionary of possible mobile platforms and their corresponding user agent strings. | |
browser_combinations (dict): A dictionary of possible browser combinations and their corresponding user agent strings. | |
rendering_engines (dict): A dictionary of possible rendering engines and their corresponding user agent strings. | |
chrome_versions (list): A list of possible Chrome browser versions. | |
firefox_versions (list): A list of possible Firefox browser versions. | |
edge_versions (list): A list of possible Edge browser versions. | |
safari_versions (list): A list of possible Safari browser versions. | |
ios_versions (list): A list of possible iOS browser versions. | |
android_versions (list): A list of possible Android browser versions. | |
Methods: | |
generate_user_agent( | |
platform: Literal["desktop", "mobile"] = "desktop", | |
browser: str = "chrome", | |
rendering_engine: str = "chrome_webkit", | |
chrome_version: Optional[str] = None, | |
firefox_version: Optional[str] = None, | |
edge_version: Optional[str] = None, | |
safari_version: Optional[str] = None, | |
ios_version: Optional[str] = None, | |
android_version: Optional[str] = None | |
): Generates a random user agent string based on the specified parameters. | |
""" | |
def __init__(self): | |
# Previous platform definitions remain the same... | |
self.desktop_platforms = { | |
"windows": { | |
"10_64": "(Windows NT 10.0; Win64; x64)", | |
"10_32": "(Windows NT 10.0; WOW64)", | |
}, | |
"macos": { | |
"intel": "(Macintosh; Intel Mac OS X 10_15_7)", | |
"newer": "(Macintosh; Intel Mac OS X 10.15; rv:109.0)", | |
}, | |
"linux": { | |
"generic": "(X11; Linux x86_64)", | |
"ubuntu": "(X11; Ubuntu; Linux x86_64)", | |
"chrome_os": "(X11; CrOS x86_64 14541.0.0)", | |
} | |
} | |
self.mobile_platforms = { | |
"android": { | |
"samsung": "(Linux; Android 13; SM-S901B)", | |
"pixel": "(Linux; Android 12; Pixel 6)", | |
"oneplus": "(Linux; Android 13; OnePlus 9 Pro)", | |
"xiaomi": "(Linux; Android 12; M2102J20SG)", | |
}, | |
"ios": { | |
"iphone": "(iPhone; CPU iPhone OS 16_5 like Mac OS X)", | |
"ipad": "(iPad; CPU OS 16_5 like Mac OS X)", | |
} | |
} | |
# Browser Combinations | |
self.browser_combinations = { | |
1: [ | |
["chrome"], | |
["firefox"], | |
["safari"], | |
["edge"] | |
], | |
2: [ | |
["gecko", "firefox"], | |
["chrome", "safari"], | |
["webkit", "safari"] | |
], | |
3: [ | |
["chrome", "safari", "edge"], | |
["webkit", "chrome", "safari"] | |
] | |
} | |
# Rendering Engines with versions | |
self.rendering_engines = { | |
"chrome_webkit": "AppleWebKit/537.36", | |
"safari_webkit": "AppleWebKit/605.1.15", | |
"gecko": [ # Added Gecko versions | |
"Gecko/20100101", | |
"Gecko/20100101", # Firefox usually uses this constant version | |
"Gecko/2010010", | |
] | |
} | |
# Browser Versions | |
self.chrome_versions = [ | |
"Chrome/119.0.6045.199", | |
"Chrome/118.0.5993.117", | |
"Chrome/117.0.5938.149", | |
"Chrome/116.0.5845.187", | |
"Chrome/115.0.5790.171", | |
] | |
self.edge_versions = [ | |
"Edg/119.0.2151.97", | |
"Edg/118.0.2088.76", | |
"Edg/117.0.2045.47", | |
"Edg/116.0.1938.81", | |
"Edg/115.0.1901.203", | |
] | |
self.safari_versions = [ | |
"Safari/537.36", # For Chrome-based | |
"Safari/605.1.15", | |
"Safari/604.1", | |
"Safari/602.1", | |
"Safari/601.5.17", | |
] | |
# Added Firefox versions | |
self.firefox_versions = [ | |
"Firefox/119.0", | |
"Firefox/118.0.2", | |
"Firefox/117.0.1", | |
"Firefox/116.0", | |
"Firefox/115.0.3", | |
"Firefox/114.0.2", | |
"Firefox/113.0.1", | |
"Firefox/112.0", | |
"Firefox/111.0.1", | |
"Firefox/110.0", | |
] | |
def get_browser_stack(self, num_browsers: int = 1) -> List[str]: | |
""" | |
Get a valid combination of browser versions. | |
How it works: | |
1. Check if the number of browsers is supported. | |
2. Randomly choose a combination of browsers. | |
3. Iterate through the combination and add browser versions. | |
4. Return the browser stack. | |
Args: | |
num_browsers: Number of browser specifications (1-3) | |
Returns: | |
List[str]: A list of browser versions. | |
""" | |
if num_browsers not in self.browser_combinations: | |
raise ValueError(f"Unsupported number of browsers: {num_browsers}") | |
combination = random.choice(self.browser_combinations[num_browsers]) | |
browser_stack = [] | |
for browser in combination: | |
if browser == "chrome": | |
browser_stack.append(random.choice(self.chrome_versions)) | |
elif browser == "firefox": | |
browser_stack.append(random.choice(self.firefox_versions)) | |
elif browser == "safari": | |
browser_stack.append(random.choice(self.safari_versions)) | |
elif browser == "edge": | |
browser_stack.append(random.choice(self.edge_versions)) | |
elif browser == "gecko": | |
browser_stack.append(random.choice(self.rendering_engines["gecko"])) | |
elif browser == "webkit": | |
browser_stack.append(self.rendering_engines["chrome_webkit"]) | |
return browser_stack | |
def generate(self, | |
device_type: Optional[Literal['desktop', 'mobile']] = None, | |
os_type: Optional[str] = None, | |
device_brand: Optional[str] = None, | |
browser_type: Optional[Literal['chrome', 'edge', 'safari', 'firefox']] = None, | |
num_browsers: int = 3) -> str: | |
""" | |
Generate a random user agent with specified constraints. | |
Args: | |
device_type: 'desktop' or 'mobile' | |
os_type: 'windows', 'macos', 'linux', 'android', 'ios' | |
device_brand: Specific device brand | |
browser_type: 'chrome', 'edge', 'safari', or 'firefox' | |
num_browsers: Number of browser specifications (1-3) | |
""" | |
# Get platform string | |
platform = self.get_random_platform(device_type, os_type, device_brand) | |
# Start with Mozilla | |
components = ["Mozilla/5.0", platform] | |
# Add browser stack | |
browser_stack = self.get_browser_stack(num_browsers) | |
# Add appropriate legacy token based on browser stack | |
if "Firefox" in str(browser_stack): | |
components.append(random.choice(self.rendering_engines["gecko"])) | |
elif "Chrome" in str(browser_stack) or "Safari" in str(browser_stack): | |
components.append(self.rendering_engines["chrome_webkit"]) | |
components.append("(KHTML, like Gecko)") | |
# Add browser versions | |
components.extend(browser_stack) | |
return " ".join(components) | |
def generate_with_client_hints(self, **kwargs) -> Tuple[str, str]: | |
"""Generate both user agent and matching client hints""" | |
user_agent = self.generate(**kwargs) | |
client_hints = self.generate_client_hints(user_agent) | |
return user_agent, client_hints | |
def get_random_platform(self, device_type, os_type, device_brand): | |
"""Helper method to get random platform based on constraints""" | |
platforms = self.desktop_platforms if device_type == 'desktop' else \ | |
self.mobile_platforms if device_type == 'mobile' else \ | |
{**self.desktop_platforms, **self.mobile_platforms} | |
if os_type: | |
for platform_group in [self.desktop_platforms, self.mobile_platforms]: | |
if os_type in platform_group: | |
platforms = {os_type: platform_group[os_type]} | |
break | |
os_key = random.choice(list(platforms.keys())) | |
if device_brand and device_brand in platforms[os_key]: | |
return platforms[os_key][device_brand] | |
return random.choice(list(platforms[os_key].values())) | |
def parse_user_agent(self, user_agent: str) -> Dict[str, str]: | |
"""Parse a user agent string to extract browser and version information""" | |
browsers = { | |
'chrome': r'Chrome/(\d+)', | |
'edge': r'Edg/(\d+)', | |
'safari': r'Version/(\d+)', | |
'firefox': r'Firefox/(\d+)' | |
} | |
result = {} | |
for browser, pattern in browsers.items(): | |
match = re.search(pattern, user_agent) | |
if match: | |
result[browser] = match.group(1) | |
return result | |
def generate_client_hints(self, user_agent: str) -> str: | |
"""Generate Sec-CH-UA header value based on user agent string""" | |
browsers = self.parse_user_agent(user_agent) | |
# Client hints components | |
hints = [] | |
# Handle different browser combinations | |
if 'chrome' in browsers: | |
hints.append(f'"Chromium";v="{browsers["chrome"]}"') | |
hints.append('"Not_A Brand";v="8"') | |
if 'edge' in browsers: | |
hints.append(f'"Microsoft Edge";v="{browsers["edge"]}"') | |
else: | |
hints.append(f'"Google Chrome";v="{browsers["chrome"]}"') | |
elif 'firefox' in browsers: | |
# Firefox doesn't typically send Sec-CH-UA | |
return '""' | |
elif 'safari' in browsers: | |
# Safari's format for client hints | |
hints.append(f'"Safari";v="{browsers["safari"]}"') | |
hints.append('"Not_A Brand";v="8"') | |
return ', '.join(hints) | |
# Example usage: | |
if __name__ == "__main__": | |
generator = UserAgentGenerator() | |
print(generator.generate()) | |
print("\nSingle browser (Chrome):") | |
print(generator.generate(num_browsers=1, browser_type='chrome')) | |
print("\nTwo browsers (Gecko/Firefox):") | |
print(generator.generate(num_browsers=2)) | |
print("\nThree browsers (Chrome/Safari/Edge):") | |
print(generator.generate(num_browsers=3)) | |
print("\nFirefox on Linux:") | |
print(generator.generate( | |
device_type='desktop', | |
os_type='linux', | |
browser_type='firefox', | |
num_browsers=2 | |
)) | |
print("\nChrome/Safari/Edge on Windows:") | |
print(generator.generate( | |
device_type='desktop', | |
os_type='windows', | |
num_browsers=3 | |
)) |