Metadata-Version: 2.4
Name: selenium-proxyhat
Version: 0.1.0
Summary: ProxyHat residential proxies for Selenium — authenticated Chrome via an auto-generated auth extension, sticky sessions, geo-targeting, rotation.
Project-URL: Homepage, https://proxyhat.com
Project-URL: Documentation, https://docs.proxyhat.com
Project-URL: Repository, https://github.com/ProxyHatCom/selenium-proxyhat
Author-email: ProxyHat <support@proxyhat.com>
License-Expression: MIT
License-File: LICENSE
Keywords: browser-automation,chrome,proxy,proxyhat,residential-proxy,selenium,sticky-session,webdriver
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: Proxy Servers
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.11
Requires-Dist: proxyhat>=0.2.0
Requires-Dist: selenium>=4.10
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: seleniumwire
Requires-Dist: selenium-wire>=5.1; extra == 'seleniumwire'
Description-Content-Type: text/markdown

# selenium-proxyhat

Route [Selenium](https://www.selenium.dev/) Chrome through [ProxyHat](https://proxyhat.com?utm_source=github&utm_medium=readme&utm_campaign=selenium) residential proxies — **authenticated** gateway proxies that just work in vanilla Selenium, plus a sticky residential IP pinned for the whole session, geo-targeting, and rotation.

[![CI](https://github.com/ProxyHatCom/selenium-proxyhat/actions/workflows/ci.yml/badge.svg)](https://github.com/ProxyHatCom/selenium-proxyhat/actions/workflows/ci.yml)
[![Compatible with selenium latest](https://github.com/ProxyHatCom/selenium-proxyhat/actions/workflows/compat.yml/badge.svg)](https://github.com/ProxyHatCom/selenium-proxyhat/actions/workflows/compat.yml)
[![PyPI](https://img.shields.io/pypi/v/selenium-proxyhat)](https://pypi.org/project/selenium-proxyhat/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

## Why

Chrome's `--proxy-server` flag **can't carry a username and password**, so pointing Selenium at a credentialed residential gateway normally means an ugly `page.authenticate` / CDP dance or an extra proxy dependency. And running a real browser from a datacenter IP gets flagged, CAPTCHA-walled, and blocked anyway.

`selenium-proxyhat` fixes both. It plugs ProxyHat's residential IPs (50M+ across 148+ countries) into Selenium and handles gateway auth by auto-generating a tiny in-memory Chrome extension that answers the proxy's auth challenge — **works with vanilla Selenium, no selenium-wire required**. One pinned residential IP per session by default, so cookies and fingerprint stay consistent while your script works.

## Install

```bash
pip install selenium-proxyhat
```

`selenium>=4.10` and `proxyhat` come with it. Bring your own Chrome + chromedriver (Selenium Manager resolves the driver for you).

## Quick start

```python
from selenium_proxyhat import proxyhat_driver

# An API key (PROXYHAT_API_KEY) auto-selects an active residential sub-user:
driver = proxyhat_driver(country="us")   # sticky US IP for the whole session
driver.get("https://httpbin.org/ip")
print(driver.find_element("tag name", "body").text)
driver.quit()
```

Get an API key at [proxyhat.com](https://proxyhat.com?utm_source=github&utm_medium=readme&utm_campaign=selenium).

Prefer to build the driver yourself? Get configured `ChromeOptions` (proxy flag + auth extension already applied) and launch Chrome your way:

```python
from selenium import webdriver
from selenium_proxyhat import proxyhat_chrome_options

options = proxyhat_chrome_options(country="de", sticky="1h")
options.add_argument("--headless=new")   # add whatever else you need
driver = webdriver.Chrome(options=options)
```

## Credentials

Pass them explicitly or via environment variables — options win over env:

| Option | Env var | Notes |
|---|---|---|
| `api_key` | `PROXYHAT_API_KEY` | Auto-selects an active sub-user with remaining traffic |
| `sub_user` | `PROXYHAT_SUBUSER` | Pick a specific sub-user by uuid or name (with an API key) |
| `username` | `PROXYHAT_USERNAME` | Explicit gateway `proxy_username` (skips the API) |
| `password` | `PROXYHAT_PASSWORD` | Explicit gateway `proxy_password` |

## Targeting

```python
proxyhat_chrome_options(
    country="us",      # ISO code or "any" (default)
    region="california",
    city="new_york",
    filter="high",     # AI IP-quality tier
    sticky="30m",      # session lifetime (default); sticky=False rotates every request
)
```

Same keyword arguments work on `proxyhat_driver(...)` and `proxyhat_seleniumwire_options(...)`.

### Sticky IP per session (default)

A browser session takes many steps against the same site — logging in, clicking, scrolling. If the exit IP changed mid-session the site would see a user teleporting between cities and block it. So this package is **sticky by default**: one residential IP is pinned for the whole session (`sticky="30m"`, renewed as you work), keeping cookies and fingerprint coherent.

Want a fresh IP on **every** request instead (e.g. many independent one-shot fetches)? Turn stickiness off:

```python
proxyhat_chrome_options(country="us", sticky=False)  # rotating residential IP per connection
```

Set a custom lifetime with `sticky="2h"`.

## How authentication works

Chrome takes the proxy host/port from `--proxy-server=gate.proxyhat.com:8080`, but a residential gateway needs a username (the ProxyHat targeting string) and password. Since the flag can't carry them, `proxyhat_chrome_options` generates a minimal **Manifest V3** extension in memory whose background service worker answers `chrome.webRequest.onAuthRequired` with your credentials, and adds it via `ChromeOptions.add_extension`. The MV3 blocking auth listener is enabled by the `webRequestAuthProvider` permission. Nothing is written to your project — the packed extension lives in a temp file that Chrome reads at launch.

The targeting username (e.g. `<user>-country-us-sid-<id>-ttl-30m`) is built by the official [`proxyhat`](https://pypi.org/project/proxyhat/) SDK, so a sticky session mints a single session id shared across the run.

## selenium-wire alternative (optional)

Already using [selenium-wire](https://github.com/wkeeling/selenium-wire)? Skip the extension and let selenium-wire carry credentials in the upstream-proxy URL:

```python
from seleniumwire import webdriver           # pip install selenium-proxyhat[seleniumwire]
from selenium_proxyhat import proxyhat_seleniumwire_options

driver = webdriver.Chrome(
    seleniumwire_options=proxyhat_seleniumwire_options(country="us"),
)
# -> {"proxy": {"http": "http://<user>:<pass>@gate.proxyhat.com:8080", "https": ..., "no_proxy": ...}}
```

selenium-wire is an **optional** extra; `proxyhat_seleniumwire_options()` only builds the dict and never imports it. It also supports `protocol="socks5"` for an authenticated SOCKS5 upstream (which the extension path does not).

## License

MIT © [ProxyHat](https://proxyhat.com)
