browser-toolkit 0.0.1a2__tar.gz → 0.0.1a3__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.
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/PKG-INFO +1 -1
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/__init__.py +1 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/base_toolkit.py +77 -21
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/create_browser/playwright.py +5 -6
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/playwright.py +72 -36
- browser_toolkit-0.0.1a3/browser_toolkit/pydoll.py +542 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/selenium_toolkit/selenium_toolkit.py +2 -1
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/types.py +11 -1
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit.egg-info/PKG-INFO +1 -1
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit.egg-info/SOURCES.txt +1 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/pyproject.toml +1 -1
- browser_toolkit-0.0.1a3/tests/test_network_requests.py +18 -0
- browser_toolkit-0.0.1a2/tests/test_network_requests.py +0 -20
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/LICENSE +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/README.md +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/camoufox.py +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/create_browser/__init__.py +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/selenium.py +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/selenium_toolkit/__init__.py +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/selenium_toolkit/utils.py +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/utils.py +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit.egg-info/dependency_links.txt +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit.egg-info/requires.txt +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit.egg-info/top_level.txt +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/setup.cfg +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/tests/test_decorators.py +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/tests/test_open.py +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/tests/test_scroll_window.py +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/tests/test_session_data.py +0 -0
- {browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/tests/test_wait.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: browser-toolkit
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.1a3
|
|
4
4
|
Summary: Toolkit that provides a single inteface to interact with different browser automations.
|
|
5
5
|
Project-URL: Homepage, https://github.com/toriium/browser-toolkit
|
|
6
6
|
Project-URL: Documentation, https://github.com/toriium/browser-toolkit/blob/master/README.md
|
|
@@ -4,20 +4,51 @@ import inspect
|
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
from random import uniform
|
|
7
|
-
from typing import Self
|
|
7
|
+
from typing import Self, Any
|
|
8
|
+
|
|
9
|
+
from browser_toolkit.types import Cookie, BoundingBox
|
|
10
|
+
|
|
11
|
+
|
|
8
12
|
|
|
9
|
-
from browser_toolkit.types import Cookie
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
class BaseWebElement(ABC):
|
|
13
16
|
"""Base class for web elements"""
|
|
14
17
|
|
|
15
18
|
@abstractmethod
|
|
16
|
-
async def
|
|
19
|
+
async def get_text(self) -> str:
|
|
20
|
+
"""
|
|
21
|
+
Gets the text from the element
|
|
22
|
+
|
|
23
|
+
:return: str - text of the element
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
async def get_attribute(self, attribute: str) -> str | None:
|
|
28
|
+
"""
|
|
29
|
+
Gets the attribute from the element
|
|
30
|
+
|
|
31
|
+
:param attribute: string - attribute name
|
|
32
|
+
:return: str - attribute of the element
|
|
33
|
+
"""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
async def get_position(self) -> BoundingBox:
|
|
38
|
+
"""
|
|
39
|
+
Gets the position of the element
|
|
40
|
+
|
|
41
|
+
:return: BoundingBox - position of the element
|
|
42
|
+
"""
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
async def click(self, hold_time: int = 0) -> None:
|
|
17
48
|
"""
|
|
18
49
|
Clicks the element
|
|
19
50
|
|
|
20
|
-
:param
|
|
51
|
+
:param hold_time: int - time to keep the mouse button pressed in seconds (default: 0)
|
|
21
52
|
:return:
|
|
22
53
|
"""
|
|
23
54
|
pass
|
|
@@ -53,7 +84,7 @@ class BaseWebElement(ABC):
|
|
|
53
84
|
pass
|
|
54
85
|
|
|
55
86
|
@abstractmethod
|
|
56
|
-
async def
|
|
87
|
+
async def query(self, selector: str) -> Self | None:
|
|
57
88
|
"""
|
|
58
89
|
Queries the web_element and returns the first element matching the selector.
|
|
59
90
|
:param selector:
|
|
@@ -62,7 +93,7 @@ class BaseWebElement(ABC):
|
|
|
62
93
|
pass
|
|
63
94
|
|
|
64
95
|
@abstractmethod
|
|
65
|
-
async def
|
|
96
|
+
async def query_all(self, selector: str) -> list[Self]:
|
|
66
97
|
"""
|
|
67
98
|
Queries the web_element and returns all elements matching the selector.
|
|
68
99
|
:param selector:
|
|
@@ -168,24 +199,20 @@ class BaseBrowserToolkit(ABC, AutoDecorate):
|
|
|
168
199
|
|
|
169
200
|
# --------------------------- START selectors ---------------------------
|
|
170
201
|
@abstractmethod
|
|
171
|
-
async def
|
|
202
|
+
async def query(self, selector: str) -> BaseWebElement | None:
|
|
172
203
|
"""
|
|
173
204
|
Queries the page and returns the first element matching the selector.
|
|
174
|
-
If a web_element is provided, it queries within that element instead of the whole page.
|
|
175
205
|
:param selector:
|
|
176
|
-
:
|
|
177
|
-
:return:
|
|
206
|
+
:return: BaseWebElement | None
|
|
178
207
|
"""
|
|
179
208
|
pass
|
|
180
209
|
|
|
181
210
|
@abstractmethod
|
|
182
|
-
async def
|
|
211
|
+
async def query_all(self, selector: str) -> list[BaseWebElement]:
|
|
183
212
|
"""
|
|
184
213
|
Queries the page and returns all elements matching the selector.
|
|
185
|
-
|
|
186
|
-
:
|
|
187
|
-
:param web_element:
|
|
188
|
-
:return:
|
|
214
|
+
:param selector: str
|
|
215
|
+
:return: list[BaseWebElement]
|
|
189
216
|
"""
|
|
190
217
|
pass
|
|
191
218
|
|
|
@@ -205,12 +232,12 @@ class BaseBrowserToolkit(ABC, AutoDecorate):
|
|
|
205
232
|
pass
|
|
206
233
|
|
|
207
234
|
@abstractmethod
|
|
208
|
-
async def click(self, selector: str,
|
|
235
|
+
async def click(self, selector: str, hold_time: int = 0) -> None:
|
|
209
236
|
"""
|
|
210
237
|
Clicks the element matching the selector
|
|
211
238
|
|
|
212
239
|
:param selector: string - CSS selector or XPath
|
|
213
|
-
:param
|
|
240
|
+
:param hold_time: int - time to keep the mouse button pressed in seconds (default: 0)
|
|
214
241
|
:return:
|
|
215
242
|
"""
|
|
216
243
|
pass
|
|
@@ -337,7 +364,7 @@ class BaseBrowserToolkit(ABC, AutoDecorate):
|
|
|
337
364
|
"""
|
|
338
365
|
|
|
339
366
|
@abstractmethod
|
|
340
|
-
async def get_attribute(self, selector: str, attribute: str) -> str:
|
|
367
|
+
async def get_attribute(self, selector: str, attribute: str) -> str | None:
|
|
341
368
|
"""
|
|
342
369
|
Gets the attribute from the element matching the selector
|
|
343
370
|
|
|
@@ -381,7 +408,7 @@ class BaseBrowserToolkit(ABC, AutoDecorate):
|
|
|
381
408
|
|
|
382
409
|
# --------------------------- START scripts ---------------------------
|
|
383
410
|
@abstractmethod
|
|
384
|
-
async def execute_script(self, script: str) ->
|
|
411
|
+
async def execute_script(self, script: str) -> Any:
|
|
385
412
|
"""
|
|
386
413
|
Executes the JavaScript script in the context of the current page
|
|
387
414
|
|
|
@@ -391,7 +418,7 @@ class BaseBrowserToolkit(ABC, AutoDecorate):
|
|
|
391
418
|
pass
|
|
392
419
|
|
|
393
420
|
@abstractmethod
|
|
394
|
-
async def execute_cdp_cmd(self, cmd: str, params: dict) ->
|
|
421
|
+
async def execute_cdp_cmd(self, cmd: str, params: dict) -> Any:
|
|
395
422
|
"""
|
|
396
423
|
Executes the Chrome DevTools Protocol command in the context of the current page
|
|
397
424
|
|
|
@@ -578,7 +605,7 @@ class BaseBrowserToolkit(ABC, AutoDecorate):
|
|
|
578
605
|
"""
|
|
579
606
|
pass
|
|
580
607
|
|
|
581
|
-
async def
|
|
608
|
+
async def get_local_storage(self, name: str) -> str | None:
|
|
582
609
|
"""
|
|
583
610
|
Gets a local storage item value by name
|
|
584
611
|
|
|
@@ -588,4 +615,33 @@ class BaseBrowserToolkit(ABC, AutoDecorate):
|
|
|
588
615
|
local_storage = await self.get_all_local_storage()
|
|
589
616
|
return local_storage.get(name, None)
|
|
590
617
|
|
|
618
|
+
async def set_local_storage(self, name: str, value: str) -> None:
|
|
619
|
+
"""
|
|
620
|
+
Sets a local storage item
|
|
621
|
+
|
|
622
|
+
:param name:
|
|
623
|
+
:param value:
|
|
624
|
+
:return:
|
|
625
|
+
"""
|
|
626
|
+
script = f'localStorage.setItem("{name}", "{value}");'
|
|
627
|
+
await self.execute_script(script)
|
|
628
|
+
|
|
629
|
+
async def delete_all_local_storage(self) -> None:
|
|
630
|
+
"""
|
|
631
|
+
Deletes all local storage items
|
|
632
|
+
:return:
|
|
633
|
+
"""
|
|
634
|
+
script = 'localStorage.clear();'
|
|
635
|
+
await self.execute_script(script)
|
|
636
|
+
|
|
637
|
+
async def delete_local_storage(self, name: str) -> None:
|
|
638
|
+
"""
|
|
639
|
+
Deletes a local storage item by name
|
|
640
|
+
|
|
641
|
+
:param name:
|
|
642
|
+
:return:
|
|
643
|
+
"""
|
|
644
|
+
script = f'localStorage.removeItem("{name}");'
|
|
645
|
+
await self.execute_script(script)
|
|
646
|
+
|
|
591
647
|
# --------------------------- END session data ---------------------------
|
{browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/create_browser/playwright.py
RENAMED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
from playwright.async_api import async_playwright, Browser, Page
|
|
2
3
|
from browser_toolkit.playwright import PlaywrightTollKit
|
|
3
4
|
|
|
@@ -19,9 +20,7 @@ async def get_playwright_toolkit() -> PlaywrightTollKit:
|
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
async def main():
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
print(await page.title())
|
|
27
|
-
await browser.close()
|
|
23
|
+
browser_toolkit = await get_playwright_toolkit()
|
|
24
|
+
await browser_toolkit.goto("https://www.google.com")
|
|
25
|
+
title = await browser_toolkit.title
|
|
26
|
+
await browser_toolkit.close_browser()
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
from typing import Self
|
|
1
|
+
from typing import Self, Any
|
|
2
2
|
|
|
3
3
|
from playwright._impl._api_structures import SetCookieParam
|
|
4
|
-
from playwright.async_api import Page, Browser, TimeoutError, ElementHandle, Cookie as PlaywrightCookie
|
|
4
|
+
from playwright.async_api import Page, Request, Browser, TimeoutError, ElementHandle, Cookie as PlaywrightCookie
|
|
5
5
|
|
|
6
6
|
from browser_toolkit.base_toolkit import BaseBrowserToolkit, BaseWebElement
|
|
7
|
-
from browser_toolkit.types import Cookie
|
|
7
|
+
from browser_toolkit.types import Cookie, BoundingBox
|
|
8
8
|
from browser_toolkit.utils import raise_not_implemented
|
|
9
9
|
|
|
10
10
|
|
|
@@ -12,14 +12,48 @@ class PlaywrightWebElement(BaseWebElement):
|
|
|
12
12
|
def __init__(self, web_element: ElementHandle):
|
|
13
13
|
self.web_element = web_element
|
|
14
14
|
|
|
15
|
-
async def
|
|
15
|
+
async def get_text(self) -> str:
|
|
16
|
+
"""
|
|
17
|
+
Gets the text from the element
|
|
18
|
+
|
|
19
|
+
:return: str - text of the element
|
|
20
|
+
"""
|
|
21
|
+
text = await self.web_element.text_content()
|
|
22
|
+
return text or ""
|
|
23
|
+
|
|
24
|
+
async def get_attribute(self, attribute: str) -> str | None:
|
|
25
|
+
"""
|
|
26
|
+
Gets the attribute from the element
|
|
27
|
+
|
|
28
|
+
:param attribute: string - attribute name
|
|
29
|
+
:return: str - attribute of the element
|
|
30
|
+
"""
|
|
31
|
+
return await self.web_element.get_attribute(name=attribute)
|
|
32
|
+
|
|
33
|
+
async def get_position(self) -> BoundingBox:
|
|
34
|
+
"""
|
|
35
|
+
Gets the position of the element
|
|
36
|
+
|
|
37
|
+
:return: BoundingBox - position of the element
|
|
38
|
+
"""
|
|
39
|
+
bounding_box = await self.web_element.bounding_box()
|
|
40
|
+
if not bounding_box:
|
|
41
|
+
raise ValueError("Could not get bounding box for element")
|
|
42
|
+
return BoundingBox(
|
|
43
|
+
x=bounding_box["x"],
|
|
44
|
+
y=bounding_box["y"],
|
|
45
|
+
width=bounding_box["width"],
|
|
46
|
+
height=bounding_box["height"],
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
async def click(self, hold_time: int = 0) -> None:
|
|
16
50
|
"""
|
|
17
51
|
Clicks the element
|
|
18
52
|
|
|
19
|
-
:param
|
|
53
|
+
:param hold_time: int - time to keep the mouse button pressed in seconds (default: 0)
|
|
20
54
|
:return:
|
|
21
55
|
"""
|
|
22
|
-
await self.web_element.click(delay=
|
|
56
|
+
await self.web_element.click(delay=hold_time*1000)
|
|
23
57
|
|
|
24
58
|
async def click_js(self) -> None:
|
|
25
59
|
"""
|
|
@@ -50,7 +84,7 @@ class PlaywrightWebElement(BaseWebElement):
|
|
|
50
84
|
"""
|
|
51
85
|
await self.web_element.fill("")
|
|
52
86
|
|
|
53
|
-
async def
|
|
87
|
+
async def query(self, selector: str) -> Self | None:
|
|
54
88
|
"""
|
|
55
89
|
Queries the web_element and returns the first element matching the selector.
|
|
56
90
|
:param selector:
|
|
@@ -61,7 +95,7 @@ class PlaywrightWebElement(BaseWebElement):
|
|
|
61
95
|
return PlaywrightWebElement(web_element=element)
|
|
62
96
|
return None
|
|
63
97
|
|
|
64
|
-
async def
|
|
98
|
+
async def query_all(self, selector: str) -> list[Self]:
|
|
65
99
|
"""
|
|
66
100
|
Queries the web_element and returns all elements matching the selector.
|
|
67
101
|
:param selector:
|
|
@@ -96,26 +130,22 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
96
130
|
|
|
97
131
|
# --------------------------- START selectors ---------------------------
|
|
98
132
|
|
|
99
|
-
async def
|
|
133
|
+
async def query(self, selector: str) -> BaseWebElement | None:
|
|
100
134
|
"""
|
|
101
135
|
Queries the page and returns the first element matching the selector.
|
|
102
|
-
If a web_element is provided, it queries within that element instead of the whole page.
|
|
103
136
|
:param selector:
|
|
104
|
-
:
|
|
105
|
-
:return:
|
|
137
|
+
:return: BaseWebElement | None
|
|
106
138
|
"""
|
|
107
139
|
element = await self.page.query_selector(selector=selector)
|
|
108
140
|
if element:
|
|
109
141
|
return PlaywrightWebElement(web_element=element)
|
|
110
142
|
return None
|
|
111
143
|
|
|
112
|
-
async def
|
|
144
|
+
async def query_all(self, selector: str) -> list[BaseWebElement]:
|
|
113
145
|
"""
|
|
114
146
|
Queries the page and returns all elements matching the selector.
|
|
115
|
-
|
|
116
|
-
:
|
|
117
|
-
:param web_element:
|
|
118
|
-
:return:
|
|
147
|
+
:param selector: str
|
|
148
|
+
:return: list[BaseWebElement]
|
|
119
149
|
"""
|
|
120
150
|
elements = await self.page.query_selector_all(selector=selector)
|
|
121
151
|
return [PlaywrightWebElement(web_element=element) for element in elements]
|
|
@@ -134,15 +164,15 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
134
164
|
"""
|
|
135
165
|
await self.page.goto(url=url, timeout=timeout * 1000)
|
|
136
166
|
|
|
137
|
-
async def click(self, selector: str,
|
|
167
|
+
async def click(self, selector: str, hold_time: int = 0) -> None:
|
|
138
168
|
"""
|
|
139
169
|
Clicks the element matching the selector
|
|
140
170
|
|
|
141
171
|
:param selector: string - CSS selector or XPath
|
|
142
|
-
:param
|
|
172
|
+
:param hold_time: int - time to keep the mouse button pressed in seconds (default: 0)
|
|
143
173
|
:return:
|
|
144
174
|
"""
|
|
145
|
-
await self.page.locator(selector=selector).click(delay=
|
|
175
|
+
await self.page.locator(selector=selector).click(delay=hold_time*1000)
|
|
146
176
|
|
|
147
177
|
async def click_js(self, selector: str) -> None:
|
|
148
178
|
"""
|
|
@@ -152,7 +182,7 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
152
182
|
:return:
|
|
153
183
|
"""
|
|
154
184
|
command = f"document.querySelector('{selector}').click()"
|
|
155
|
-
await self.
|
|
185
|
+
await self.execute_script(script=command)
|
|
156
186
|
|
|
157
187
|
async def type(self, text: str, selector: str, interval: float | int = 0, clear_before: bool = False) -> None:
|
|
158
188
|
"""
|
|
@@ -184,8 +214,8 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
184
214
|
:param selector: string - CSS selector or XPath
|
|
185
215
|
:return:
|
|
186
216
|
"""
|
|
187
|
-
|
|
188
|
-
await self.
|
|
217
|
+
script = f"document.querySelector('{selector}').scrollIntoView()"
|
|
218
|
+
await self.execute_script(script=script)
|
|
189
219
|
|
|
190
220
|
async def scroll_to_top(self) -> None:
|
|
191
221
|
"""
|
|
@@ -193,7 +223,8 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
193
223
|
|
|
194
224
|
:return:
|
|
195
225
|
"""
|
|
196
|
-
|
|
226
|
+
script = "window.scrollTo(0, 0)"
|
|
227
|
+
await self.execute_script(script=script)
|
|
197
228
|
|
|
198
229
|
async def scroll_to_bottom(self) -> None:
|
|
199
230
|
"""
|
|
@@ -201,7 +232,8 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
201
232
|
|
|
202
233
|
:return:
|
|
203
234
|
"""
|
|
204
|
-
|
|
235
|
+
script = "window.scrollTo(0, document.body.scrollHeight);"
|
|
236
|
+
await self.execute_script(script=script)
|
|
205
237
|
|
|
206
238
|
async def reload(self) -> None:
|
|
207
239
|
"""
|
|
@@ -217,8 +249,8 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
217
249
|
|
|
218
250
|
:return:
|
|
219
251
|
"""
|
|
220
|
-
|
|
221
|
-
await self.
|
|
252
|
+
script = "window.location.reload()"
|
|
253
|
+
await self.execute_script(script=script)
|
|
222
254
|
|
|
223
255
|
# --------------------------- END Actions ---------------------------
|
|
224
256
|
|
|
@@ -257,9 +289,10 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
257
289
|
:param selector: string - CSS selector or XPath
|
|
258
290
|
:return: str - text of the element
|
|
259
291
|
"""
|
|
260
|
-
|
|
292
|
+
text = await self.page.locator(selector=selector).text_content()
|
|
293
|
+
return text or ""
|
|
261
294
|
|
|
262
|
-
async def get_attribute(self, selector: str, attribute: str) -> str:
|
|
295
|
+
async def get_attribute(self, selector: str, attribute: str) -> str | None:
|
|
263
296
|
"""
|
|
264
297
|
Gets the attribute from the element matching the selector
|
|
265
298
|
|
|
@@ -281,12 +314,13 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
281
314
|
|
|
282
315
|
# --------------------------- START network ---------------------------
|
|
283
316
|
|
|
284
|
-
async def get_network_requests(self) -> list[
|
|
317
|
+
async def get_network_requests(self) -> list[Request]:
|
|
285
318
|
"""
|
|
286
319
|
Get all network Requests
|
|
287
320
|
:return:
|
|
288
321
|
"""
|
|
289
|
-
|
|
322
|
+
# return await self.page.requests()
|
|
323
|
+
pass
|
|
290
324
|
|
|
291
325
|
async def get_network_response_body(self, request_id: str) -> str:
|
|
292
326
|
"""
|
|
@@ -300,7 +334,7 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
300
334
|
# --------------------------- END network ---------------------------
|
|
301
335
|
|
|
302
336
|
# --------------------------- START scripts ---------------------------
|
|
303
|
-
async def execute_script(self, script: str) ->
|
|
337
|
+
async def execute_script(self, script: str) -> Any:
|
|
304
338
|
"""
|
|
305
339
|
Executes the JavaScript script in the context of the current page
|
|
306
340
|
|
|
@@ -309,7 +343,7 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
309
343
|
"""
|
|
310
344
|
return await self.page.evaluate(expression=script)
|
|
311
345
|
|
|
312
|
-
async def execute_cdp_cmd(self, cmd: str, params: dict) ->
|
|
346
|
+
async def execute_cdp_cmd(self, cmd: str, params: dict) -> Any:
|
|
313
347
|
"""
|
|
314
348
|
Executes the Chrome DevTools Protocol command in the context of the current page
|
|
315
349
|
|
|
@@ -317,7 +351,8 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
317
351
|
:param params: dict - parameters for the CDP command
|
|
318
352
|
:return:
|
|
319
353
|
"""
|
|
320
|
-
|
|
354
|
+
client = await self.page.context.new_cdp_session(self.page)
|
|
355
|
+
return await client.send(cmd, params=params)
|
|
321
356
|
|
|
322
357
|
# --------------------------- END scripts ---------------------------
|
|
323
358
|
|
|
@@ -501,9 +536,10 @@ class PlaywrightTollKit(BaseBrowserToolkit):
|
|
|
501
536
|
async def get_all_local_storage(self) -> dict:
|
|
502
537
|
"""
|
|
503
538
|
Gets all local storage
|
|
504
|
-
:return:
|
|
539
|
+
:return: list[Cookie]
|
|
505
540
|
"""
|
|
506
|
-
|
|
541
|
+
cmd = "() => Object.fromEntries(Object.entries(localStorage))"
|
|
542
|
+
local_storage = await self.execute_script(script=cmd)
|
|
507
543
|
if not isinstance(local_storage, dict):
|
|
508
544
|
local_storage = {}
|
|
509
545
|
return local_storage
|
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
from typing import Self, Any
|
|
2
|
+
|
|
3
|
+
from pydoll.protocol.network.types import CookieParam, Cookie as PydollCookie
|
|
4
|
+
from pydoll.browser.tab import Tab
|
|
5
|
+
from pydoll.browser.chromium.chrome import Chrome
|
|
6
|
+
from pydoll.elements.web_element import WebElement
|
|
7
|
+
|
|
8
|
+
from browser_toolkit.base_toolkit import BaseBrowserToolkit, BaseWebElement
|
|
9
|
+
from browser_toolkit.types import Cookie, BoundingBox
|
|
10
|
+
from browser_toolkit.utils import raise_not_implemented
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PydollWebElement(BaseWebElement):
|
|
14
|
+
def __init__(self, web_element: WebElement):
|
|
15
|
+
self.web_element = web_element
|
|
16
|
+
|
|
17
|
+
async def get_text(self) -> str:
|
|
18
|
+
"""
|
|
19
|
+
Gets the text from the element
|
|
20
|
+
|
|
21
|
+
:return: str - text of the element
|
|
22
|
+
"""
|
|
23
|
+
text = await self.web_element.text
|
|
24
|
+
return text or ""
|
|
25
|
+
|
|
26
|
+
async def get_attribute(self, attribute: str) -> str | None:
|
|
27
|
+
"""
|
|
28
|
+
Gets the attribute from the element
|
|
29
|
+
|
|
30
|
+
:param attribute: string - attribute name
|
|
31
|
+
:return: str - attribute of the element
|
|
32
|
+
"""
|
|
33
|
+
return self.web_element.get_attribute(name=attribute)
|
|
34
|
+
|
|
35
|
+
async def get_position(self) -> BoundingBox:
|
|
36
|
+
"""
|
|
37
|
+
Gets the position of the element
|
|
38
|
+
|
|
39
|
+
:return: BoundingBox - position of the element
|
|
40
|
+
"""
|
|
41
|
+
bounding_box = await self.web_element.bounds
|
|
42
|
+
if not bounding_box:
|
|
43
|
+
raise ValueError("Could not get bounding box for element")
|
|
44
|
+
return BoundingBox(
|
|
45
|
+
x=bounding_box["x"],
|
|
46
|
+
y=bounding_box["y"],
|
|
47
|
+
width=bounding_box["width"],
|
|
48
|
+
height=bounding_box["height"],
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
async def click(self, hold_time: int = 0) -> None:
|
|
52
|
+
"""
|
|
53
|
+
Clicks the element
|
|
54
|
+
|
|
55
|
+
:param hold_time: int - time to keep the mouse button pressed in seconds (default: 0)
|
|
56
|
+
:return:
|
|
57
|
+
"""
|
|
58
|
+
await self.web_element.click(hold_time=hold_time, humanize=True)
|
|
59
|
+
|
|
60
|
+
async def click_js(self) -> None:
|
|
61
|
+
"""
|
|
62
|
+
Clicks the element using JavaScript
|
|
63
|
+
|
|
64
|
+
:return:
|
|
65
|
+
"""
|
|
66
|
+
await self.web_element.click_using_js()
|
|
67
|
+
|
|
68
|
+
async def type(self, text: str, interval: float | int = 0, clear_before: bool = False) -> None:
|
|
69
|
+
"""
|
|
70
|
+
Fills the element with the text
|
|
71
|
+
|
|
72
|
+
:param text: string - text to fill
|
|
73
|
+
:param interval: float or int - time to wait in seconds between each character
|
|
74
|
+
:param clear_before: bool - whether to clear the field before filling
|
|
75
|
+
:return:
|
|
76
|
+
"""
|
|
77
|
+
if clear_before:
|
|
78
|
+
await self.clear()
|
|
79
|
+
await self.web_element.type_text(text=text, humanize=True, interval=interval)
|
|
80
|
+
|
|
81
|
+
async def clear(self) -> None:
|
|
82
|
+
"""
|
|
83
|
+
Clears the element
|
|
84
|
+
|
|
85
|
+
:return:
|
|
86
|
+
"""
|
|
87
|
+
await self.web_element.clear()
|
|
88
|
+
|
|
89
|
+
async def query(self, selector: str) -> Self | None:
|
|
90
|
+
"""
|
|
91
|
+
Queries the web_element and returns the first element matching the selector.
|
|
92
|
+
:param selector:
|
|
93
|
+
:return:
|
|
94
|
+
"""
|
|
95
|
+
element = await self.web_element.query(expression=selector, timeout=0, find_all=False, raise_exc=False)
|
|
96
|
+
if element:
|
|
97
|
+
return PydollWebElement(web_element=element)
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
async def query_all(self, selector: str) -> list[Self]:
|
|
101
|
+
"""
|
|
102
|
+
Queries the web_element and returns all elements matching the selector.
|
|
103
|
+
:param selector:
|
|
104
|
+
:return:
|
|
105
|
+
"""
|
|
106
|
+
elements = await self.web_element.query(expression=selector, timeout=0, find_all=True, raise_exc=False)
|
|
107
|
+
return [PydollWebElement(web_element=element) for element in elements]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class PydollTollKit(BaseBrowserToolkit):
|
|
111
|
+
def __init__(self, browser: Chrome, page: Tab, *args, **kwargs):
|
|
112
|
+
self.browser: Chrome = browser
|
|
113
|
+
self.page: Tab = page
|
|
114
|
+
|
|
115
|
+
# --------------------------- START session management ---------------------------
|
|
116
|
+
|
|
117
|
+
async def close_page(self) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Closes the browser tab
|
|
120
|
+
:return:
|
|
121
|
+
"""
|
|
122
|
+
await self.page.close()
|
|
123
|
+
|
|
124
|
+
async def close_browser(self) -> None:
|
|
125
|
+
"""
|
|
126
|
+
Closes the browser process and all its pages
|
|
127
|
+
:return:
|
|
128
|
+
"""
|
|
129
|
+
await self.browser.stop()
|
|
130
|
+
|
|
131
|
+
# --------------------------- END session management ---------------------------
|
|
132
|
+
|
|
133
|
+
# --------------------------- START selectors ---------------------------
|
|
134
|
+
|
|
135
|
+
async def query(self, selector: str) -> BaseWebElement | None:
|
|
136
|
+
"""
|
|
137
|
+
Queries the page and returns the first element matching the selector.
|
|
138
|
+
:param selector:
|
|
139
|
+
:return: BaseWebElement | None
|
|
140
|
+
"""
|
|
141
|
+
element = await self.page.query(expression=selector, timeout=0, find_all=False, raise_exc=False)
|
|
142
|
+
if element:
|
|
143
|
+
return PydollWebElement(web_element=element)
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
async def query_all(self, selector: str) -> list[BaseWebElement]:
|
|
147
|
+
"""
|
|
148
|
+
Queries the page and returns all elements matching the selector.
|
|
149
|
+
:param selector: str
|
|
150
|
+
:return: list[BaseWebElement]
|
|
151
|
+
"""
|
|
152
|
+
elements = await self.page.query(expression=selector, timeout=0, find_all=True, raise_exc=False)
|
|
153
|
+
return [PydollWebElement(web_element=element) for element in elements]
|
|
154
|
+
|
|
155
|
+
# --------------------------- END selectors ---------------------------
|
|
156
|
+
|
|
157
|
+
# --------------------------- START Actions ---------------------------
|
|
158
|
+
|
|
159
|
+
async def goto(self, url: str, timeout: int = 30) -> None:
|
|
160
|
+
"""
|
|
161
|
+
Navigates to URL
|
|
162
|
+
|
|
163
|
+
:param url: url to navigate to
|
|
164
|
+
:param timeout: maximum time to wait for the page to load in seconds
|
|
165
|
+
:return:
|
|
166
|
+
"""
|
|
167
|
+
await self.page.go_to(url=url, timeout=timeout)
|
|
168
|
+
|
|
169
|
+
async def click(self, selector: str, hold_time: int = 0) -> None:
|
|
170
|
+
"""
|
|
171
|
+
Clicks the element matching the selector
|
|
172
|
+
|
|
173
|
+
:param selector: string - CSS selector or XPath
|
|
174
|
+
:param hold_time: int - time to keep the mouse button pressed in seconds (default: 0)
|
|
175
|
+
:return:
|
|
176
|
+
"""
|
|
177
|
+
element = await self.page.query(expression=selector, timeout=0, find_all=False, raise_exc=True)
|
|
178
|
+
await element.click(hold_time=hold_time, humanize=True)
|
|
179
|
+
|
|
180
|
+
async def click_js(self, selector: str) -> None:
|
|
181
|
+
"""
|
|
182
|
+
Clicks the element matching the selector using JavaScript
|
|
183
|
+
|
|
184
|
+
:param selector: string - CSS selector or XPath
|
|
185
|
+
:return:
|
|
186
|
+
"""
|
|
187
|
+
element = await self.page.query(expression=selector, timeout=0, find_all=False, raise_exc=True)
|
|
188
|
+
await element.click_using_js()
|
|
189
|
+
|
|
190
|
+
async def type(self, text: str, selector: str, interval: float | int = 0, clear_before: bool = False) -> None:
|
|
191
|
+
"""
|
|
192
|
+
Fills the element matching the selector with the text
|
|
193
|
+
|
|
194
|
+
:param text: string - text to fill
|
|
195
|
+
:param selector: string - CSS selector or XPath
|
|
196
|
+
:param interval: float or int - time to wait in seconds between each character
|
|
197
|
+
:param clear_before: bool - whether to clear the field before filling
|
|
198
|
+
:return:
|
|
199
|
+
"""
|
|
200
|
+
if clear_before:
|
|
201
|
+
await self.clear(selector=selector)
|
|
202
|
+
|
|
203
|
+
element = await self.page.query(expression=selector, timeout=0, find_all=False, raise_exc=True)
|
|
204
|
+
await element.type_text(text=text, humanize=True, interval=interval)
|
|
205
|
+
|
|
206
|
+
async def clear(self, selector: str) -> None:
|
|
207
|
+
"""
|
|
208
|
+
Clears the element matching the selector
|
|
209
|
+
|
|
210
|
+
:param selector: string - CSS selector or XPath
|
|
211
|
+
:return:
|
|
212
|
+
"""
|
|
213
|
+
element = await self.page.query(expression=selector, timeout=0, find_all=False, raise_exc=True)
|
|
214
|
+
await element.clear()
|
|
215
|
+
|
|
216
|
+
async def scroll_to_element(self, selector: str) -> None:
|
|
217
|
+
"""
|
|
218
|
+
Scrolls to the element matching the selector
|
|
219
|
+
|
|
220
|
+
:param selector: string - CSS selector or XPath
|
|
221
|
+
:return:
|
|
222
|
+
"""
|
|
223
|
+
command = f"document.querySelector('{selector}').scrollIntoView()"
|
|
224
|
+
await self.execute_script(script=command)
|
|
225
|
+
|
|
226
|
+
async def scroll_to_top(self) -> None:
|
|
227
|
+
"""
|
|
228
|
+
Scrolls to the top of the page
|
|
229
|
+
|
|
230
|
+
:return:
|
|
231
|
+
"""
|
|
232
|
+
cmd = "window.scrollTo(0, 0)"
|
|
233
|
+
await self.execute_script(script=cmd)
|
|
234
|
+
|
|
235
|
+
async def scroll_to_bottom(self) -> None:
|
|
236
|
+
"""
|
|
237
|
+
Scrolls to the bottom of the page
|
|
238
|
+
|
|
239
|
+
:return:
|
|
240
|
+
"""
|
|
241
|
+
cmd = "window.scrollTo(0, document.body.scrollHeight);"
|
|
242
|
+
await self.execute_script(script=cmd)
|
|
243
|
+
|
|
244
|
+
async def reload(self) -> None:
|
|
245
|
+
"""
|
|
246
|
+
Reloads the page
|
|
247
|
+
|
|
248
|
+
:return:
|
|
249
|
+
"""
|
|
250
|
+
await self.page.refresh(ignore_cache=False)
|
|
251
|
+
|
|
252
|
+
async def hard_reload(self) -> None:
|
|
253
|
+
"""
|
|
254
|
+
Hard reloads the page (ignoring cache)
|
|
255
|
+
|
|
256
|
+
:return:
|
|
257
|
+
"""
|
|
258
|
+
await self.page.refresh(ignore_cache=True)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
# --------------------------- END Actions ---------------------------
|
|
262
|
+
|
|
263
|
+
# --------------------------- START page data ---------------------------
|
|
264
|
+
@property
|
|
265
|
+
async def current_url(self) -> str:
|
|
266
|
+
"""
|
|
267
|
+
Gets the current URL
|
|
268
|
+
|
|
269
|
+
:return: str - current URL
|
|
270
|
+
"""
|
|
271
|
+
return await self.page.current_url
|
|
272
|
+
|
|
273
|
+
@property
|
|
274
|
+
async def title(self) -> str:
|
|
275
|
+
"""
|
|
276
|
+
Gets the page title
|
|
277
|
+
|
|
278
|
+
:return: str - page title
|
|
279
|
+
"""
|
|
280
|
+
return await self.page.title
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
async def page_source(self) -> str:
|
|
284
|
+
"""
|
|
285
|
+
Gets the page source
|
|
286
|
+
|
|
287
|
+
:return: str - page source
|
|
288
|
+
"""
|
|
289
|
+
return await self.page.page_source
|
|
290
|
+
|
|
291
|
+
async def get_text(self, selector: str) -> str:
|
|
292
|
+
"""
|
|
293
|
+
Gets the text from the element matching the selector
|
|
294
|
+
|
|
295
|
+
:param selector: string - CSS selector or XPath
|
|
296
|
+
:return: str - text of the element
|
|
297
|
+
"""
|
|
298
|
+
element = await self.page.query(expression=selector, timeout=0, find_all=False, raise_exc=True)
|
|
299
|
+
return await element.text
|
|
300
|
+
|
|
301
|
+
async def get_attribute(self, selector: str, attribute: str) -> str | None:
|
|
302
|
+
"""
|
|
303
|
+
Gets the attribute from the element matching the selector
|
|
304
|
+
|
|
305
|
+
:param selector: str - CSS selector or XPath
|
|
306
|
+
:param attribute: str - attribute name
|
|
307
|
+
:return: str - attribute of the element
|
|
308
|
+
"""
|
|
309
|
+
element = await self.page.query(expression=selector, timeout=0, find_all=False, raise_exc=True)
|
|
310
|
+
return element.get_attribute(name=attribute)
|
|
311
|
+
|
|
312
|
+
async def save_screenshot(self, file_path: str) -> None:
|
|
313
|
+
"""
|
|
314
|
+
Takes a screenshot of the current page and saves it to the exception directory if it is set
|
|
315
|
+
:param file_path:
|
|
316
|
+
:return:
|
|
317
|
+
"""
|
|
318
|
+
await self.page.take_screenshot(path=file_path, quality=100)
|
|
319
|
+
|
|
320
|
+
# --------------------------- END page data ---------------------------
|
|
321
|
+
|
|
322
|
+
# --------------------------- START network ---------------------------
|
|
323
|
+
|
|
324
|
+
async def get_network_requests(self) -> list[dict]:
|
|
325
|
+
"""
|
|
326
|
+
Get all network Requests
|
|
327
|
+
:return:
|
|
328
|
+
"""
|
|
329
|
+
return await self.page.get_network_logs()
|
|
330
|
+
pass
|
|
331
|
+
|
|
332
|
+
async def get_network_response_body(self, request_id: str) -> str:
|
|
333
|
+
"""
|
|
334
|
+
Gets the response body of the network request with the given request ID
|
|
335
|
+
|
|
336
|
+
:param request_id: string - network request ID
|
|
337
|
+
:return: str - response body of the network request
|
|
338
|
+
"""
|
|
339
|
+
return await self.page.get_network_response_body(request_id=request_id)
|
|
340
|
+
|
|
341
|
+
# --------------------------- END network ---------------------------
|
|
342
|
+
|
|
343
|
+
# --------------------------- START scripts ---------------------------
|
|
344
|
+
async def execute_script(self, script: str) -> Any:
|
|
345
|
+
"""
|
|
346
|
+
Executes the JavaScript script in the context of the current page
|
|
347
|
+
|
|
348
|
+
:param script: string - JavaScript code to execute
|
|
349
|
+
:return:
|
|
350
|
+
"""
|
|
351
|
+
return await self.page.execute_script(script=script)
|
|
352
|
+
|
|
353
|
+
async def execute_cdp_cmd(self, cmd: str, params: dict) -> Any:
|
|
354
|
+
"""
|
|
355
|
+
Executes the Chrome DevTools Protocol command in the context of the current page
|
|
356
|
+
|
|
357
|
+
:param cmd: string - CDP command to execute
|
|
358
|
+
:param params: dict - parameters for the CDP command
|
|
359
|
+
:return:
|
|
360
|
+
"""
|
|
361
|
+
raise_not_implemented()
|
|
362
|
+
|
|
363
|
+
# --------------------------- END scripts ---------------------------
|
|
364
|
+
|
|
365
|
+
# --------------------------- START wait ---------------------------
|
|
366
|
+
async def element_is_present(self, selector: str, timeout: int) -> bool:
|
|
367
|
+
"""
|
|
368
|
+
Checks if the element matching the selector is present
|
|
369
|
+
|
|
370
|
+
:param selector: string - CSS selector or XPath
|
|
371
|
+
:param timeout: int - seconds to wait for the element
|
|
372
|
+
:return: bool - whether the element is present
|
|
373
|
+
"""
|
|
374
|
+
element = await self.page.query(expression=selector, timeout=timeout, find_all=True, raise_exc=False)
|
|
375
|
+
return bool(element)
|
|
376
|
+
|
|
377
|
+
async def element_is_visible(self, selector: str, timeout: int) -> bool:
|
|
378
|
+
"""
|
|
379
|
+
Checks if the element matching the selector is visible
|
|
380
|
+
|
|
381
|
+
:param selector: string - CSS selector or XPath
|
|
382
|
+
:param timeout: int - seconds to wait for the element
|
|
383
|
+
:return: bool - whether the element is visible
|
|
384
|
+
"""
|
|
385
|
+
element = await self.page.query(expression=selector, timeout=timeout, find_all=True, raise_exc=False)
|
|
386
|
+
return bool(element)
|
|
387
|
+
|
|
388
|
+
async def element_is_invisible(self, selector: str, timeout: int) -> bool:
|
|
389
|
+
"""
|
|
390
|
+
Checks if the element matching the selector is invisible
|
|
391
|
+
|
|
392
|
+
:param selector: string - CSS selector or XPath
|
|
393
|
+
:param timeout: int - seconds to wait for the element
|
|
394
|
+
:return: bool - whether the element is invisible
|
|
395
|
+
"""
|
|
396
|
+
element = await self.page.query(expression=selector, timeout=timeout, find_all=True, raise_exc=False)
|
|
397
|
+
return True if element is None else False
|
|
398
|
+
|
|
399
|
+
async def element_is_clickable(self, selector: str, timeout: int) -> bool:
|
|
400
|
+
"""
|
|
401
|
+
Checks if the element matching the selector is clickable
|
|
402
|
+
|
|
403
|
+
:param selector: string - CSS selector or XPath
|
|
404
|
+
:param timeout: int - seconds to wait for the element
|
|
405
|
+
:return: bool - whether the element is clickable
|
|
406
|
+
"""
|
|
407
|
+
element = await self.page.query(expression=selector, timeout=timeout, find_all=False, raise_exc=False)
|
|
408
|
+
if not element:
|
|
409
|
+
return False
|
|
410
|
+
return await element.is_visible()
|
|
411
|
+
|
|
412
|
+
async def text_is_present(self, text: str, selector: str, timeout: int) -> bool:
|
|
413
|
+
"""
|
|
414
|
+
Checks if the text is present in the element matching the selector
|
|
415
|
+
|
|
416
|
+
:param text: string - text to check
|
|
417
|
+
:param selector: string - CSS selector or XPath
|
|
418
|
+
:param timeout: int - seconds to wait for the element
|
|
419
|
+
:return: bool - whether the text is present in the element
|
|
420
|
+
"""
|
|
421
|
+
element = await self.query(selector=selector)
|
|
422
|
+
if not element:
|
|
423
|
+
return False
|
|
424
|
+
element_text = await element.get_text()
|
|
425
|
+
return text in element_text
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
async def alert_is_present(self, timeout: int, message: str) -> bool:
|
|
429
|
+
"""
|
|
430
|
+
Checks if an alert is present
|
|
431
|
+
|
|
432
|
+
:param timeout: int - seconds to wait for the alert
|
|
433
|
+
:param message: str - alert message
|
|
434
|
+
:return: bool - whether an alert is present
|
|
435
|
+
"""
|
|
436
|
+
raise_not_implemented()
|
|
437
|
+
|
|
438
|
+
async def page_is_loading(self, timeout: int) -> bool:
|
|
439
|
+
"""
|
|
440
|
+
Checks if the page is ready
|
|
441
|
+
|
|
442
|
+
:param timeout: int - seconds to wait for the page to be ready
|
|
443
|
+
:return: bool - whether the page is ready
|
|
444
|
+
"""
|
|
445
|
+
cmd = "return document.readyState"
|
|
446
|
+
if self.page.execute_script(cmd) != "complete":
|
|
447
|
+
return True
|
|
448
|
+
else:
|
|
449
|
+
return False
|
|
450
|
+
|
|
451
|
+
# --------------------------- END wait ---------------------------
|
|
452
|
+
|
|
453
|
+
# --------------------------- START session data ---------------------------
|
|
454
|
+
async def get_all_cookies(self) -> list[Cookie]:
|
|
455
|
+
"""
|
|
456
|
+
Gets all cookies
|
|
457
|
+
:return: list[Cookie]
|
|
458
|
+
"""
|
|
459
|
+
raw_cookies: list[PydollCookie] = await self.page.get_cookies()
|
|
460
|
+
transformed_cookies: list[Cookie] = []
|
|
461
|
+
for raw_cookie in raw_cookies:
|
|
462
|
+
transformed_cookie = Cookie(
|
|
463
|
+
name=raw_cookie.get("name"),
|
|
464
|
+
value=raw_cookie.get("value"),
|
|
465
|
+
url=raw_cookie.get("url"),
|
|
466
|
+
domain=raw_cookie.get("domain"),
|
|
467
|
+
path=raw_cookie.get("path"),
|
|
468
|
+
expires=raw_cookie.get("expires"),
|
|
469
|
+
httpOnly=raw_cookie.get("httpOnly"),
|
|
470
|
+
secure=raw_cookie.get("secure"),
|
|
471
|
+
sameSite=raw_cookie.get("sameSite"),
|
|
472
|
+
partitionKey=raw_cookie.get("partitionKey"),
|
|
473
|
+
)
|
|
474
|
+
transformed_cookies.append(transformed_cookie)
|
|
475
|
+
|
|
476
|
+
return transformed_cookies
|
|
477
|
+
|
|
478
|
+
async def add_cookie(self, cookie: Cookie) -> None:
|
|
479
|
+
"""
|
|
480
|
+
Adds a cookie to the current session
|
|
481
|
+
|
|
482
|
+
:param cookie: Cookie - cookie to add
|
|
483
|
+
:return:
|
|
484
|
+
"""
|
|
485
|
+
transformed_cookie: CookieParam = CookieParam(
|
|
486
|
+
name=cookie.name,
|
|
487
|
+
value=cookie.value,
|
|
488
|
+
url=cookie.url,
|
|
489
|
+
domain=cookie.domain,
|
|
490
|
+
path=cookie.path,
|
|
491
|
+
expires=cookie.expires,
|
|
492
|
+
httpOnly=cookie.httpOnly,
|
|
493
|
+
secure=cookie.secure,
|
|
494
|
+
sameSite=cookie.sameSite,
|
|
495
|
+
partitionKey=cookie.partitionKey,
|
|
496
|
+
)
|
|
497
|
+
await self.page.set_cookies(cookies=[transformed_cookie])
|
|
498
|
+
|
|
499
|
+
async def delete_all_cookies(self) -> None:
|
|
500
|
+
"""
|
|
501
|
+
Deletes all cookies from the current session
|
|
502
|
+
:return:
|
|
503
|
+
"""
|
|
504
|
+
await self.page.delete_all_cookies()
|
|
505
|
+
|
|
506
|
+
async def delete_cookie_by_name(self, name: str) -> None:
|
|
507
|
+
"""
|
|
508
|
+
Deletes a cookie by name
|
|
509
|
+
|
|
510
|
+
:param name: string - name of the cookie to delete
|
|
511
|
+
:return:
|
|
512
|
+
"""
|
|
513
|
+
raise_not_implemented()
|
|
514
|
+
|
|
515
|
+
async def delete_cookie_filter(
|
|
516
|
+
self, name: str | None = None, domain: str | None = None, path: str | None = None
|
|
517
|
+
) -> None:
|
|
518
|
+
"""
|
|
519
|
+
Deletes cookies by name, domain, and path
|
|
520
|
+
|
|
521
|
+
:param name:
|
|
522
|
+
:param domain:
|
|
523
|
+
:param path:
|
|
524
|
+
:return:
|
|
525
|
+
"""
|
|
526
|
+
raise_not_implemented()
|
|
527
|
+
|
|
528
|
+
async def get_all_local_storage(self) -> dict:
|
|
529
|
+
"""
|
|
530
|
+
Gets all local storage
|
|
531
|
+
:return: dict
|
|
532
|
+
"""
|
|
533
|
+
cmd = "() => Object.fromEntries(Object.entries(localStorage))"
|
|
534
|
+
raw = await self.page.execute_script(script=cmd, return_by_value=True)
|
|
535
|
+
if not isinstance(raw, dict):
|
|
536
|
+
return {}
|
|
537
|
+
local_storage = raw["result"]["result"]["value"]
|
|
538
|
+
return local_storage
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
# --------------------------- END session data ---------------------------
|
|
@@ -225,7 +225,8 @@ class SeleniumToolKit:
|
|
|
225
225
|
return False
|
|
226
226
|
|
|
227
227
|
def page_is_loading(self) -> bool:
|
|
228
|
-
|
|
228
|
+
cmd = "return document.readyState"
|
|
229
|
+
if self.__driver.execute_script(cmd) != "complete":
|
|
229
230
|
return True
|
|
230
231
|
else:
|
|
231
232
|
return False
|
|
@@ -2,9 +2,18 @@ from dataclasses import dataclass
|
|
|
2
2
|
from enum import StrEnum
|
|
3
3
|
from typing import Literal
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
class BoundingBox(BaseModel):
|
|
9
|
+
"""
|
|
10
|
+
Represents the position and size of an element on the page.
|
|
11
|
+
"""
|
|
12
|
+
x: float = Field(description="the x coordinate of the element in pixels")
|
|
13
|
+
y: float = Field(description="the y coordinate of the element in pixels")
|
|
14
|
+
width: float = Field(description="the width of the element in pixels")
|
|
15
|
+
height: float = Field(description="the height of the element in pixels")
|
|
16
|
+
|
|
8
17
|
class Cookie(BaseModel):
|
|
9
18
|
name: str
|
|
10
19
|
value: str
|
|
@@ -41,6 +50,7 @@ class Redirect:
|
|
|
41
50
|
|
|
42
51
|
@dataclass
|
|
43
52
|
class Request:
|
|
53
|
+
method: str
|
|
44
54
|
url: str
|
|
45
55
|
request_id: str
|
|
46
56
|
cookies: dict
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: browser-toolkit
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.1a3
|
|
4
4
|
Summary: Toolkit that provides a single inteface to interact with different browser automations.
|
|
5
5
|
Project-URL: Homepage, https://github.com/toriium/browser-toolkit
|
|
6
6
|
Project-URL: Documentation, https://github.com/toriium/browser-toolkit/blob/master/README.md
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from browser_toolkit.create_browser.playwright import get_playwright_toolkit
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def test_network_requests():
|
|
7
|
+
btk = await get_playwright_toolkit()
|
|
8
|
+
|
|
9
|
+
await btk.goto("https://scrapingtest.com/ecommerce/pagination")
|
|
10
|
+
await asyncio.sleep(5)
|
|
11
|
+
|
|
12
|
+
all_requests = await btk.get_network_requests()
|
|
13
|
+
|
|
14
|
+
assert all_requests
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if __name__ == "__main__":
|
|
18
|
+
asyncio.run(test_network_requests())
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
|
|
3
|
-
from browser_toolkit.create_browser.playwright import get_playwright_toolkit
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
async def test_element_is_present():
|
|
7
|
-
btk = await get_playwright_toolkit()
|
|
8
|
-
|
|
9
|
-
await btk.goto("https://statusinvest.com.br/")
|
|
10
|
-
await asyncio.sleep(5)
|
|
11
|
-
|
|
12
|
-
request_data = await btk.get_requests(request_url="https://statusinvest.com.br/account/userdata")
|
|
13
|
-
value = await btk.get_response_body_from_request_id(request_id=request_data[0].request_id)
|
|
14
|
-
await btk.close()
|
|
15
|
-
|
|
16
|
-
assert value
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if __name__ == "__main__":
|
|
20
|
-
test_element_is_present()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/create_browser/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/selenium_toolkit/__init__.py
RENAMED
|
File without changes
|
{browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit/selenium_toolkit/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
{browser_toolkit-0.0.1a2 → browser_toolkit-0.0.1a3}/browser_toolkit.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|