Skip to content

Commit 711ccae

Browse files
committed
docs: improvements and revisions
1 parent 8773f25 commit 711ccae

File tree

15 files changed

+490
-969
lines changed

15 files changed

+490
-969
lines changed

docs/en/features/advanced/behavioral-captcha-bypass.md

Lines changed: 234 additions & 537 deletions
Large diffs are not rendered by default.

docs/en/features/advanced/event-system.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ await tab.on(NetworkEvent.RESPONSE_RECEIVED, log_response)
162162
async def log_failure(event: LoadingFailedEvent):
163163
url = event['params']['type']
164164
error = event['params']['errorText']
165-
print(f"✗ Failed: {url} - {error}")
165+
print(f"[FAILED] {url} - {error}")
166166

167167
await tab.on(NetworkEvent.LOADING_FAILED, log_failure)
168168
```
@@ -477,11 +477,11 @@ Each event type is a `TypedDict` that defines the exact structure of the event,
477477
```python
478478
from pydoll.protocol.network.events import NetworkEvent
479479

480-
# Good
480+
# Good
481481
await tab.enable_network_events()
482482
await tab.on(NetworkEvent.RESPONSE_RECEIVED, callback)
483483

484-
# Bad - callback will never fire
484+
# Bad: callback will never fire
485485
await tab.on(NetworkEvent.RESPONSE_RECEIVED, callback)
486486
await tab.enable_network_events()
487487
```
@@ -508,7 +508,7 @@ await tab.disable_network_events()
508508
```python
509509
from pydoll.protocol.network.events import RequestWillBeSentEvent
510510

511-
# Good - filter early
511+
# Good: filter early
512512
async def handle_api_request(event: RequestWillBeSentEvent):
513513
url = event['params']['request']['url']
514514
if '/api/' not in url:
@@ -517,7 +517,7 @@ async def handle_api_request(event: RequestWillBeSentEvent):
517517
# Process only API requests
518518
process_request(event)
519519

520-
# Bad - processes everything
520+
# Bad: processes everything
521521
async def handle_all_requests(event: RequestWillBeSentEvent):
522522
url = event['params']['request']['url']
523523
process_request(event)
@@ -569,12 +569,12 @@ async def safe_callback(event: ResponseReceivedEvent):
569569
import asyncio
570570
from pydoll.protocol.network.events import ResponseReceivedEvent
571571

572-
# Good - fast callback, offload heavy work
572+
# Good: fast callback, offload heavy work
573573
async def handle_response(event: ResponseReceivedEvent):
574574
if should_process(event):
575575
asyncio.create_task(heavy_processing(event)) # Don't block
576576

577-
# Bad - blocks event loop
577+
# Bad: blocks event loop
578578
async def handle_response(event: ResponseReceivedEvent):
579579
await heavy_processing(event) # Blocks other events
580580
```

docs/en/features/advanced/remote-connections.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ async def connect_to_remote_server():
349349
chrome = Chrome()
350350
tab = await chrome.connect(ws_url)
351351

352-
print(f"\n Connected to remote Chrome server!")
352+
print(f"\n[SUCCESS] Connected to remote Chrome server!")
353353

354354
# 3. Use normally
355355
await tab.go_to('https://example.com')
@@ -499,7 +499,16 @@ asyncio.run(connect_to_remote_browser())
499499
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222
500500
```
501501

502-
Then visit `http://localhost:9222/json/version` to get the WebSocket URL in the `webSocketDebuggerUrl` field.
502+
**For local connections** (same machine):
503+
504+
- Visit `http://localhost:9222/json/version` in your browser to get the WebSocket URL in the `webSocketDebuggerUrl` field
505+
- Or programmatically query it as shown in the example above using `aiohttp`
506+
- For quick debugging, you can also check `browser._connection_port` after starting a local browser instance
507+
508+
**For remote connections** (different machine):
509+
510+
- Query `http://SERVER_IP:9222/json/version` from your client machine
511+
- Use the `webSocketDebuggerUrl` from the response, replacing `localhost` with the actual server IP if needed
503512

504513
### Method 2: Direct Element Control (Hybrid Approach)
505514

docs/en/features/automation/file-operations.md

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ The simplest approach is using `set_input_files()` directly on file input elemen
1717

1818
```python
1919
import asyncio
20+
from pathlib import Path
2021
from pydoll.browser.chromium import Chrome
2122

2223
async def direct_file_upload():
@@ -28,7 +29,8 @@ async def direct_file_upload():
2829
file_input = await tab.find(tag_name='input', type='file')
2930

3031
# Set the file directly
31-
await file_input.set_input_files(['path/to/document.pdf'])
32+
file_path = Path('path/to/document.pdf')
33+
await file_input.set_input_files(file_path)
3234

3335
# Submit the form
3436
submit_button = await tab.find(id='submit-button')
@@ -39,12 +41,19 @@ async def direct_file_upload():
3941
asyncio.run(direct_file_upload())
4042
```
4143

44+
!!! tip "Path vs String"
45+
While `Path` objects from `pathlib` are recommended as best practice for better path handling and cross-platform compatibility, you can also use plain strings if preferred:
46+
```python
47+
await file_input.set_input_files('path/to/document.pdf') # Also works!
48+
```
49+
4250
### Multiple Files
4351

4452
For inputs that accept multiple files (`<input type="file" multiple>`), pass a list of file paths:
4553

4654
```python
4755
import asyncio
56+
from pathlib import Path
4857
from pydoll.browser.chromium import Chrome
4958

5059
async def upload_multiple_files():
@@ -56,9 +65,9 @@ async def upload_multiple_files():
5665

5766
# Upload multiple files at once
5867
files = [
59-
'documents/report.pdf',
60-
'images/screenshot.png',
61-
'data/results.csv'
68+
Path('documents/report.pdf'),
69+
Path('images/screenshot.png'),
70+
Path('data/results.csv')
6271
]
6372
await file_input.set_input_files(files)
6473

@@ -69,29 +78,32 @@ async def upload_multiple_files():
6978
asyncio.run(upload_multiple_files())
7079
```
7180

72-
### Using Path Objects
81+
### Dynamic Path Resolution
7382

74-
Both string paths and `Path` objects from `pathlib` are supported:
83+
`Path` objects make it easy to build paths dynamically and handle cross-platform compatibility:
7584

7685
```python
7786
import asyncio
7887
from pathlib import Path
7988
from pydoll.browser.chromium import Chrome
8089

81-
async def upload_with_path_objects():
90+
async def upload_with_dynamic_paths():
8291
async with Chrome() as browser:
8392
tab = await browser.start()
8493
await tab.go_to('https://example.com/upload')
8594

8695
file_input = await tab.find(tag_name='input', type='file')
8796

88-
# Using Path objects
97+
# Build paths dynamically
8998
project_dir = Path(__file__).parent
9099
file_path = project_dir / 'uploads' / 'data.json'
91-
92-
await file_input.set_input_files([str(file_path)])
93100

94-
asyncio.run(upload_with_path_objects())
101+
await file_input.set_input_files(file_path)
102+
# Or use home directory
103+
user_file = Path.home() / 'Documents' / 'report.pdf'
104+
await file_input.set_input_files(user_file)
105+
106+
asyncio.run(upload_with_dynamic_paths())
95107
```
96108

97109
!!! tip "When to Use Direct File Input"
@@ -119,7 +131,7 @@ The `expect_file_chooser()` context manager:
119131

120132
```python
121133
import asyncio
122-
import os
134+
from pathlib import Path
123135
from pydoll.browser.chromium import Chrome
124136

125137
async def file_chooser_upload():
@@ -128,7 +140,7 @@ async def file_chooser_upload():
128140
await tab.go_to('https://example.com/custom-upload')
129141

130142
# Prepare the file path
131-
file_path = os.path.join(os.getcwd(), 'document.pdf')
143+
file_path = Path.cwd() / 'document.pdf'
132144

133145
# Use context manager to handle file chooser
134146
async with tab.expect_file_chooser(files=file_path):
@@ -147,6 +159,7 @@ asyncio.run(file_chooser_upload())
147159

148160
```python
149161
import asyncio
162+
from pathlib import Path
150163
from pydoll.browser.chromium import Chrome
151164

152165
async def multiple_files_chooser():
@@ -155,10 +168,11 @@ async def multiple_files_chooser():
155168
await tab.go_to('https://example.com/gallery-upload')
156169

157170
# Prepare multiple files
171+
photos_dir = Path.home() / 'photos'
158172
files = [
159-
'/home/user/photos/img1.jpg',
160-
'/home/user/photos/img2.jpg',
161-
'/home/user/photos/img3.jpg'
173+
photos_dir / 'img1.jpg',
174+
photos_dir / 'img2.jpg',
175+
photos_dir / 'img3.jpg'
162176
]
163177

164178
async with tab.expect_file_chooser(files=files):
@@ -175,16 +189,17 @@ asyncio.run(multiple_files_chooser())
175189

176190
```python
177191
import asyncio
178-
import glob
192+
from pathlib import Path
179193
from pydoll.browser.chromium import Chrome
180194

181195
async def dynamic_file_selection():
182196
async with Chrome() as browser:
183197
tab = await browser.start()
184198
await tab.go_to('https://example.com/batch-upload')
185199

186-
# Find all CSV files in a directory
187-
csv_files = glob.glob('data/*.csv')
200+
# Find all CSV files in a directory using Path.glob()
201+
data_dir = Path('data')
202+
csv_files = list(data_dir.glob('*.csv'))
188203

189204
async with tab.expect_file_chooser(files=csv_files):
190205
upload_area = await tab.find(class_name='drop-zone')
@@ -219,7 +234,6 @@ Here's a comprehensive example combining both approaches:
219234

220235
```python
221236
import asyncio
222-
import os
223237
from pathlib import Path
224238
from pydoll.browser.chromium import Chrome
225239

@@ -228,16 +242,16 @@ async def comprehensive_upload_example():
228242
tab = await browser.start()
229243
await tab.go_to('https://example.com/upload-form')
230244

231-
# Scenario 1: Direct input for profile picture
245+
# Scenario 1: Direct input for profile picture (single file)
232246
avatar_input = await tab.find(id='avatar-upload')
233247
avatar_path = Path.home() / 'Pictures' / 'profile.jpg'
234-
await avatar_input.set_input_files([str(avatar_path)])
248+
await avatar_input.set_input_files(avatar_path)
235249

236250
# Wait a bit for preview to load
237251
await asyncio.sleep(1)
238252

239253
# Scenario 2: File chooser for document upload
240-
document_path = os.path.join(os.getcwd(), 'documents', 'resume.pdf')
254+
document_path = Path.cwd() / 'documents' / 'resume.pdf'
241255
async with tab.expect_file_chooser(files=document_path):
242256
# Custom styled button that triggers file chooser
243257
upload_btn = await tab.find(class_name='btn-upload-document')
@@ -247,10 +261,11 @@ async def comprehensive_upload_example():
247261
await asyncio.sleep(2)
248262

249263
# Scenario 3: Multiple files via file chooser
264+
certs_dir = Path('certs')
250265
certificates = [
251-
'certs/certificate1.pdf',
252-
'certs/certificate2.pdf',
253-
'certs/certificate3.pdf'
266+
certs_dir / 'certificate1.pdf',
267+
certs_dir / 'certificate2.pdf',
268+
certs_dir / 'certificate3.pdf'
254269
]
255270
async with tab.expect_file_chooser(files=certificates):
256271
add_certs_btn = await tab.find(text='Add Certificates')
@@ -268,6 +283,14 @@ async def comprehensive_upload_example():
268283
asyncio.run(comprehensive_upload_example())
269284
```
270285

286+
!!! info "Method Summary"
287+
This example demonstrates the flexibility of Pydoll's file upload system:
288+
289+
- **Single files**: Pass `Path` or `str` directly (no list needed)
290+
- **Multiple files**: Pass a list of `Path` or `str` objects
291+
- **Direct input**: Fast for visible `<input>` elements
292+
- **File chooser**: Works with custom upload buttons and hidden inputs
293+
271294
## Learn More
272295

273296
For deeper understanding of the file upload mechanisms:

0 commit comments

Comments
 (0)