pydoll-python 2.2.3__tar.gz → 2.3.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.
Files changed (97) hide show
  1. pydoll_python-2.3.0/PKG-INFO +382 -0
  2. pydoll_python-2.3.0/README.md +363 -0
  3. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/connection/connection_handler.py +11 -8
  4. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/elements/mixins/find_elements_mixin.py +1 -1
  5. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/base.py +3 -1
  6. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/browser/params.py +1 -1
  7. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/browser/types.py +1 -1
  8. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/dom/params.py +1 -1
  9. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/dom/responses.py +1 -1
  10. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/dom/types.py +3 -1
  11. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/fetch/params.py +1 -1
  12. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/fetch/types.py +1 -1
  13. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/input/params.py +1 -1
  14. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/input/types.py +1 -1
  15. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/network/params.py +1 -1
  16. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/network/responses.py +1 -1
  17. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/network/types.py +1 -1
  18. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/page/params.py +3 -1
  19. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/page/responses.py +1 -1
  20. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/page/types.py +1 -1
  21. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/runtime/params.py +1 -1
  22. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/runtime/responses.py +1 -1
  23. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/runtime/types.py +3 -1
  24. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/storage/params.py +1 -1
  25. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/storage/types.py +1 -1
  26. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/target/params.py +1 -1
  27. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/target/types.py +1 -1
  28. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pyproject.toml +4 -3
  29. pydoll_python-2.2.3/PKG-INFO +0 -391
  30. pydoll_python-2.2.3/README.md +0 -374
  31. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/LICENSE +0 -0
  32. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/__init__.py +0 -0
  33. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/__init__.py +0 -0
  34. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/chromium/__init__.py +0 -0
  35. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/chromium/base.py +0 -0
  36. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/chromium/chrome.py +0 -0
  37. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/chromium/edge.py +0 -0
  38. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/interfaces.py +0 -0
  39. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/managers/__init__.py +0 -0
  40. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/managers/browser_options_manager.py +0 -0
  41. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/managers/browser_process_manager.py +0 -0
  42. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/managers/proxy_manager.py +0 -0
  43. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/managers/temp_dir_manager.py +0 -0
  44. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/options.py +0 -0
  45. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/browser/tab.py +0 -0
  46. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/commands/__init__.py +0 -0
  47. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/commands/browser_commands.py +0 -0
  48. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/commands/dom_commands.py +0 -0
  49. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/commands/fetch_commands.py +0 -0
  50. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/commands/input_commands.py +0 -0
  51. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/commands/network_commands.py +0 -0
  52. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/commands/page_commands.py +0 -0
  53. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/commands/runtime_commands.py +0 -0
  54. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/commands/storage_commands.py +0 -0
  55. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/commands/target_commands.py +0 -0
  56. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/connection/__init__.py +0 -0
  57. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/connection/managers/__init__.py +0 -0
  58. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/connection/managers/commands_manager.py +0 -0
  59. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/connection/managers/events_manager.py +0 -0
  60. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/constants.py +0 -0
  61. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/elements/__init__.py +0 -0
  62. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/elements/mixins/__init__.py +0 -0
  63. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/elements/web_element.py +0 -0
  64. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/exceptions.py +0 -0
  65. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/__init__.py +0 -0
  66. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/browser/__init__.py +0 -0
  67. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/browser/events.py +0 -0
  68. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/browser/methods.py +0 -0
  69. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/browser/responses.py +0 -0
  70. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/dom/__init__.py +0 -0
  71. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/dom/events.py +0 -0
  72. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/dom/methods.py +0 -0
  73. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/fetch/__init__.py +0 -0
  74. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/fetch/events.py +0 -0
  75. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/fetch/methods.py +0 -0
  76. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/fetch/responses.py +0 -0
  77. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/input/__init__.py +0 -0
  78. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/input/events.py +0 -0
  79. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/input/methods.py +0 -0
  80. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/network/__init__.py +0 -0
  81. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/network/events.py +0 -0
  82. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/network/methods.py +0 -0
  83. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/page/__init__.py +0 -0
  84. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/page/events.py +0 -0
  85. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/page/methods.py +0 -0
  86. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/runtime/__init__.py +0 -0
  87. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/runtime/events.py +0 -0
  88. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/runtime/methods.py +0 -0
  89. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/storage/__init__.py +0 -0
  90. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/storage/events.py +0 -0
  91. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/storage/methods.py +0 -0
  92. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/storage/responses.py +0 -0
  93. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/target/__init__.py +0 -0
  94. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/target/events.py +0 -0
  95. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/target/methods.py +0 -0
  96. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/protocol/target/responses.py +0 -0
  97. {pydoll_python-2.2.3 → pydoll_python-2.3.0}/pydoll/utils.py +0 -0
@@ -0,0 +1,382 @@
1
+ Metadata-Version: 2.3
2
+ Name: pydoll-python
3
+ Version: 2.3.0
4
+ Summary:
5
+ Author: Thalison Fernandes
6
+ Author-email: thalissfernandes99@gmail.com
7
+ Requires-Python: >=3.10,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Dist: aiofiles (>=23.2.1,<24.0.0)
14
+ Requires-Dist: aiohttp (>=3.9.5,<4.0.0)
15
+ Requires-Dist: typing_extensions (>=4.14.0,<5.0.0)
16
+ Requires-Dist: websockets (>=14,<15)
17
+ Description-Content-Type: text/markdown
18
+
19
+ <p align="center">
20
+ <img src="https://github.com/user-attachments/assets/219f2dbc-37ed-4aea-a289-ba39cdbb335d" alt="Pydoll Logo" /> <br>
21
+ </p>
22
+ <h1 align="center">Pydoll: Automate the Web, Naturally</h1>
23
+
24
+ <p align="center">
25
+ <a href="https://codecov.io/gh/autoscrape-labs/pydoll" >
26
+ <img src="https://codecov.io/gh/autoscrape-labs/pydoll/graph/badge.svg?token=40I938OGM9"/>
27
+ </a>
28
+ <img src="https://github.com/thalissonvs/pydoll/actions/workflows/tests.yml/badge.svg" alt="Tests">
29
+ <img src="https://github.com/thalissonvs/pydoll/actions/workflows/ruff-ci.yml/badge.svg" alt="Ruff CI">
30
+ <img src="https://github.com/thalissonvs/pydoll/actions/workflows/mypy.yml/badge.svg" alt="MyPy CI">
31
+ <img src="https://img.shields.io/badge/python-%3E%3D3.10-blue" alt="Python >= 3.10">
32
+ <a href="https://deepwiki.com/autoscrape-labs/pydoll"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
33
+ </p>
34
+
35
+
36
+ <p align="center">
37
+ 📖 <a href="https://autoscrape-labs.github.io/pydoll/">Documentation</a> •
38
+ 🚀 <a href="#getting-started">Getting Started</a> •
39
+ ⚡ <a href="#advanced-features">Advanced Features</a> •
40
+ 🤝 <a href="#contributing">Contributing</a> •
41
+ 💖 <a href="#support-my-work">Support My Work</a>
42
+ </p>
43
+
44
+ Imagine the following scenario: you need to automate tasks in your browser. Maybe it's testing a web application, collecting data from a site, or even automating repetitive processes. Normally this involves using external drivers, complex configurations, and many compatibility issues.
45
+
46
+ **Pydoll was born to solve these problems.**
47
+
48
+ Built from scratch with a different philosophy, Pydoll connects directly to the Chrome DevTools Protocol (CDP), eliminating the need for external drivers. This clean implementation along with realistic ways of clicking, navigating and interacting with elements makes it practically indistinguishable from a real user.
49
+
50
+ We believe that powerful automation shouldn't require you to become an expert in configuration or constantly fight with bot protection systems. With Pydoll, you can focus on what really matters: your automation logic, not the underlying complexity or protection systems.
51
+
52
+ ## 🌟 What makes Pydoll special?
53
+
54
+ - **Zero Webdrivers**: Say goodbye to webdriver compatibility issues
55
+ - **Native Captcha Bypass**: Handles Cloudflare Turnstile and reCAPTCHA v3*
56
+ - **Asynchronous Performance**: For high-speed automation and multiple simultaneous tasks
57
+ - **Humanized Interactions**: Mimic real user behavior
58
+ - **Simplicity**: With Pydoll, you install and you're ready to automate.
59
+
60
+ >⚠️ The effectiveness of captcha bypass depends on various factors, such as IP address reputation. Pydoll can achieve scores comparable to real users, but cannot handle restrictive configurations or IP blocks.
61
+
62
+ ## 📦 Installation
63
+
64
+ ```bash
65
+ pip install pydoll-python
66
+ ```
67
+
68
+ And that's it! Just install and start automating.
69
+
70
+ ## 🚀 Getting Started
71
+
72
+ ### Your first automation
73
+
74
+ Let's start with a real example: an automation that performs a Google search and clicks on the first result. With this example, we can see how the library works and how you can start automating your tasks.
75
+
76
+ ```python
77
+ import asyncio
78
+
79
+ from pydoll.browser import Chrome
80
+ from pydoll.constants import Key
81
+
82
+ async def google_search(query: str):
83
+ async with Chrome() as browser:
84
+ tab = await browser.start()
85
+ await tab.go_to('https://www.google.com')
86
+ search_box = await tab.find(tag_name='textarea', name='q')
87
+ await search_box.insert_text(query)
88
+ await search_box.press_keyboard_key(Key.ENTER)
89
+ await (await tab.find(
90
+ tag_name='h3',
91
+ text='autoscrape-labs/pydoll',
92
+ timeout=10,
93
+ )).click()
94
+ await tab.find(id='repository-container-header', timeout=10)
95
+
96
+ asyncio.run(google_search('pydoll python'))
97
+ ```
98
+
99
+ Without configurations, just a simple script, we can do a complete Google search!
100
+ Okay, now let's see how we can extract data from a page, using the same previous example.
101
+ Let's consider in the code below that we're already on the Pydoll page. We want to extract the following information:
102
+
103
+ - Project description
104
+ - Number of stars
105
+ - Number of forks
106
+ - Number of issues
107
+ - Number of pull requests
108
+
109
+ Let's get started! To get the project description, we'll use xpath queries. You can check the documentation on how to build your own queries.
110
+
111
+ ```python
112
+ description = await (await tab.query(
113
+ '//h2[contains(text(), "About")]/following-sibling::p',
114
+ timeout=10,
115
+ )).text
116
+ ```
117
+
118
+ And that's it! Let's understand what this query does:
119
+
120
+ 1. `//h2[contains(text(), "About")]` - Selects the first `<h2>` that contains "About"
121
+ 2. `/following-sibling::p` - Selects the first `<p>` that comes after the `<h2>`
122
+
123
+ Now let's get the rest of the data:
124
+
125
+ ```python
126
+ number_of_stars = await (await tab.find(
127
+ id='repo-stars-counter-star'
128
+ )).text
129
+
130
+ number_of_forks = await (await tab.find(
131
+ id='repo-network-counter'
132
+ )).text
133
+ number_of_issues = await (await tab.find(
134
+ id='issues-repo-tab-count',
135
+ )).text
136
+ number_of_pull_requests = await (await tab.find(
137
+ id='pull-requests-repo-tab-count',
138
+ )).text
139
+
140
+ data = {
141
+ 'description': description,
142
+ 'number_of_stars': number_of_stars,
143
+ 'number_of_forks': number_of_forks,
144
+ 'number_of_issues': number_of_issues,
145
+ 'number_of_pull_requests': number_of_pull_requests,
146
+ }
147
+ print(data)
148
+
149
+ ```
150
+
151
+ In the image below we can see the execution speed and the result of the automation.
152
+ For demonstration purposes, the browser is not displayed.
153
+
154
+ ![google_seach](./docs/images/google-search-example.gif)
155
+
156
+
157
+ In just 5 seconds, we managed to extract all the necessary data! This is the
158
+ speed you can expect from automation with Pydoll.
159
+
160
+
161
+ ### A more complex example
162
+
163
+ Let's now move to a case that you've probably encountered many times: a captcha
164
+ like Cloudflare's. Pydoll has a method to try to handle this, although, as mentioned earlier, effectiveness depends on various factors. In the code below, we have a complete example of how to handle a Cloudflare captcha.
165
+
166
+ ```python
167
+ import asyncio
168
+
169
+ from pydoll.browser import Chrome
170
+ from pydoll.constants import By
171
+
172
+ async def cloudflare_example():
173
+ async with Chrome() as browser:
174
+ tab = await browser.start()
175
+ async with tab.expect_and_bypass_cloudflare_captcha():
176
+ await tab.go_to('https://2captcha.com/demo/cloudflare-turnstile')
177
+ print('Captcha handled, continuing...')
178
+ await asyncio.sleep(5) # just to see the result :)
179
+
180
+ asyncio.run(cloudflare_example())
181
+
182
+ ```
183
+
184
+ Below we have the result of the execution:
185
+
186
+ ![cloudflare_example](./docs/images/cloudflare-example.gif)
187
+
188
+
189
+ With just a few lines of code, we managed to handle one of the most
190
+ difficult captchas to deal with. This is just one of the many functionalities that Pydoll
191
+ offers. But it doesn't stop there!
192
+
193
+
194
+ ### Custom Configurations
195
+
196
+ Sometimes we need more control over the browser. Pydoll offers a flexible way to do this. Let's see the example below:
197
+
198
+
199
+ ```python
200
+ from pydoll.browser import Chrome
201
+ from pydoll.browser.options import ChromiumOptions as Options
202
+
203
+ async def custom_automation():
204
+ # Configure browser options
205
+ options = Options()
206
+ options.add_argument('--proxy-server=username:password@ip:port')
207
+ options.add_argument('--window-size=1920,1080')
208
+ options.binary_location = '/path/to/your/browser'
209
+
210
+ async with Chrome(options=options) as browser:
211
+ tab = await browser.start()
212
+ # Your automation code here
213
+ await tab.go_to('https://example.com')
214
+ # The browser is now using your custom settings
215
+
216
+ asyncio.run(custom_automation())
217
+ ```
218
+
219
+ In this example, we're configuring the browser to use a proxy and a 1920x1080 window, in addition to a custom path for the Chrome binary, in case your installation location is different from the common defaults.
220
+
221
+
222
+ ## ⚡ Advanced Features
223
+
224
+ Pydoll offers a series of advanced features to please even the most
225
+ demanding users.
226
+
227
+
228
+ ### Advanced Element Search
229
+
230
+ We have several ways to find elements on the page. No matter how you prefer, we have a way that makes sense for you:
231
+
232
+ ```python
233
+ import asyncio
234
+ from pydoll.browser import Chrome
235
+
236
+ async def element_finding_examples():
237
+ async with Chrome() as browser:
238
+ tab = await browser.start()
239
+ await tab.go_to('https://example.com')
240
+
241
+ # Find by attributes (most intuitive)
242
+ submit_btn = await tab.find(
243
+ tag_name='button',
244
+ class_name='btn-primary',
245
+ text='Submit'
246
+ )
247
+ # Find by ID
248
+ username_field = await tab.find(id='username')
249
+ # Find multiple elements
250
+ all_links = await tab.find(tag_name='a', find_all=True)
251
+ # CSS selectors and XPath
252
+ nav_menu = await tab.query('nav.main-menu')
253
+ specific_item = await tab.query('//div[@data-testid="item-123"]')
254
+ # With timeout and error handling
255
+ delayed_element = await tab.find(
256
+ class_name='dynamic-content',
257
+ timeout=10,
258
+ raise_exc=False # Returns None if not found
259
+ )
260
+ # Advanced: Custom attributes
261
+ custom_element = await tab.find(
262
+ data_testid='submit-button',
263
+ aria_label='Submit form'
264
+ )
265
+
266
+ asyncio.run(element_finding_examples())
267
+ ```
268
+
269
+ The `find` method is more user-friendly. We can search by common attributes like id, tag_name, class_name, etc., up to custom attributes (e.g. `data-testid`).
270
+
271
+ If that's not enough, we can use the `query` method to search for elements using CSS selectors, XPath queries, etc. Pydoll automatically takes care of identifying what type of query we're using.
272
+
273
+
274
+ ### Concurrent Automation
275
+
276
+ One of the great advantages of Pydoll is the ability to process multiple tasks simultaneously thanks to its asynchronous implementation. We can automate multiple tabs
277
+ at the same time! Let's see an example:
278
+
279
+ ```python
280
+ import asyncio
281
+ from pydoll.browser import Chrome
282
+
283
+ async def scrape_page(url, tab):
284
+ await tab.go_to(url)
285
+ title = await tab.execute_script('return document.title')
286
+ links = await tab.find(tag_name='a', find_all=True)
287
+ return {
288
+ 'url': url,
289
+ 'title': title,
290
+ 'link_count': len(links)
291
+ }
292
+
293
+ async def concurrent_scraping():
294
+ browser = Chrome()
295
+ tab_google = await browser.start()
296
+ tab_duckduckgo = await browser.new_tab()
297
+ tasks = [
298
+ scrape_page('https://google.com/', tab_google),
299
+ scrape_page('https://duckduckgo.com/', tab_duckduckgo)
300
+ ]
301
+ results = await asyncio.gather(*tasks)
302
+ print(results)
303
+ await browser.stop()
304
+
305
+ asyncio.run(concurrent_scraping())
306
+ ```
307
+
308
+ Below we see the incredible execution speed:
309
+
310
+ ![concurrent_example](./docs/images/concurrent-example.gif)
311
+
312
+
313
+ We managed to extract data from two pages at the same time! Tell me if that's not incredible?
314
+
315
+
316
+ And there's much, much more! Event system for reactive automations, request interception and modification, and so on. Take a look at the documentation, you won't
317
+ regret it!
318
+
319
+
320
+ ## 🔧 Quick Troubleshooting
321
+
322
+ **Browser not found?**
323
+ ```python
324
+ from pydoll.browser import Chrome
325
+ from pydoll.browser.options import ChromiumOptions
326
+
327
+ options = ChromiumOptions()
328
+ options.binary_location = '/path/to/your/chrome'
329
+ browser = Chrome(options=options)
330
+ ```
331
+
332
+ **Need a proxy?**
333
+ ```python
334
+ options.add_argument('--proxy-server=your-proxy:port')
335
+ ```
336
+
337
+ **Running in Docker?**
338
+ ```python
339
+ options.add_argument('--no-sandbox')
340
+ options.add_argument('--disable-dev-shm-usage')
341
+ ```
342
+
343
+ ## 📚 Documentation
344
+
345
+ For complete documentation, detailed examples and deep dives into all Pydoll functionalities, visit our [official documentation](https://autoscrape-labs.github.io/pydoll/).
346
+
347
+ The documentation includes:
348
+ - **Getting Started Guide** - Step-by-step tutorials
349
+ - **API Reference** - Complete method documentation
350
+ - **Advanced Techniques** - Network interception, event handling, performance optimization
351
+
352
+ ## 🤝 Contributing
353
+
354
+ We would love your help to make Pydoll even better! Check out our [contribution guidelines](CONTRIBUTING.md) to get started. Whether it's fixing bugs, adding features or improving documentation - all contributions are welcome!
355
+
356
+ Please make sure to:
357
+ - Write tests for new features or bug fixes
358
+ - Follow code style and conventions
359
+ - Use conventional commits for pull requests
360
+ - Run lint checks and tests before submitting
361
+
362
+ ## 💖 Support My Work
363
+
364
+ If you find Pydoll useful, consider [supporting me on GitHub](https://github.com/sponsors/thalissonvs).
365
+ You'll get access to exclusive benefits like priority support, custom features and much more!
366
+
367
+ Can't sponsor right now? No problem — you can still help a lot by:
368
+ - Starring the repository
369
+ - Sharing on social media
370
+ - Writing posts or tutorials
371
+ - Giving feedback or reporting issues
372
+
373
+ Every bit of support makes a difference — thank you!
374
+
375
+ ## 📄 License
376
+
377
+ Pydoll is licensed under the [MIT License](LICENSE).
378
+
379
+ <p align="center">
380
+ <b>Pydoll</b> — Making browser automation magical!
381
+ </p>
382
+