"""
Chrome Automation with Cloudflare Turnstile Protection
SETUP:
pip install patchright==1.56.0 psutil
"""
import asyncio
import os
import sys
from pathlib import Path
from patchright.async_api import async_playwright
class ChromeAutomation:
def __init__(self):
print("Chrome Automation started. Please wait...")
self.playwright = None # Patchright engine
self.browser = None # Browser session/profile
self.page = None # Active tab
async def open_profile(self, profile_no=1, max_retries=3, use_cwd=False):
"""
Open Chrome with specified profile.
Args:
profile_no: Profile number (1 = "Profile 1", 0 = "Default")
max_retries: Retry attempts (not used yet)
use_cwd: If True, use current directory; if False, use Chrome original profiles
"""
if self.playwright is None:
self.playwright = await async_playwright().start()
# Determine profile directory name
if profile_no == 0:
profile_dir_name = "Default"
else:
profile_dir_name = f"Profile {profile_no}"
# Determine user data directory
if use_cwd:
# Use current working directory
user_data_dir = str(Path.cwd() / "ChromeProfile")
Path(user_data_dir).mkdir(exist_ok=True)
print(f"???? Using CWD profile: {user_data_dir}")
else:
# Use Chrome original profiles
username = os.getenv('USERNAME') or os.getenv('USER') or 'User'
if sys.platform == 'win32':
user_data_dir = f"C:\\Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data"
elif sys.platform == 'darwin':
user_data_dir = str(Path.home() / "Library" / "Application Support" / "Google" / "Chrome")
else: # Linux
user_data_dir = str(Path.home() / ".config" / "google-chrome")
if not Path(user_data_dir).exists():
print(f"?????? Original Chrome profile not found at {user_data_dir}")
print("???? Try using use_cwd=True to create profile in current directory")
raise FileNotFoundError(f"Chrome profile not found: {user_data_dir}")
print(f"???? Using Chrome original profile: {user_data_dir}")
self.browser = await self.playwright.chromium.launch_persistent_context(
user_data_dir=user_data_dir,
channel="chrome",
headless=False,
no_viewport=True,
args=[f"--profile-directory={profile_dir_name}"]
)
# Use existing page if available
if len(self.browser.pages) > 0:
self.page = self.browser.pages[0]
else:
self.page = await self.browser.new_page()
print(f"??? Chrome session ready with {profile_dir_name}")
async def solve_turnstile_challenge(self):
"""
Detect and solve Cloudflare Turnstile challenge.
Returns:
bool: True if solved or not present, False if failed
"""
try:
print("Checking for human verification challenge...")
# Step 1: Quickly check if a Cloudflare/Turnstile iframe is injected into the DOM
# We wait up to 3.5 seconds for the iframe itself. If it's not and won't be there, we skip.
challenge_iframe = self.page.locator(
"iframe[src*='cloudflare'], iframe[src*='turnstile'], "
"iframe[title*='challenge'], iframe[title*='Cloudflare']"
).first
is_challenge_present = False
try:
await challenge_iframe.wait_for(state="attached", timeout=3500)
is_challenge_present = True
except Exception:
pass
if is_challenge_present:
print("Turnstile challenge detected! Waiting for checkbox to become visible...")
# The iframe is present, now wait up to 15 seconds for the checkbox inside to be visible
checkbox = self.page.frame_locator("iframe").locator(
".cb-c input[type='checkbox'], label.cb-lb, .cb-i"
).first
await checkbox.wait_for(state="visible", timeout=15000)
print("Checkbox found! Moving mouse carefully...")
await checkbox.hover()
await asyncio.sleep(0.8) # Human-like pause
await checkbox.click(delay=150) # Human-like click delay
print("Checkbox clicked successfully!")
# Wait a little bit for the challenge to resolve
await asyncio.sleep(5)
return True
else:
print("No Turnstile challenge detected on page. Proceeding immediately.")
return True
except Exception as e:
print(f"Skipping verification check or failed: {e}")
return False
async def close(self):
"""Close browser."""
if self.browser:
await self.browser.close()
if self.playwright:
await self.playwright.stop()
async def main():
automation = ChromeAutomation()
# Example 1: Use CWD with Profile 1
await automation.open_profile(profile_no=1, use_cwd=True)
# Example 2: Use Chrome original profile (uncomment to use)
# await automation.open_profile(profile_no=1, use_cwd=False)
await automation.page.goto('https://www.ip2location.com/')
await automation.solve_turnstile_challenge()
await automation.page.screenshot(path=f'example-{automation.playwright.chromium.name}.png')
await automation.close()
if __name__ == "__main__":
asyncio.run(main())