pydoll-python 2.21.0__tar.gz → 2.21.1__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.
Files changed (107) hide show
  1. pydoll_python-2.21.1/PKG-INFO +380 -0
  2. pydoll_python-2.21.1/README.md +359 -0
  3. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/constants.py +115 -0
  4. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/elements/web_element.py +19 -7
  5. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/interactions/keyboard.py +25 -2
  6. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/interactions/scroll.py +4 -1
  7. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pyproject.toml +1 -1
  8. pydoll_python-2.21.0/PKG-INFO +0 -446
  9. pydoll_python-2.21.0/README.md +0 -425
  10. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/LICENSE +0 -0
  11. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/__init__.py +0 -0
  12. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/__init__.py +0 -0
  13. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/chromium/__init__.py +0 -0
  14. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/chromium/base.py +0 -0
  15. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/chromium/chrome.py +0 -0
  16. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/chromium/edge.py +0 -0
  17. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/interfaces.py +0 -0
  18. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/managers/__init__.py +0 -0
  19. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/managers/browser_options_manager.py +0 -0
  20. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/managers/browser_process_manager.py +0 -0
  21. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/managers/proxy_manager.py +0 -0
  22. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/managers/temp_dir_manager.py +0 -0
  23. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/options.py +0 -0
  24. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/requests/__init__.py +0 -0
  25. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/requests/har_recorder.py +0 -0
  26. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/requests/request.py +0 -0
  27. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/requests/response.py +0 -0
  28. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/browser/tab.py +0 -0
  29. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/commands/__init__.py +0 -0
  30. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/commands/browser_commands.py +0 -0
  31. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/commands/dom_commands.py +0 -0
  32. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/commands/emulation_commands.py +0 -0
  33. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/commands/fetch_commands.py +0 -0
  34. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/commands/input_commands.py +0 -0
  35. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/commands/network_commands.py +0 -0
  36. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/commands/page_commands.py +0 -0
  37. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/commands/runtime_commands.py +0 -0
  38. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/commands/storage_commands.py +0 -0
  39. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/commands/target_commands.py +0 -0
  40. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/connection/__init__.py +0 -0
  41. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/connection/connection_handler.py +0 -0
  42. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/connection/managers/__init__.py +0 -0
  43. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/connection/managers/commands_manager.py +0 -0
  44. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/connection/managers/events_manager.py +0 -0
  45. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/decorators.py +0 -0
  46. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/elements/__init__.py +0 -0
  47. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/elements/mixins/__init__.py +0 -0
  48. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/elements/mixins/find_elements_mixin.py +0 -0
  49. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/elements/shadow_root.py +0 -0
  50. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/elements/utils/__init__.py +0 -0
  51. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/elements/utils/selector_parser.py +0 -0
  52. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/exceptions.py +0 -0
  53. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/interactions/__init__.py +0 -0
  54. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/interactions/iframe.py +0 -0
  55. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/interactions/mouse.py +0 -0
  56. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/interactions/utils.py +0 -0
  57. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/__init__.py +0 -0
  58. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/base.py +0 -0
  59. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/browser/__init__.py +0 -0
  60. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/browser/events.py +0 -0
  61. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/browser/methods.py +0 -0
  62. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/browser/types.py +0 -0
  63. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/debugger/types.py +0 -0
  64. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/dom/__init__.py +0 -0
  65. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/dom/events.py +0 -0
  66. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/dom/methods.py +0 -0
  67. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/dom/types.py +0 -0
  68. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/emulation/__init__.py +0 -0
  69. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/emulation/methods.py +0 -0
  70. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/emulation/types.py +0 -0
  71. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/fetch/__init__.py +0 -0
  72. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/fetch/events.py +0 -0
  73. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/fetch/methods.py +0 -0
  74. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/fetch/types.py +0 -0
  75. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/input/__init__.py +0 -0
  76. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/input/events.py +0 -0
  77. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/input/methods.py +0 -0
  78. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/input/types.py +0 -0
  79. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/io/types.py +0 -0
  80. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/network/__init__.py +0 -0
  81. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/network/events.py +0 -0
  82. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/network/har_types.py +0 -0
  83. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/network/methods.py +0 -0
  84. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/network/types.py +0 -0
  85. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/page/__init__.py +0 -0
  86. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/page/events.py +0 -0
  87. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/page/methods.py +0 -0
  88. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/page/types.py +0 -0
  89. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/runtime/__init__.py +0 -0
  90. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/runtime/events.py +0 -0
  91. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/runtime/methods.py +0 -0
  92. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/runtime/types.py +0 -0
  93. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/security/types.py +0 -0
  94. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/storage/__init__.py +0 -0
  95. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/storage/events.py +0 -0
  96. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/storage/methods.py +0 -0
  97. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/storage/types.py +0 -0
  98. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/target/__init__.py +0 -0
  99. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/target/events.py +0 -0
  100. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/target/methods.py +0 -0
  101. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/protocol/target/types.py +0 -0
  102. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/py.typed +0 -0
  103. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/utils/__init__.py +0 -0
  104. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/utils/bundle.py +0 -0
  105. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/utils/general.py +0 -0
  106. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/utils/socks5_proxy_forwarder.py +0 -0
  107. {pydoll_python-2.21.0 → pydoll_python-2.21.1}/pydoll/utils/user_agent_parser.py +0 -0
@@ -0,0 +1,380 @@
1
+ Metadata-Version: 2.4
2
+ Name: pydoll-python
3
+ Version: 2.21.1
4
+ Summary: Pydoll is a library for automating chromium-based browsers without a WebDriver, offering realistic interactions.
5
+ License-File: LICENSE
6
+ Author: Thalison Fernandes
7
+ Author-email: thalissfernandes99@gmail.com
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Dist: aiofiles (>=25.1.0,<26.0.0)
16
+ Requires-Dist: aiohttp (>=3.9.5,<4.0.0)
17
+ Requires-Dist: typing_extensions (>=4.14.0,<5.0.0)
18
+ Requires-Dist: websockets (>=14,<15)
19
+ Description-Content-Type: text/markdown
20
+
21
+ <p align="center">
22
+ <img src="https://github.com/user-attachments/assets/2c380638-b04a-4b04-b1c8-2958e4237a94" alt="Pydoll Logo" /> <br>
23
+ </p>
24
+ <p align="center">Async-native, fully typed, built for evasion and performance.</p>
25
+
26
+ <p align="center">
27
+ <a href="https://github.com/autoscrape-labs/pydoll/stargazers"><img src="https://img.shields.io/github/stars/autoscrape-labs/pydoll?style=social"></a>
28
+ <a href="https://codecov.io/gh/autoscrape-labs/pydoll" >
29
+ <img src="https://codecov.io/gh/autoscrape-labs/pydoll/graph/badge.svg?token=40I938OGM9"/>
30
+ </a>
31
+ <img src="https://github.com/autoscrape-labs/pydoll/actions/workflows/tests.yml/badge.svg" alt="Tests">
32
+ <img src="https://github.com/autoscrape-labs/pydoll/actions/workflows/ruff-ci.yml/badge.svg" alt="Ruff CI">
33
+ <img src="https://github.com/autoscrape-labs/pydoll/actions/workflows/mypy.yml/badge.svg" alt="MyPy CI">
34
+ <img src="https://img.shields.io/badge/python-%3E%3D3.10-blue" alt="Python >= 3.10">
35
+ <a href="https://deepwiki.com/autoscrape-labs/pydoll"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
36
+ </p>
37
+
38
+ <p align="center">
39
+ <a href="https://pydoll.tech/">Documentation</a> &middot;
40
+ <a href="#getting-started">Getting Started</a> &middot;
41
+ <a href="#features">Features</a> &middot;
42
+ <a href="#support">Support</a>
43
+ </p>
44
+
45
+ Pydoll automates Chromium-based browsers (Chrome, Edge) by connecting directly to the Chrome DevTools Protocol over WebSocket. No WebDriver binary, no `navigator.webdriver` flag, no compatibility issues.
46
+
47
+ It combines a high-level API for common tasks with low-level CDP access for fine-grained control over network, fingerprinting, and browser behavior. The entire codebase is async-native and fully type-checked with mypy.
48
+
49
+ ### Sponsors
50
+
51
+ <a href="https://www.thordata.com/?ls=github&lk=pydoll">
52
+ <img alt="Thordata" src="public/images/thordata.png" />
53
+ </a>
54
+
55
+ Pydoll is proudly sponsored by **[Thordata](https://www.thordata.com/?ls=github&lk=pydoll)**: a residential proxy network built for serious web scraping and automation. With **190+ real residential and ISP locations**, fully encrypted connections, and infrastructure optimized for high-performance workflows, Thordata is an excellent choice for scaling your Pydoll automations.
56
+
57
+ **[Sign up through our link](https://www.thordata.com/?ls=github&lk=pydoll)** to support the project and get **1GB free** to get started.
58
+
59
+ ---
60
+
61
+ <a href="https://dashboard.capsolver.com/passport/register?inviteCode=WPhTbOsbXEpc">
62
+ <img alt="CapSolver" src="public/images/capsolver.jpeg" />
63
+ </a>
64
+
65
+ Pydoll excels at behavioral evasion, but it doesn't solve captchas. That's where **[CapSolver](https://dashboard.capsolver.com/passport/register?inviteCode=WPhTbOsbXEpc)** comes in. An AI-powered service that handles reCAPTCHA, Cloudflare challenges, and more, seamlessly integrating with your automation workflows.
66
+
67
+ **[Register with our invite code](https://dashboard.capsolver.com/passport/register?inviteCode=WPhTbOsbXEpc)** and use code **PYDOLL** to get an extra **6% balance bonus**.
68
+
69
+ ---
70
+
71
+ ### Why Pydoll
72
+
73
+ - **Stealth-first**: Human-like mouse movement, realistic typing, and granular [browser preference](https://pydoll.tech/docs/features/configuration/browser-preferences/) control for fingerprint management.
74
+ - **Async and typed**: Built on `asyncio` from the ground up, 100% type-checked with `mypy`. Full IDE autocompletion and static error checking.
75
+ - **Network control**: [Intercept](https://pydoll.tech/docs/features/network/interception/) requests to block ads/trackers, [monitor](https://pydoll.tech/docs/features/network/monitoring/) traffic for API discovery, and make [authenticated HTTP requests](https://pydoll.tech/docs/features/network/http-requests/) that inherit the browser session.
76
+ - **Shadow DOM and iframes**: Full support for [shadow roots](https://pydoll.tech/docs/deep-dive/architecture/shadow-dom/) (including closed) and cross-origin iframes. Discover, query, and interact with elements inside them using the same API.
77
+ - **Ergonomic API**: `tab.find()` for most cases, `tab.query()` for complex [CSS/XPath selectors](https://pydoll.tech/docs/deep-dive/guides/selectors-guide/).
78
+
79
+ ## Installation
80
+
81
+ ```bash
82
+ pip install pydoll-python
83
+ ```
84
+
85
+ No WebDriver binaries or external dependencies required.
86
+
87
+ ## What's New
88
+
89
+ <details>
90
+ <summary><b>HAR Network Recording</b></summary>
91
+ <br>
92
+
93
+ Record network activity during a browser session and export as HAR 1.2. Replay recorded requests to reproduce exact API sequences.
94
+
95
+ ```python
96
+ from pydoll.browser.chromium import Chrome
97
+
98
+ async with Chrome() as browser:
99
+ tab = await browser.start()
100
+
101
+ async with tab.request.record() as capture:
102
+ await tab.go_to('https://example.com')
103
+
104
+ capture.save('flow.har')
105
+ print(f'Captured {len(capture.entries)} requests')
106
+
107
+ responses = await tab.request.replay('flow.har')
108
+ ```
109
+
110
+ Filter by resource type:
111
+
112
+ ```python
113
+ from pydoll.protocol.network.types import ResourceType
114
+
115
+ async with tab.request.record(
116
+ resource_types=[ResourceType.FETCH, ResourceType.XHR]
117
+ ) as capture:
118
+ await tab.go_to('https://example.com')
119
+ ```
120
+
121
+ [HAR Recording Docs](https://pydoll.tech/docs/features/network/network-recording/)
122
+ </details>
123
+
124
+ <details>
125
+ <summary><b>Page Bundles</b></summary>
126
+ <br>
127
+
128
+ Save the current page and all its assets (CSS, JS, images, fonts) as a `.zip` bundle for offline viewing. Optionally inline everything into a single HTML file.
129
+
130
+ ```python
131
+ await tab.save_bundle('page.zip')
132
+ await tab.save_bundle('page-inline.zip', inline_assets=True)
133
+ ```
134
+
135
+ [Screenshots, PDFs & Bundles Docs](https://pydoll.tech/docs/features/automation/screenshots-and-pdfs/)
136
+ </details>
137
+
138
+ <details>
139
+ <summary><b>Shadow DOM Support</b></summary>
140
+ <br>
141
+
142
+ Full Shadow DOM support, including closed shadow roots. Because Pydoll operates at the CDP level (below JavaScript), the `closed` mode restriction doesn't apply.
143
+
144
+ ```python
145
+ shadow = await element.get_shadow_root()
146
+ button = await shadow.query('.internal-btn')
147
+ await button.click()
148
+
149
+ # Discover all shadow roots on the page
150
+ shadow_roots = await tab.find_shadow_roots()
151
+ for sr in shadow_roots:
152
+ checkbox = await sr.query('input[type="checkbox"]', raise_exc=False)
153
+ if checkbox:
154
+ await checkbox.click()
155
+ ```
156
+
157
+ Highlights:
158
+ - Closed shadow roots work without workarounds
159
+ - `find_shadow_roots()` discovers every shadow root on the page
160
+ - `timeout` parameter for polling until shadow roots appear
161
+ - `deep=True` traverses cross-origin iframes (OOPIFs)
162
+ - Standard `find()`, `query()`, `click()` API inside shadow roots
163
+
164
+ ```python
165
+ # Cloudflare Turnstile inside a cross-origin iframe
166
+ shadow_roots = await tab.find_shadow_roots(deep=True, timeout=10)
167
+ for sr in shadow_roots:
168
+ checkbox = await sr.query('input[type="checkbox"]', raise_exc=False)
169
+ if checkbox:
170
+ await checkbox.click()
171
+ ```
172
+
173
+ [Shadow DOM Docs](https://pydoll.tech/docs/deep-dive/architecture/shadow-dom/)
174
+ </details>
175
+
176
+ <details>
177
+ <summary><b>Humanized Mouse Movement</b></summary>
178
+ <br>
179
+
180
+ Mouse operations produce human-like cursor movement by default:
181
+
182
+ - **Bezier curve paths** with asymmetric control points
183
+ - **Fitts's Law timing**: duration scales with distance
184
+ - **Minimum-jerk velocity**: bell-shaped speed profile
185
+ - **Physiological tremor**: Gaussian noise scaled with velocity
186
+ - **Overshoot correction**: ~70% chance on fast movements, then corrects back
187
+
188
+ ```python
189
+ await tab.mouse.move(500, 300)
190
+ await tab.mouse.click(500, 300)
191
+ await tab.mouse.drag(100, 200, 500, 400)
192
+
193
+ button = await tab.find(id='submit')
194
+ await button.click()
195
+
196
+ # Opt out when speed matters
197
+ await tab.mouse.click(500, 300, humanize=False)
198
+ ```
199
+
200
+ [Mouse Control Docs](https://pydoll.tech/docs/features/automation/mouse-control/)
201
+ </details>
202
+
203
+ ## Getting Started
204
+
205
+ ```python
206
+ import asyncio
207
+ from pydoll.browser import Chrome
208
+ from pydoll.constants import Key
209
+
210
+ async def google_search(query: str):
211
+ async with Chrome() as browser:
212
+ tab = await browser.start()
213
+ await tab.go_to('https://www.google.com')
214
+
215
+ search_box = await tab.find(tag_name='textarea', name='q')
216
+ await search_box.insert_text(query)
217
+ await tab.keyboard.press(Key.ENTER)
218
+
219
+ first_result = await tab.find(
220
+ tag_name='h3',
221
+ text='autoscrape-labs/pydoll',
222
+ timeout=10,
223
+ )
224
+ await first_result.click()
225
+
226
+ await tab.find(id='repository-container-header', timeout=10)
227
+ print(f"Page loaded: {await tab.title}")
228
+
229
+ asyncio.run(google_search('pydoll site:github.com'))
230
+ ```
231
+
232
+ ## Features
233
+
234
+ <details>
235
+ <summary><b>Hybrid Automation (UI + API)</b></summary>
236
+ <br>
237
+
238
+ Use UI automation to pass login flows (CAPTCHAs, JS challenges), then switch to `tab.request` for fast API calls that inherit the full browser session: cookies, headers, and all.
239
+
240
+ ```python
241
+ # Log in via UI
242
+ await tab.go_to('https://my-site.com/login')
243
+ await (await tab.find(id='username')).type_text('user')
244
+ await (await tab.find(id='password')).type_text('pass123')
245
+ await (await tab.find(id='login-btn')).click()
246
+
247
+ # Make authenticated API calls using the browser session
248
+ response = await tab.request.get('https://my-site.com/api/user/profile')
249
+ user_data = response.json()
250
+ ```
251
+ [Hybrid Automation Docs](https://pydoll.tech/docs/features/network/http-requests/)
252
+ </details>
253
+
254
+ <details>
255
+ <summary><b>Network Interception and Monitoring</b></summary>
256
+ <br>
257
+
258
+ Monitor traffic for API discovery or intercept requests to block ads, trackers, and unnecessary resources.
259
+
260
+ ```python
261
+ import asyncio
262
+ from pydoll.browser.chromium import Chrome
263
+ from pydoll.protocol.fetch.events import FetchEvent, RequestPausedEvent
264
+ from pydoll.protocol.network.types import ErrorReason
265
+
266
+ async def block_images():
267
+ async with Chrome() as browser:
268
+ tab = await browser.start()
269
+
270
+ async def block_resource(event: RequestPausedEvent):
271
+ request_id = event['params']['requestId']
272
+ resource_type = event['params']['resourceType']
273
+
274
+ if resource_type in ['Image', 'Stylesheet']:
275
+ await tab.fail_request(request_id, ErrorReason.BLOCKED_BY_CLIENT)
276
+ else:
277
+ await tab.continue_request(request_id)
278
+
279
+ await tab.enable_fetch_events()
280
+ await tab.on(FetchEvent.REQUEST_PAUSED, block_resource)
281
+
282
+ await tab.go_to('https://example.com')
283
+ await asyncio.sleep(3)
284
+ await tab.disable_fetch_events()
285
+
286
+ asyncio.run(block_images())
287
+ ```
288
+ [Network Monitoring](https://pydoll.tech/docs/features/network/monitoring/) | [Request Interception](https://pydoll.tech/docs/features/network/interception/)
289
+ </details>
290
+
291
+ <details>
292
+ <summary><b>Browser Fingerprint Control</b></summary>
293
+ <br>
294
+
295
+ Granular control over [browser preferences](https://pydoll.tech/docs/features/configuration/browser-preferences/): hundreds of internal Chrome settings for building consistent fingerprints.
296
+
297
+ ```python
298
+ options = ChromiumOptions()
299
+
300
+ options.browser_preferences = {
301
+ 'profile': {
302
+ 'default_content_setting_values': {
303
+ 'notifications': 2,
304
+ 'geolocation': 2,
305
+ },
306
+ 'password_manager_enabled': False
307
+ },
308
+ 'intl': {
309
+ 'accept_languages': 'en-US,en',
310
+ },
311
+ 'browser': {
312
+ 'check_default_browser': False,
313
+ }
314
+ }
315
+ ```
316
+ [Browser Preferences Guide](https://pydoll.tech/docs/features/configuration/browser-preferences/)
317
+ </details>
318
+
319
+ <details>
320
+ <summary><b>Concurrency, Contexts and Remote Connections</b></summary>
321
+ <br>
322
+
323
+ Manage [multiple tabs](https://pydoll.tech/docs/features/browser-management/tabs/) and [browser contexts](https://pydoll.tech/docs/features/browser-management/contexts/) (isolated sessions) concurrently. Connect to browsers running in Docker or remote servers.
324
+
325
+ ```python
326
+ async def scrape_page(url, tab):
327
+ await tab.go_to(url)
328
+ return await tab.title
329
+
330
+ async def concurrent_scraping():
331
+ async with Chrome() as browser:
332
+ tab_google = await browser.start()
333
+ tab_ddg = await browser.new_tab()
334
+
335
+ results = await asyncio.gather(
336
+ scrape_page('https://google.com/', tab_google),
337
+ scrape_page('https://duckduckgo.com/', tab_ddg)
338
+ )
339
+ print(results)
340
+ ```
341
+ [Multi-Tab Management](https://pydoll.tech/docs/features/browser-management/tabs/) | [Remote Connections](https://pydoll.tech/docs/features/advanced/remote-connections/)
342
+ </details>
343
+
344
+ <details>
345
+ <summary><b>Retry Decorator</b></summary>
346
+ <br>
347
+
348
+ The `@retry` decorator supports custom recovery logic between attempts (e.g., refreshing the page, rotating proxies) and exponential backoff.
349
+
350
+ ```python
351
+ from pydoll.decorators import retry
352
+ from pydoll.exceptions import ElementNotFound, NetworkError
353
+
354
+ @retry(
355
+ max_retries=3,
356
+ exceptions=[ElementNotFound, NetworkError],
357
+ on_retry=my_recovery_function,
358
+ exponential_backoff=True
359
+ )
360
+ async def scrape_product(self, url: str):
361
+ # scraping logic
362
+ ...
363
+ ```
364
+ [Retry Decorator Docs](https://pydoll.tech/docs/features/advanced/decorators/)
365
+ </details>
366
+
367
+ ---
368
+
369
+ ## Contributing
370
+
371
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
372
+
373
+ ## Support
374
+
375
+ If you find Pydoll useful, consider [sponsoring the project on GitHub](https://github.com/sponsors/thalissonvs).
376
+
377
+ ## License
378
+
379
+ [MIT License](LICENSE)
380
+