botonomus 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- botonomus-0.2.0/LICENSE +21 -0
- botonomus-0.2.0/PKG-INFO +368 -0
- botonomus-0.2.0/README.md +319 -0
- botonomus-0.2.0/pyproject.toml +77 -0
- botonomus-0.2.0/setup.cfg +4 -0
- botonomus-0.2.0/src/botonomus/__init__.py +63 -0
- botonomus-0.2.0/src/botonomus/_version.py +3 -0
- botonomus-0.2.0/src/botonomus/browser/__init__.py +38 -0
- botonomus-0.2.0/src/botonomus/browser/arguments.py +57 -0
- botonomus-0.2.0/src/botonomus/browser/backend.py +64 -0
- botonomus-0.2.0/src/botonomus/browser/chrome.py +227 -0
- botonomus-0.2.0/src/botonomus/browser/discovery.py +100 -0
- botonomus-0.2.0/src/botonomus/browser/display.py +194 -0
- botonomus-0.2.0/src/botonomus/browser/installer.py +588 -0
- botonomus-0.2.0/src/botonomus/browser/process.py +77 -0
- botonomus-0.2.0/src/botonomus/browser/release.py +353 -0
- botonomus-0.2.0/src/botonomus/browser/signing.py +197 -0
- botonomus-0.2.0/src/botonomus/browser/version.py +80 -0
- botonomus-0.2.0/src/botonomus/cdp/__init__.py +37 -0
- botonomus-0.2.0/src/botonomus/cdp/browser.py +89 -0
- botonomus-0.2.0/src/botonomus/cdp/connection.py +205 -0
- botonomus-0.2.0/src/botonomus/cdp/errors.py +36 -0
- botonomus-0.2.0/src/botonomus/cdp/input.py +466 -0
- botonomus-0.2.0/src/botonomus/cdp/locator.py +189 -0
- botonomus-0.2.0/src/botonomus/cdp/page.py +320 -0
- botonomus-0.2.0/src/botonomus/cdp/polling.py +49 -0
- botonomus-0.2.0/src/botonomus/cdp/scripts.py +70 -0
- botonomus-0.2.0/src/botonomus/cdp/websocket.py +200 -0
- botonomus-0.2.0/src/botonomus/cli/__init__.py +10 -0
- botonomus-0.2.0/src/botonomus/cli/__main__.py +7 -0
- botonomus-0.2.0/src/botonomus/cli/benchmark.py +70 -0
- botonomus-0.2.0/src/botonomus/cli/binaries.py +158 -0
- botonomus-0.2.0/src/botonomus/cli/browse.py +73 -0
- botonomus-0.2.0/src/botonomus/cli/common.py +261 -0
- botonomus-0.2.0/src/botonomus/cli/consistency.py +48 -0
- botonomus-0.2.0/src/botonomus/cli/detect.py +129 -0
- botonomus-0.2.0/src/botonomus/cli/experiment.py +71 -0
- botonomus-0.2.0/src/botonomus/cli/info.py +180 -0
- botonomus-0.2.0/src/botonomus/cli/main.py +110 -0
- botonomus-0.2.0/src/botonomus/cli/probe.py +135 -0
- botonomus-0.2.0/src/botonomus/cli/profiles.py +148 -0
- botonomus-0.2.0/src/botonomus/cli/proxies.py +155 -0
- botonomus-0.2.0/src/botonomus/cli/trace.py +103 -0
- botonomus-0.2.0/src/botonomus/config/__init__.py +15 -0
- botonomus-0.2.0/src/botonomus/config/browser.py +204 -0
- botonomus-0.2.0/src/botonomus/config/proxy.py +85 -0
- botonomus-0.2.0/src/botonomus/core/__init__.py +7 -0
- botonomus-0.2.0/src/botonomus/core/identity.py +187 -0
- botonomus-0.2.0/src/botonomus/core/manager.py +303 -0
- botonomus-0.2.0/src/botonomus/core/session.py +76 -0
- botonomus-0.2.0/src/botonomus/diagnostics/__init__.py +49 -0
- botonomus-0.2.0/src/botonomus/diagnostics/apitrace.py +324 -0
- botonomus-0.2.0/src/botonomus/diagnostics/assets/apitrace.js +246 -0
- botonomus-0.2.0/src/botonomus/diagnostics/assets/consistency.html +122 -0
- botonomus-0.2.0/src/botonomus/diagnostics/assets/probe.html +73 -0
- botonomus-0.2.0/src/botonomus/diagnostics/benchmark.py +125 -0
- botonomus-0.2.0/src/botonomus/diagnostics/catalogue.py +289 -0
- botonomus-0.2.0/src/botonomus/diagnostics/compare.py +45 -0
- botonomus-0.2.0/src/botonomus/diagnostics/consistency.py +305 -0
- botonomus-0.2.0/src/botonomus/diagnostics/detection.py +596 -0
- botonomus-0.2.0/src/botonomus/diagnostics/experiment.py +432 -0
- botonomus-0.2.0/src/botonomus/diagnostics/probe.py +103 -0
- botonomus-0.2.0/src/botonomus/diagnostics/snapshots.py +80 -0
- botonomus-0.2.0/src/botonomus/diagnostics/stats.py +23 -0
- botonomus-0.2.0/src/botonomus/drivers/__init__.py +31 -0
- botonomus-0.2.0/src/botonomus/drivers/base.py +51 -0
- botonomus-0.2.0/src/botonomus/drivers/native.py +58 -0
- botonomus-0.2.0/src/botonomus/drivers/playwright.py +91 -0
- botonomus-0.2.0/src/botonomus/errors.py +88 -0
- botonomus-0.2.0/src/botonomus/fingerprint/__init__.py +35 -0
- botonomus-0.2.0/src/botonomus/fingerprint/gpus.py +142 -0
- botonomus-0.2.0/src/botonomus/fingerprint/host.py +84 -0
- botonomus-0.2.0/src/botonomus/fingerprint/persona.py +263 -0
- botonomus-0.2.0/src/botonomus/fingerprint/seed.py +113 -0
- botonomus-0.2.0/src/botonomus/fingerprint/switches.py +36 -0
- botonomus-0.2.0/src/botonomus/human/__init__.py +56 -0
- botonomus-0.2.0/src/botonomus/human/actionability.py +104 -0
- botonomus-0.2.0/src/botonomus/human/clock.py +28 -0
- botonomus-0.2.0/src/botonomus/human/config.py +154 -0
- botonomus-0.2.0/src/botonomus/human/human.py +370 -0
- botonomus-0.2.0/src/botonomus/human/keyboard.py +233 -0
- botonomus-0.2.0/src/botonomus/human/page.py +215 -0
- botonomus-0.2.0/src/botonomus/human/paths.py +79 -0
- botonomus-0.2.0/src/botonomus/human/timing.py +119 -0
- botonomus-0.2.0/src/botonomus/network/__init__.py +34 -0
- botonomus-0.2.0/src/botonomus/network/forwarder.py +219 -0
- botonomus-0.2.0/src/botonomus/network/geoip.py +426 -0
- botonomus-0.2.0/src/botonomus/network/proxies.py +128 -0
- botonomus-0.2.0/src/botonomus/network/tunnel.py +197 -0
- botonomus-0.2.0/src/botonomus/profiles/__init__.py +16 -0
- botonomus-0.2.0/src/botonomus/profiles/lease.py +100 -0
- botonomus-0.2.0/src/botonomus/profiles/store.py +90 -0
- botonomus-0.2.0/src/botonomus/profiles/warmup.py +260 -0
- botonomus-0.2.0/src/botonomus/py.typed +0 -0
- botonomus-0.2.0/src/botonomus.egg-info/PKG-INFO +368 -0
- botonomus-0.2.0/src/botonomus.egg-info/SOURCES.txt +98 -0
- botonomus-0.2.0/src/botonomus.egg-info/dependency_links.txt +1 -0
- botonomus-0.2.0/src/botonomus.egg-info/entry_points.txt +2 -0
- botonomus-0.2.0/src/botonomus.egg-info/requires.txt +26 -0
- botonomus-0.2.0/src/botonomus.egg-info/top_level.txt +1 -0
botonomus-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Botonomus contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
botonomus-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: botonomus
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Stealth browser automation on real Chrome: native CDP driver, C++-level personas, async session pools
|
|
5
|
+
Author: Botonomus contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Kuduxaaa/botonomus
|
|
8
|
+
Project-URL: Documentation, https://kuduxaaa.github.io/botonomus
|
|
9
|
+
Project-URL: Changelog, https://github.com/Kuduxaaa/botonomus/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Issues, https://github.com/Kuduxaaa/botonomus/issues
|
|
11
|
+
Keywords: browser automation,chrome,cdp,stealth,anti-detect,fingerprint,scraping,playwright
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Framework :: AsyncIO
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
23
|
+
Classifier: Topic :: Software Development :: Testing
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.12
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: filelock<4,>=3.16
|
|
29
|
+
Requires-Dist: psutil<8,>=6
|
|
30
|
+
Requires-Dist: tzdata>=2025.1; sys_platform == "win32"
|
|
31
|
+
Provides-Extra: playwright
|
|
32
|
+
Requires-Dist: playwright<2,>=1.60; extra == "playwright"
|
|
33
|
+
Provides-Extra: patchright
|
|
34
|
+
Requires-Dist: patchright<2,>=1.60; extra == "patchright"
|
|
35
|
+
Provides-Extra: docs
|
|
36
|
+
Requires-Dist: mkdocs<2,>=1.6; extra == "docs"
|
|
37
|
+
Requires-Dist: mkdocs-material<10,>=9.5; extra == "docs"
|
|
38
|
+
Requires-Dist: mkdocstrings[python]<2,>=0.26; extra == "docs"
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: playwright<2,>=1.60; extra == "dev"
|
|
41
|
+
Requires-Dist: patchright<2,>=1.60; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest<10,>=8; extra == "dev"
|
|
43
|
+
Requires-Dist: pytest-asyncio<2,>=0.25; extra == "dev"
|
|
44
|
+
Requires-Dist: ruff<1,>=0.11; extra == "dev"
|
|
45
|
+
Requires-Dist: mypy<2,>=1.15; extra == "dev"
|
|
46
|
+
Requires-Dist: types-psutil; extra == "dev"
|
|
47
|
+
Requires-Dist: build; extra == "dev"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# Botonomus
|
|
51
|
+
|
|
52
|
+
[](https://pypi.org/project/botonomus/)
|
|
53
|
+
[](https://github.com/Kuduxaaa/botonomus/actions/workflows/ci.yml)
|
|
54
|
+
[](pyproject.toml)
|
|
55
|
+
[](LICENSE)
|
|
56
|
+
|
|
57
|
+
Botonomus is an async Python SDK for browser automation that looks like an ordinary browser. It launches real Google Chrome, or the Botonomus Chromium build, as a normal process with its own profile, then attaches a native Chrome DevTools Protocol (CDP) driver that never sends `Runtime.enable` and does its DOM work in an isolated world that page scripts cannot see. Fingerprint personas (hardware, time zone, canvas/WebGL/audio noise) are applied inside Botonomus Chromium at the C++ level from command-line switches. Nothing is spoofed in JavaScript. Around that sit a bounded concurrency pool, persistent profiles with cross-process locks, authenticated proxies, geo consistency, human-like input and a reproducible detection runner.
|
|
58
|
+
|
|
59
|
+
Botonomus does not claim to be undetectable. The [measured results](#measured-results) below show what was checked, when and under which conditions.
|
|
60
|
+
|
|
61
|
+
## Install
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install botonomus
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Requires Python 3.12+ and an installed Google Chrome (stable channel). Windows is the most-tested platform; Linux servers are supported with a virtual display (see [Linux servers](#linux-servers)). No browser is downloaded automatically.
|
|
68
|
+
|
|
69
|
+
Optional drivers (the default `native` driver needs neither):
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install "botonomus[patchright]" # Patchright, a Playwright fork without Runtime.enable
|
|
73
|
+
pip install "botonomus[playwright]" # stock Playwright
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Quickstart
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import asyncio
|
|
80
|
+
from botonomus import Botonomus
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
async def main():
|
|
84
|
+
async with Botonomus(max_instances=5) as bot:
|
|
85
|
+
async with bot.open(profile="acct-01") as session:
|
|
86
|
+
await session.page.goto("https://example.com")
|
|
87
|
+
print(await session.page.title())
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
asyncio.run(main())
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The browser opens visibly, the profile in `.botonomus/profiles/acct-01` keeps its cookies and storage, and the browser closes when the `async with` block exits.
|
|
94
|
+
|
|
95
|
+
## Why Botonomus
|
|
96
|
+
|
|
97
|
+
A comparison of approaches, limited to properties that can be checked from the code or the vendors' public documentation. Closed anti-detect browsers vary; the column describes the common pattern.
|
|
98
|
+
|
|
99
|
+
| | Stock Playwright | Patchright | Camoufox | Closed anti-detect browsers | Botonomus |
|
|
100
|
+
|---|---|---|---|---|---|
|
|
101
|
+
| Browser engine | Bundled Chromium / Chrome for Testing, or system Chrome | Same as Playwright | Patched Firefox | Patched Chromium (usually) | Real Google Chrome, or Botonomus Chromium |
|
|
102
|
+
| `Runtime.enable` sent | Yes | No | n/a (Juggler protocol) | Varies | No |
|
|
103
|
+
| `navigator.webdriver` on default launch | `true` | `false` | `false` | `false` | `false` |
|
|
104
|
+
| Fingerprint changes applied in | n/a | n/a | C++ (Firefox) | Engine and/or injected JS | C++ (Botonomus Chromium only) |
|
|
105
|
+
| JavaScript fingerprint spoofing | No | No | No | Often | Never |
|
|
106
|
+
| Driver dependencies | Node.js driver process | Node.js driver process | Playwright | Vendor app / API | Python stdlib (native driver) |
|
|
107
|
+
| Concurrency pool, persistent locked profiles | Build it yourself | Build it yourself | Build it yourself | Usually, in a GUI | Built in |
|
|
108
|
+
| Source licence | Apache-2.0 | Apache-2.0 | MPL-2.0 | Proprietary | MIT (SDK) |
|
|
109
|
+
|
|
110
|
+
With stock Chrome, Botonomus offers no fingerprint diversity: every session presents the host's real hardware. Personas need Botonomus Chromium.
|
|
111
|
+
|
|
112
|
+
## Features
|
|
113
|
+
|
|
114
|
+
### Concurrency pool
|
|
115
|
+
|
|
116
|
+
`max_instances` caps simultaneous browsers. Extra `open()` requests wait; cancelling a waiting request does not consume capacity.
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
async with Botonomus(max_instances=10) as bot:
|
|
120
|
+
|
|
121
|
+
async def work(n: int) -> str:
|
|
122
|
+
async with bot.open(profile=f"worker-{n}") as session:
|
|
123
|
+
await session.page.goto("https://example.com")
|
|
124
|
+
return await session.page.title()
|
|
125
|
+
|
|
126
|
+
titles = await asyncio.gather(*(work(i) for i in range(40)))
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Each browser is a full process, so measure your machine (`botonomus benchmark`) before choosing 20, 40 or 80. A manager belongs to one event loop and can be entered once. Do not hold more nested sessions than the limit: the inner request would wait for the outer slot forever.
|
|
130
|
+
|
|
131
|
+
### Profiles
|
|
132
|
+
|
|
133
|
+
A profile is a directory under `BrowserConfig.profile_root` that keeps cookies, storage and history between runs. Names are 1-64 ASCII letters, digits, `_` or `-`, start with a letter or digit, and are lowercased. A cross-process file lock prevents two sessions, in any process, from using the same profile (`ProfileInUseError`).
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from pathlib import Path
|
|
137
|
+
from botonomus.profiles import list_profiles, remove_profile, warm_up
|
|
138
|
+
|
|
139
|
+
for info in list_profiles(Path(".botonomus/profiles")):
|
|
140
|
+
print(info.name, info.in_use, info.modified)
|
|
141
|
+
|
|
142
|
+
# Opt-in: browse common sites humanly so a fresh profile gains history.
|
|
143
|
+
report = await warm_up(session.page, duration=120)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Use a dedicated profile root, never your everyday Chrome profile. Profiles hold sensitive browsing state.
|
|
147
|
+
|
|
148
|
+
### Proxies
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from botonomus import BrowserConfig
|
|
152
|
+
|
|
153
|
+
config = BrowserConfig(proxy="http://user:pass@proxy.example:8080") # http, https or socks5
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
- Proxies without credentials go straight to Chrome's `--proxy-server`.
|
|
157
|
+
- With credentials, Botonomus starts a loopback SOCKS5 forwarder per browser and tunnels each connection through the upstream (HTTP `CONNECT` or SOCKS5 auth). Chrome never sees the credentials, no auth prompt is answered over CDP, and hostnames resolve at the proxy. Credentials never appear in command lines, `repr()` or logs.
|
|
158
|
+
- Any proxy also sets `--force-webrtc-ip-handling-policy=disable_non_proxied_udp`, so WebRTC cannot reveal the direct address.
|
|
159
|
+
|
|
160
|
+
Check a list of proxies (exit IP, country, data-centre flag) before use:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
botonomus proxy-check proxies.txt --parallel 16
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Geo consistency
|
|
167
|
+
|
|
168
|
+
`geoip=True` looks up the proxy's exit through the proxy (ip-api.com, then ipinfo.io over TLS), caches it per proxy for an hour, and aligns the browser locale and time zone with it before launch.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
config = BrowserConfig(proxy="socks5://user:pass@proxy.example:1080", geoip=True)
|
|
172
|
+
async with Botonomus(config=config) as bot, bot.open(profile="de-01") as session:
|
|
173
|
+
print(session.exit.country_code, session.exit.timezone)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- Locale comes from the exit country (`--lang`, `--accept-lang`) unless you set `locale`.
|
|
177
|
+
- Botonomus Chromium presents the exit's time zone with `--bn-timezone`.
|
|
178
|
+
- Stock Chrome always uses the host time zone. If its UTC offset differs from the exit's, the launch raises `GeoMismatchError` rather than ship a contradiction. Pass `allow_timezone_mismatch=True` to accept the risk.
|
|
179
|
+
- If every lookup fails, the launch raises `GeoLookupError`.
|
|
180
|
+
|
|
181
|
+
### Personas (Botonomus Chromium)
|
|
182
|
+
|
|
183
|
+
A persona is a seeded, internally consistent identity: `navigator.hardwareConcurrency`, `navigator.deviceMemory`, time zone, an optional WebGL vendor/renderer override, and a seed for canvas, WebGL readback and audio noise. Values follow weighted real-world distributions and never exceed the host's hardware. The C++ patches derive per-site noise keys from the seed and the top-level site, so a profile is stable per site and unlinkable across profiles.
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from botonomus import BrowserConfig
|
|
187
|
+
from botonomus.fingerprint import HostInfo, Persona
|
|
188
|
+
|
|
189
|
+
BrowserConfig(persona="auto") # default: stable seed per profile on Botonomus Chromium
|
|
190
|
+
BrowserConfig(persona="off") # no persona
|
|
191
|
+
BrowserConfig(persona=12345) # explicit 64-bit seed (requires Botonomus Chromium)
|
|
192
|
+
|
|
193
|
+
host = HostInfo(logical_cpus=16, memory_gb=32.0, platform="win32")
|
|
194
|
+
Persona.from_seed(42, host).to_switches()
|
|
195
|
+
# ('--bn-device-memory=16', '--bn-hardware-concurrency=12', '--bn-seed=000000000000002a')
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Personas require Botonomus Chromium, a separately licensed Chromium build. With stock Chrome, `persona="auto"` does nothing and an explicit seed or `Persona` raises `PersonaUnsupportedError`; there is no JavaScript fallback. Builds are installed with `botonomus.browser.install()`, which verifies an Ed25519-signed manifest and the archive's SHA-256 before unpacking (see [Personas](docs/guides/personas.md)). `botonomus install` does the same from the command line. The release host and signing key are not yet live, so installs cannot complete until the first Botonomus Chromium release.
|
|
199
|
+
|
|
200
|
+
### Linux servers
|
|
201
|
+
|
|
202
|
+
Headed Chrome on a virtual X display behaves like a desktop browser (real window and screen geometry, never "headless"), which is the usual way to run many visible browsers on a server. On Linux, when no `DISPLAY` is set, Botonomus starts one Xvfb screen (1920x1080) per manager and runs every headed browser on it (when neither `DISPLAY` nor `WAYLAND_DISPLAY` is set; headless sessions never use it):
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
sudo apt-get install -y xvfb # plus Google Chrome: https://www.google.com/chrome/
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
BrowserConfig() # virtual_display=None: automatic on Linux without DISPLAY
|
|
210
|
+
BrowserConfig(virtual_display=True) # always (Linux only)
|
|
211
|
+
BrowserConfig(virtual_display=False) # never
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
In containers Botonomus adds `--disable-dev-shm-usage` when `/dev/shm` is under 512 MB (Docker's default is 64 MB) and `--no-sandbox` only when running as root. Pages cannot see the first; the second makes Chrome show an "unsupported command-line flag" bar that shrinks the viewport, so run as a non-root user. Use a machine with a GPU for WebGL-sensitive sites: without one, WebGL is missing or software-rendered, which detection services treat as a server. Any local user can connect to the Xvfb display, so use it on single-user machines.
|
|
215
|
+
|
|
216
|
+
Every Chrome on one machine presents the same device fingerprint. Spread large fleets over several machines, or use Botonomus Chromium personas.
|
|
217
|
+
|
|
218
|
+
### Human-like input
|
|
219
|
+
|
|
220
|
+
`humanize=True` wraps `session.page` in a `HumanPage`: `click`, `fill`, `type`, `press`, `hover`, `scroll`, and the same methods on locators, move the pointer along eased Bézier paths with Fitts's-law timing, hold buttons briefly, and type key by key with a lognormal rhythm, occasional neighbouring-key slips that are corrected, and longer pauses after words and punctuation. Elements are waited on until visible, enabled and stable.
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from botonomus import BrowserConfig, HumanConfig
|
|
224
|
+
|
|
225
|
+
config = BrowserConfig(humanize=HumanConfig.preset("careful")) # or True, "default", "fast"
|
|
226
|
+
async with Botonomus(config=config) as bot, bot.open(profile="p1") as session:
|
|
227
|
+
page = session.page
|
|
228
|
+
await page.goto("https://example.com/login")
|
|
229
|
+
await page.fill("#email", "user@example.com")
|
|
230
|
+
await page.fill("#password", "correct horse", sensitive=True) # no slips in secrets
|
|
231
|
+
await page.press("Enter")
|
|
232
|
+
await page.raw.evaluate("document.title") # .raw bypasses humanizing
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
All events are trusted browser input sent through CDP `Input`; nothing is injected into the page. This shapes timing and trajectories only. Behavioural classifiers can still tell automation apart.
|
|
236
|
+
|
|
237
|
+
### Detection runner
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
botonomus detect --runs 3
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Visits 13 public bot-detection pages (deviceandbrowserinfo, sannysoft, browserscan, fingerprint, fingerprint-playground, creepjs, incolumitas, nowsecure, recaptcha-score, pixelscan, fingerprint-scan, rebrowser, turnstile), each `--runs` times with a fresh profile per run, and writes screenshots, page text and `report.json` with per-run verdicts, pass rates, error categories and the environment (date, OS, executable SHA-256, browser version, driver, proxy scheme only). Verdicts come only from per-site extractor scripts; anything else is `unknown`, and rate-limit or challenge pages are `blocked`. Add your own sites from Python with `botonomus.diagnostics.DetectionSite` and `run_detection`.
|
|
244
|
+
|
|
245
|
+
### Measuring
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
botonomus experiment examples/experiment.toml --proxies proxies.txt # A/B with 95% intervals
|
|
249
|
+
botonomus trace URL --output a.json && botonomus trace-diff a.json b.json # which values differ
|
|
250
|
+
botonomus consistency --browser botonomus --persona 12345 # local, no network
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
`experiment` interleaves browser configurations over the same sites with a fresh profile and the next proxy per visit, and reports pass rates with Wilson 95 % intervals. `trace` records which fingerprinting APIs a page reads and what they return (diagnostic only: the hooks are visible to the page). `consistency` checks readback stability and identity agreement across page, iframe and workers. See [Measuring](docs/guides/measuring.md).
|
|
254
|
+
|
|
255
|
+
## CLI
|
|
256
|
+
|
|
257
|
+
Installing the package adds `botonomus` (also `python -m botonomus.cli`).
|
|
258
|
+
|
|
259
|
+
| Command | Purpose |
|
|
260
|
+
|---|---|
|
|
261
|
+
| `botonomus info [--browser B] [--json]` | Versions, platform, discovered Chrome, installed Botonomus Chromium, which executable a launch would use, available drivers |
|
|
262
|
+
| `botonomus install [--version V] [--manifest-url URL] [--json]` | Download, verify and install Botonomus Chromium (progress on stderr) |
|
|
263
|
+
| `botonomus uninstall VERSION` / `botonomus binaries [--json]` | Remove / list installed Botonomus Chromium builds |
|
|
264
|
+
| `botonomus open --profile P [--url U]` | Visible session until Enter or Ctrl+C |
|
|
265
|
+
| `botonomus probe [--normal \| --baseline FILE \| --serve]` | Local probe snapshot, optionally compared with an ordinary launch |
|
|
266
|
+
| `botonomus detect [--sites ...] [--runs N] [--parallel N]` | Public detection pages, aggregated report |
|
|
267
|
+
| `botonomus experiment ARMS.toml [--runs N] [--proxies FILE] [--seed S]` | Interleaved A/B runs with Wilson 95 % intervals (`report.json`, `report.md`) |
|
|
268
|
+
| `botonomus trace URL --output FILE` / `botonomus trace-diff A B` | Fingerprinting API reads of a page, and the values two browsers report differently |
|
|
269
|
+
| `botonomus consistency` | Local readback and cross-context identity checks; exit 1 on any failure |
|
|
270
|
+
| `botonomus proxy-check FILE [--parallel N] [--timeout S]` | Exit IP and location per proxy; never prints credentials |
|
|
271
|
+
| `botonomus profiles list \| remove NAME [--root DIR]` | List profiles; remove refuses profiles in use |
|
|
272
|
+
| `botonomus profiles warmup NAME [--duration S] [--sites URL ...]` | Browse common sites humanly so a profile accumulates history |
|
|
273
|
+
| `botonomus benchmark --levels 1,2,5` | Held-open concurrency on a local page |
|
|
274
|
+
|
|
275
|
+
`open`, `probe`, `detect`, `trace`, `consistency`, `profiles warmup` and `benchmark` also accept `--executable`, `--browser {auto,botonomus,chrome}`, `--persona {auto,off,SEED}`, `--proxy`, `--geoip`, `--timezone ZONE`, `--allow-timezone-mismatch`, `--locale`, `--headless` and `--driver`; `open`, `detect` and `profiles warmup` also accept `--humanize {off,default,careful,fast}`. Exit codes: 0 success, 1 runtime failure, 2 usage or configuration error.
|
|
276
|
+
|
|
277
|
+
## Configuration reference
|
|
278
|
+
|
|
279
|
+
`BrowserConfig` is a frozen dataclass validated on construction (`ConfigurationError`). Pass it as `Botonomus(config=...)`.
|
|
280
|
+
|
|
281
|
+
| Field | Type | Default | Meaning |
|
|
282
|
+
|---|---|---|---|
|
|
283
|
+
| `profile_root` | `Path` | `.botonomus/profiles` | Directory with one subdirectory per profile; resolved to an absolute path |
|
|
284
|
+
| `executable_path` | `Path \| None` | `None` | Browser executable; `None` chooses according to `browser` |
|
|
285
|
+
| `headless` | `bool` | `False` | Run without a window (visible is the validated mode) |
|
|
286
|
+
| `launch_timeout` | `float` | `30.0` | Seconds for the process to start and accept CDP |
|
|
287
|
+
| `close_timeout` | `float` | `10.0` | Seconds for a graceful close before termination |
|
|
288
|
+
| `locale` | `str \| None` | `None` | BCP 47 tag applied with `--lang` and `--accept-lang` |
|
|
289
|
+
| `proxy` | `str \| None` | `None` | `http`, `https` or `socks5` URL, credentials allowed; hidden from `repr` |
|
|
290
|
+
| `extra_args` | `tuple[str, ...]` | `()` | Extra `--flag[=value]` for every session; owned flags are rejected |
|
|
291
|
+
| `driver` | `"native" \| "patchright" \| "playwright"` | `"native"` | Page driver attached over CDP |
|
|
292
|
+
| `render_when_occluded` | `bool` | `True` | Keep rendering covered windows (avoids stalled input on Windows) |
|
|
293
|
+
| `browser` | `"auto" \| "botonomus" \| "chrome"` | `"auto"` | With no `executable_path`: prefer Botonomus Chromium, require it, or use Chrome |
|
|
294
|
+
| `persona` | `"auto" \| "off" \| int \| Persona` | `"auto"` | Fingerprint identity (Botonomus Chromium) |
|
|
295
|
+
| `geoip` | `bool` | `False` | Align locale and time zone with the proxy exit; requires `proxy` |
|
|
296
|
+
| `timezone` | `str \| None` | `None` | IANA zone to present; overrides the exit's zone |
|
|
297
|
+
| `allow_timezone_mismatch` | `bool` | `False` | Launch Chrome even if the host zone differs from the wanted zone |
|
|
298
|
+
| `humanize` | `bool \| HumanConfig` | `False` | Wrap `session.page` in `HumanPage` |
|
|
299
|
+
| `virtual_display` | `bool \| None` | `None` | Xvfb for headed browsers on Linux: `None` when no display is set, `True` always, `False` never |
|
|
300
|
+
| `persona_switches` | `tuple[str, ...]` | `()` | Internal: filled in per launch by the manager; leave empty |
|
|
301
|
+
|
|
302
|
+
Flags Botonomus owns and rejects in `extra_args` or `open(..., args=...)`: `--user-data-dir`, `--remote-debugging-port`, `--remote-debugging-address`, `--remote-debugging-pipe`, `--headless`, `--enable-automation`, `--proxy-server`, `--lang`, `--accept-lang`, `--user-agent`, and every `--bn-*` persona switch.
|
|
303
|
+
|
|
304
|
+
## Errors
|
|
305
|
+
|
|
306
|
+
Every SDK error derives from `botonomus.BotonomusError`; the low-level cause is kept as `__cause__`, and messages never contain credentials, cookies, page contents or URLs.
|
|
307
|
+
|
|
308
|
+
| Error | Raised when |
|
|
309
|
+
|---|---|
|
|
310
|
+
| `ConfigurationError` (also a `ValueError`) | Invalid configuration, argument or profile name |
|
|
311
|
+
| `PersonaUnsupportedError` (a `ConfigurationError`) | Explicit persona requested on stock Chrome |
|
|
312
|
+
| `BrowserUnavailableError` | No browser executable found |
|
|
313
|
+
| `BinaryNotInstalledError` (a `BrowserUnavailableError`) | `browser="botonomus"` but no build installed |
|
|
314
|
+
| `BinaryDownloadError` / `BinaryVerificationError` | Botonomus Chromium download or signature/hash check failed |
|
|
315
|
+
| `ProfileInUseError` | Another session, in any process, holds the profile |
|
|
316
|
+
| `ManagerClosedError` | The manager is not open for new sessions |
|
|
317
|
+
| `BrowserStartupError` | Process, driver, context or page could not start |
|
|
318
|
+
| `BrowserCleanupError` | Shutdown could not be confirmed; profile and slot stay reserved |
|
|
319
|
+
| `GeoLookupError` | `geoip=True` and every exit lookup failed |
|
|
320
|
+
| `GeoMismatchError` | Chrome cannot present the required time zone |
|
|
321
|
+
|
|
322
|
+
`PersonaUnsupportedError`, `GeoLookupError` and `GeoMismatchError` are imported from `botonomus.errors`; the others are also exported from `botonomus`. The native driver's own page errors (`botonomus.cdp.TimeoutError_`, `NavigationError`, `EvaluationError`, `ProtocolError`) are not `BotonomusError` subclasses; `TimeoutError_` subclasses the built-in `TimeoutError`.
|
|
323
|
+
|
|
324
|
+
## Measured results
|
|
325
|
+
|
|
326
|
+
Measured 2026-10-04, Windows 11, Google Chrome 154 stable through the `native` driver, `--persona off`, fresh profile per visit, rotating datacenter proxies (one per visit), with `botonomus experiment` and `botonomus consistency`:
|
|
327
|
+
|
|
328
|
+
| Check | Result |
|
|
329
|
+
|---|---|
|
|
330
|
+
| demo.fingerprint.com scraping demo (bot and tampering decision) | **3/3 passed** (n=3; rate-limited visits reported as `blocked`, not counted) |
|
|
331
|
+
| `botonomus consistency` (readback stability, page/iframe/worker/shared/service-worker agreement, media, voices, geometry) | **all 9 checks pass** |
|
|
332
|
+
| TLS JA4 and HTTP/2 fingerprint | identical to a manual Chrome launch |
|
|
333
|
+
|
|
334
|
+
Unproxied repeats of the same site from one IP were rate-limited within minutes; earlier tooling misread those pages as passes, which is why `blocked` exists.
|
|
335
|
+
|
|
336
|
+
Measured 2026-10-03, Windows 11, Google Chrome 154 stable, visible window, residential ISP, fresh profile:
|
|
337
|
+
|
|
338
|
+
| Check | Ordinary Chrome (no automation) | Botonomus `native` | `playwright` driver |
|
|
339
|
+
|---|---|---|---|
|
|
340
|
+
| Local probe, all observations | - | identical to ordinary launch | - |
|
|
341
|
+
| deviceandbrowserinfo.com true flags | 0 | **0** | 4 (`isBot`, CDP, CDP-in-worker, timing) |
|
|
342
|
+
| bot.sannysoft.com failed rows | - | **0** | timed out |
|
|
343
|
+
| browserscan.net bot detection | - | **Normal** | - |
|
|
344
|
+
| FingerprintJS scraping demo | - | **data served** | - |
|
|
345
|
+
| Cloudflare challenge (nowsecure.nl) | - | **passed** | - |
|
|
346
|
+
| reCAPTCHA v3 (antcpt.com) | 0.1 | 0.1 | - |
|
|
347
|
+
| incolumitas legacy `WEBDRIVER` | FAIL | FAIL | FAIL |
|
|
348
|
+
|
|
349
|
+
On the last two rows ordinary Chrome scores the same. The legacy test flags `'webdriver' in navigator`, which is true in every current Chrome. reCAPTCHA v3 scored this IP and a history-free profile at 0.1 with or without automation, so improving it is a matter of IP reputation and profile age, not the browser.
|
|
350
|
+
|
|
351
|
+
Earlier local measurement on the same machine with Chromium 153.0.8010.12: default Playwright launch reported `navigator.webdriver` `true`, Botonomus `false`; an ordinary launch and a Botonomus launch matched on every captured probe field except viewport height (929x917 ordinary, 929x861 attached).
|
|
352
|
+
|
|
353
|
+
Load: the initial `botonomus benchmark` run completed 1 session in 1.125 s and 2 simultaneously active sessions in 3.031 s, including local navigation, with zero failures. Levels 10-80 have not been benchmarked, and no capacity or throughput figure is promised.
|
|
354
|
+
|
|
355
|
+
These results apply to one machine, network, browser build and date. They are not a guarantee that any site will treat a session as human: network, account and behavioural signals remain. Reproduce them on your own setup with `botonomus detect` and `botonomus probe --normal`. Measurements of Botonomus Chromium will be published separately with the same method.
|
|
356
|
+
|
|
357
|
+
## Responsible use
|
|
358
|
+
|
|
359
|
+
Botonomus is for authorised work: testing and monitoring your own sites, QA, research, accessibility checks, and automation the target site permits. Respect each site's terms of service, robots directives and rate limits, and the laws that apply to you, including data-protection and computer-misuse laws. Using Botonomus for credential stuffing, account takeover, fake account creation, ad or payment fraud, ticket scalping, evading bans, or any other abuse is prohibited. You are responsible for how you use it.
|
|
360
|
+
|
|
361
|
+
## Documentation and project
|
|
362
|
+
|
|
363
|
+
- Full documentation: the MkDocs site under [`docs/`](docs/README.md) (`pip install -e ".[docs]"`, then `mkdocs serve`).
|
|
364
|
+
- [CHANGELOG](CHANGELOG.md), [CONTRIBUTING](CONTRIBUTING.md), [SECURITY](SECURITY.md), [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md).
|
|
365
|
+
|
|
366
|
+
## Licence
|
|
367
|
+
|
|
368
|
+
The `botonomus` Python SDK is released under the [MIT licence](LICENSE). The Botonomus Chromium binary is distributed separately under its own licence and is not covered by the MIT licence of this repository.
|