wip
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
Download and patch ChromeDriver once. Run via: make patch-driver
|
||||
|
||||
Uses the official Chrome for Testing API to fetch the exact ChromeDriver
|
||||
version matching the installed Chrome, then patches it with undetected_chromedriver.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.request
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import undetected_chromedriver as uc
|
||||
|
||||
from config import config
|
||||
from logger import get_logger
|
||||
|
||||
log = get_logger(__name__)
|
||||
|
||||
|
||||
def _get_chrome_full_version() -> tuple[int, str]:
|
||||
candidates = ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]
|
||||
if config.chrome_binary:
|
||||
candidates.insert(0, config.chrome_binary)
|
||||
|
||||
for binary in candidates:
|
||||
if not shutil.which(binary) and not Path(binary).exists():
|
||||
continue
|
||||
try:
|
||||
out = subprocess.check_output(
|
||||
[binary, "--version"], stderr=subprocess.DEVNULL, text=True, timeout=5
|
||||
).strip()
|
||||
full = out.split()[-1]
|
||||
major = int(full.split(".")[0])
|
||||
log.info("Detected Chrome %s via '%s'", full, binary)
|
||||
return major, full
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
log.critical("Could not detect Chrome version. Set CHROME_BINARY in .env")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _latest_chromedriver_version(major: int) -> str:
|
||||
url = f"https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_{major}"
|
||||
try:
|
||||
with urllib.request.urlopen(url, timeout=15) as resp:
|
||||
version = resp.read().decode().strip()
|
||||
log.info("Latest ChromeDriver for Chrome %d: %s", major, version)
|
||||
return version
|
||||
except Exception as exc:
|
||||
log.critical("Could not fetch ChromeDriver version for Chrome %d: %s", major, exc)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _download_chromedriver(version: str, dest: Path) -> None:
|
||||
zip_url = (
|
||||
f"https://storage.googleapis.com/chrome-for-testing-public"
|
||||
f"/{version}/linux64/chromedriver-linux64.zip"
|
||||
)
|
||||
log.info("Downloading ChromeDriver %s …", version)
|
||||
try:
|
||||
with urllib.request.urlopen(zip_url, timeout=60) as resp:
|
||||
data = resp.read()
|
||||
except Exception as exc:
|
||||
log.critical("Download failed: %s", exc)
|
||||
sys.exit(1)
|
||||
|
||||
with zipfile.ZipFile(io.BytesIO(data)) as zf:
|
||||
binary_name = next(
|
||||
n for n in zf.namelist() if n.endswith("/chromedriver") or n == "chromedriver"
|
||||
)
|
||||
dest.write_bytes(zf.read(binary_name))
|
||||
|
||||
dest.chmod(dest.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
|
||||
log.info("Extracted to %s", dest)
|
||||
|
||||
|
||||
def _patch(dest: Path) -> None:
|
||||
log.info("Patching binary …")
|
||||
patcher = uc.Patcher(executable_path=str(dest))
|
||||
patcher.patch_exe()
|
||||
log.info("Patch applied successfully")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
dest = Path(config.chromedriver_path)
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
major, _ = _get_chrome_full_version()
|
||||
cd_version = _latest_chromedriver_version(major)
|
||||
|
||||
_download_chromedriver(cd_version, dest)
|
||||
_patch(dest)
|
||||
|
||||
log.info("ChromeDriver %s ready at %s", cd_version, dest)
|
||||
log.info("Re-run 'make patch-driver' only when Chrome updates to a new major version.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user