pydoll-python 2.16.0__tar.gz → 2.17.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.
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/PKG-INFO +93 -26
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/README.md +92 -25
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/tab.py +374 -28
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/elements/mixins/find_elements_mixin.py +23 -3
- pydoll_python-2.17.0/pydoll/elements/shadow_root.py +89 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/elements/web_element.py +80 -5
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/exceptions.py +6 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pyproject.toml +1 -1
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/LICENSE +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/chromium/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/chromium/base.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/chromium/chrome.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/chromium/edge.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/interfaces.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/managers/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/managers/browser_options_manager.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/managers/browser_process_manager.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/managers/proxy_manager.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/managers/temp_dir_manager.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/options.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/requests/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/requests/request.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/browser/requests/response.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/commands/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/commands/browser_commands.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/commands/dom_commands.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/commands/fetch_commands.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/commands/input_commands.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/commands/network_commands.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/commands/page_commands.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/commands/runtime_commands.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/commands/storage_commands.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/commands/target_commands.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/connection/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/connection/connection_handler.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/connection/managers/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/connection/managers/commands_manager.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/connection/managers/events_manager.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/constants.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/decorators.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/elements/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/elements/mixins/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/interactions/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/interactions/iframe.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/interactions/keyboard.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/interactions/scroll.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/base.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/browser/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/browser/events.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/browser/methods.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/browser/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/debugger/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/dom/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/dom/events.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/dom/methods.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/dom/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/emulation/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/fetch/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/fetch/events.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/fetch/methods.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/fetch/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/input/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/input/events.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/input/methods.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/input/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/io/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/network/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/network/events.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/network/methods.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/network/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/page/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/page/events.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/page/methods.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/page/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/runtime/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/runtime/events.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/runtime/methods.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/runtime/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/security/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/storage/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/storage/events.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/storage/methods.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/storage/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/target/__init__.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/target/events.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/target/methods.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/protocol/target/types.py +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/py.typed +0 -0
- {pydoll_python-2.16.0 → pydoll_python-2.17.0}/pydoll/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydoll-python
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.17.0
|
|
4
4
|
Summary: Pydoll is a library for automating chromium-based browsers without a WebDriver, offering realistic interactions.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: Thalison Fernandes
|
|
@@ -84,6 +84,73 @@ That's it. No `webdrivers`. No external dependencies.
|
|
|
84
84
|
|
|
85
85
|
## 🆕 What's New
|
|
86
86
|
|
|
87
|
+
<details>
|
|
88
|
+
<summary><b>Shadow DOM Support: Access Closed Shadow Roots with Zero Effort</b></summary>
|
|
89
|
+
<br>
|
|
90
|
+
|
|
91
|
+
Pydoll now provides **full Shadow DOM support**, automatically handling both open and closed shadow roots — something traditional automation tools can't do. Because Pydoll operates at the CDP level (below JavaScript), the `closed` mode restriction simply doesn't apply.
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
# Get the shadow root of a specific element
|
|
95
|
+
shadow = await element.get_shadow_root()
|
|
96
|
+
button = await shadow.query('.internal-btn')
|
|
97
|
+
await button.click()
|
|
98
|
+
|
|
99
|
+
# Or discover ALL shadow roots on the page at once
|
|
100
|
+
shadow_roots = await tab.find_shadow_roots()
|
|
101
|
+
for sr in shadow_roots:
|
|
102
|
+
checkbox = await sr.query('input[type="checkbox"]', raise_exc=False)
|
|
103
|
+
if checkbox:
|
|
104
|
+
await checkbox.click()
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Key highlights:**
|
|
108
|
+
|
|
109
|
+
- **Closed shadow roots just work** — no workarounds, no hacks
|
|
110
|
+
- **`find_shadow_roots()`** discovers every shadow root on the page, even when you don't know the host selector
|
|
111
|
+
- **`timeout` parameter** for polling until shadow roots appear asynchronously — works on both `find_shadow_roots()` and `get_shadow_root()`
|
|
112
|
+
- **`deep=True`** traverses cross-origin iframes (OOPIFs) — essential for widgets like Cloudflare Turnstile captchas
|
|
113
|
+
- **Same familiar API** — use `find()`, `query()`, and `click()` inside shadow roots just like anywhere else
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
# Real-world example: Cloudflare Turnstile inside a cross-origin iframe
|
|
117
|
+
shadow_roots = await tab.find_shadow_roots(deep=True, timeout=10)
|
|
118
|
+
for sr in shadow_roots:
|
|
119
|
+
checkbox = await sr.query('input[type="checkbox"]', raise_exc=False)
|
|
120
|
+
if checkbox:
|
|
121
|
+
await checkbox.click()
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
[**📖 Shadow DOM Docs**](https://pydoll.tech/docs/deep-dive/architecture/shadow-dom/)
|
|
125
|
+
</details>
|
|
126
|
+
|
|
127
|
+
<details>
|
|
128
|
+
<summary><b>Cloudflare Turnstile Bypass: Faster, More Robust, Fully Automatic</b></summary>
|
|
129
|
+
<br>
|
|
130
|
+
|
|
131
|
+
The Cloudflare Turnstile bypass has been **completely rewritten** using shadow root traversal, making it significantly more reliable than the old selector-based approach.
|
|
132
|
+
|
|
133
|
+
**What changed:**
|
|
134
|
+
|
|
135
|
+
- **Automatic detection** — Pydoll now polls the page's shadow DOM for the Cloudflare challenge domain, no selectors needed
|
|
136
|
+
- **Full shadow root traversal** — navigates outer shadow root → cross-origin iframe → inner shadow root → actual checkbox element
|
|
137
|
+
- **No more artificial delays** — the old `time_before_click` sleep is gone; the checkbox is clicked as soon as it's found
|
|
138
|
+
- **Zero configuration** — just call the method, Pydoll handles the rest
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
async with Chrome() as browser:
|
|
142
|
+
tab = await browser.start()
|
|
143
|
+
|
|
144
|
+
# That's it — fully automatic
|
|
145
|
+
async with tab.expect_and_bypass_cloudflare_captcha():
|
|
146
|
+
await tab.go_to('https://site-with-turnstile.com')
|
|
147
|
+
|
|
148
|
+
print("Captcha handled!")
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
[**📖 Cloudflare Turnstile Docs**](https://pydoll.tech/docs/features/advanced/behavioral-captcha-bypass/)
|
|
152
|
+
</details>
|
|
153
|
+
|
|
87
154
|
<details>
|
|
88
155
|
<summary><b>Humanized Keyboard Input (<code>humanize=True</code>)</b></summary>
|
|
89
156
|
<br>
|
|
@@ -142,31 +209,31 @@ from pydoll.browser import Chrome
|
|
|
142
209
|
from pydoll.constants import Key
|
|
143
210
|
|
|
144
211
|
async def google_search(query: str):
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
asyncio.run(google_search('pydoll
|
|
212
|
+
# Context manager handles browser start() and stop()
|
|
213
|
+
async with Chrome() as browser:
|
|
214
|
+
tab = await browser.start()
|
|
215
|
+
await tab.go_to('https://www.google.com')
|
|
216
|
+
|
|
217
|
+
# Intuitive finding API: find by HTML attributes
|
|
218
|
+
search_box = await tab.find(tag_name='textarea', name='q')
|
|
219
|
+
|
|
220
|
+
# "Human-like" interactions simulate typing
|
|
221
|
+
await search_box.insert_text(query)
|
|
222
|
+
await tab.keyboard.press(Key.ENTER)
|
|
223
|
+
|
|
224
|
+
# Find by text and click (simulates mouse movement)
|
|
225
|
+
first_result = await tab.find(
|
|
226
|
+
tag_name='h3',
|
|
227
|
+
text='autoscrape-labs/pydoll', # Supports partial text matching
|
|
228
|
+
timeout=10,
|
|
229
|
+
)
|
|
230
|
+
await first_result.click()
|
|
231
|
+
|
|
232
|
+
# Wait for an element to confirm navigation
|
|
233
|
+
await tab.find(id='repository-container-header', timeout=10)
|
|
234
|
+
print(f"Page loaded: {await tab.title}")
|
|
235
|
+
|
|
236
|
+
asyncio.run(google_search('pydoll site:github.com'))
|
|
170
237
|
```
|
|
171
238
|
|
|
172
239
|
## ⚡ The Pydoll Feature Ecosystem
|
|
@@ -64,6 +64,73 @@ That's it. No `webdrivers`. No external dependencies.
|
|
|
64
64
|
|
|
65
65
|
## 🆕 What's New
|
|
66
66
|
|
|
67
|
+
<details>
|
|
68
|
+
<summary><b>Shadow DOM Support: Access Closed Shadow Roots with Zero Effort</b></summary>
|
|
69
|
+
<br>
|
|
70
|
+
|
|
71
|
+
Pydoll now provides **full Shadow DOM support**, automatically handling both open and closed shadow roots — something traditional automation tools can't do. Because Pydoll operates at the CDP level (below JavaScript), the `closed` mode restriction simply doesn't apply.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
# Get the shadow root of a specific element
|
|
75
|
+
shadow = await element.get_shadow_root()
|
|
76
|
+
button = await shadow.query('.internal-btn')
|
|
77
|
+
await button.click()
|
|
78
|
+
|
|
79
|
+
# Or discover ALL shadow roots on the page at once
|
|
80
|
+
shadow_roots = await tab.find_shadow_roots()
|
|
81
|
+
for sr in shadow_roots:
|
|
82
|
+
checkbox = await sr.query('input[type="checkbox"]', raise_exc=False)
|
|
83
|
+
if checkbox:
|
|
84
|
+
await checkbox.click()
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Key highlights:**
|
|
88
|
+
|
|
89
|
+
- **Closed shadow roots just work** — no workarounds, no hacks
|
|
90
|
+
- **`find_shadow_roots()`** discovers every shadow root on the page, even when you don't know the host selector
|
|
91
|
+
- **`timeout` parameter** for polling until shadow roots appear asynchronously — works on both `find_shadow_roots()` and `get_shadow_root()`
|
|
92
|
+
- **`deep=True`** traverses cross-origin iframes (OOPIFs) — essential for widgets like Cloudflare Turnstile captchas
|
|
93
|
+
- **Same familiar API** — use `find()`, `query()`, and `click()` inside shadow roots just like anywhere else
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
# Real-world example: Cloudflare Turnstile inside a cross-origin iframe
|
|
97
|
+
shadow_roots = await tab.find_shadow_roots(deep=True, timeout=10)
|
|
98
|
+
for sr in shadow_roots:
|
|
99
|
+
checkbox = await sr.query('input[type="checkbox"]', raise_exc=False)
|
|
100
|
+
if checkbox:
|
|
101
|
+
await checkbox.click()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
[**📖 Shadow DOM Docs**](https://pydoll.tech/docs/deep-dive/architecture/shadow-dom/)
|
|
105
|
+
</details>
|
|
106
|
+
|
|
107
|
+
<details>
|
|
108
|
+
<summary><b>Cloudflare Turnstile Bypass: Faster, More Robust, Fully Automatic</b></summary>
|
|
109
|
+
<br>
|
|
110
|
+
|
|
111
|
+
The Cloudflare Turnstile bypass has been **completely rewritten** using shadow root traversal, making it significantly more reliable than the old selector-based approach.
|
|
112
|
+
|
|
113
|
+
**What changed:**
|
|
114
|
+
|
|
115
|
+
- **Automatic detection** — Pydoll now polls the page's shadow DOM for the Cloudflare challenge domain, no selectors needed
|
|
116
|
+
- **Full shadow root traversal** — navigates outer shadow root → cross-origin iframe → inner shadow root → actual checkbox element
|
|
117
|
+
- **No more artificial delays** — the old `time_before_click` sleep is gone; the checkbox is clicked as soon as it's found
|
|
118
|
+
- **Zero configuration** — just call the method, Pydoll handles the rest
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
async with Chrome() as browser:
|
|
122
|
+
tab = await browser.start()
|
|
123
|
+
|
|
124
|
+
# That's it — fully automatic
|
|
125
|
+
async with tab.expect_and_bypass_cloudflare_captcha():
|
|
126
|
+
await tab.go_to('https://site-with-turnstile.com')
|
|
127
|
+
|
|
128
|
+
print("Captcha handled!")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
[**📖 Cloudflare Turnstile Docs**](https://pydoll.tech/docs/features/advanced/behavioral-captcha-bypass/)
|
|
132
|
+
</details>
|
|
133
|
+
|
|
67
134
|
<details>
|
|
68
135
|
<summary><b>Humanized Keyboard Input (<code>humanize=True</code>)</b></summary>
|
|
69
136
|
<br>
|
|
@@ -122,31 +189,31 @@ from pydoll.browser import Chrome
|
|
|
122
189
|
from pydoll.constants import Key
|
|
123
190
|
|
|
124
191
|
async def google_search(query: str):
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
asyncio.run(google_search('pydoll
|
|
192
|
+
# Context manager handles browser start() and stop()
|
|
193
|
+
async with Chrome() as browser:
|
|
194
|
+
tab = await browser.start()
|
|
195
|
+
await tab.go_to('https://www.google.com')
|
|
196
|
+
|
|
197
|
+
# Intuitive finding API: find by HTML attributes
|
|
198
|
+
search_box = await tab.find(tag_name='textarea', name='q')
|
|
199
|
+
|
|
200
|
+
# "Human-like" interactions simulate typing
|
|
201
|
+
await search_box.insert_text(query)
|
|
202
|
+
await tab.keyboard.press(Key.ENTER)
|
|
203
|
+
|
|
204
|
+
# Find by text and click (simulates mouse movement)
|
|
205
|
+
first_result = await tab.find(
|
|
206
|
+
tag_name='h3',
|
|
207
|
+
text='autoscrape-labs/pydoll', # Supports partial text matching
|
|
208
|
+
timeout=10,
|
|
209
|
+
)
|
|
210
|
+
await first_result.click()
|
|
211
|
+
|
|
212
|
+
# Wait for an element to confirm navigation
|
|
213
|
+
await tab.find(id='repository-container-header', timeout=10)
|
|
214
|
+
print(f"Page loaded: {await tab.title}")
|
|
215
|
+
|
|
216
|
+
asyncio.run(google_search('pydoll site:github.com'))
|
|
150
217
|
```
|
|
151
218
|
|
|
152
219
|
## ⚡ The Pydoll Feature Ecosystem
|