|
5 | 5 |
|
6 | 6 |
|
7 | 7 | class BrowserCommand: |
| 8 | + """Base class for browser commands executed after a page has loaded. |
| 9 | +
|
| 10 | + Browser commands allow automated interaction with a web page when |
| 11 | + :attr:`~scrapi_sdk.models.ScrapeRequest.use_browser` is set to ``True``. |
| 12 | + Commands are executed in the order they are added to the request. |
| 13 | + """ |
| 14 | + |
8 | 15 | command_name: str |
| 16 | + """The command name that identifies the type of browser command.""" |
9 | 17 |
|
10 | 18 | def to_wire(self) -> dict[str, Any]: |
| 19 | + """Serialize this command to its API wire representation. |
| 20 | +
|
| 21 | + Returns: |
| 22 | + A dictionary containing the command payload for the ScrAPI API. |
| 23 | +
|
| 24 | + Raises: |
| 25 | + NotImplementedError: Must be implemented by subclasses. |
| 26 | + """ |
11 | 27 | raise NotImplementedError |
12 | 28 |
|
13 | 29 |
|
14 | 30 | @dataclass(slots=True) |
15 | 31 | class ClickCommand(BrowserCommand): |
| 32 | + """Browser command that clicks an element on the page. |
| 33 | +
|
| 34 | + Attributes: |
| 35 | + target_selector: The CSS or XPath selector used to find the element to click. |
| 36 | + command_name: The wire command name. Always ``"click"``. |
| 37 | + """ |
| 38 | + |
16 | 39 | target_selector: str = "" |
17 | 40 | command_name: str = "click" |
18 | 41 |
|
19 | 42 | def to_wire(self) -> dict[str, Any]: |
| 43 | + """Serialize the click command to its API wire representation. |
| 44 | +
|
| 45 | + Returns: |
| 46 | + A dictionary mapping ``"click"`` to the target selector. |
| 47 | + """ |
20 | 48 | return {self.command_name: self.target_selector} |
21 | 49 |
|
22 | 50 |
|
23 | 51 | @dataclass(slots=True) |
24 | 52 | class ScrollCommand(BrowserCommand): |
| 53 | + """Browser command that scrolls the page by a number of pixels. |
| 54 | +
|
| 55 | + Attributes: |
| 56 | + pixels: The number of pixels to scroll. Use negative values to scroll up. |
| 57 | + Defaults to 1000. |
| 58 | + command_name: The wire command name. Always ``"scroll"``. |
| 59 | + """ |
| 60 | + |
25 | 61 | pixels: int = 1000 |
26 | 62 | command_name: str = "scroll" |
27 | 63 |
|
28 | 64 | def to_wire(self) -> dict[str, Any]: |
| 65 | + """Serialize the scroll command to its API wire representation. |
| 66 | +
|
| 67 | + Returns: |
| 68 | + A dictionary mapping ``"scroll"`` to the pixel count. |
| 69 | + """ |
29 | 70 | return {self.command_name: self.pixels} |
30 | 71 |
|
31 | 72 |
|
32 | 73 | @dataclass(slots=True) |
33 | 74 | class WaitCommand(BrowserCommand): |
| 75 | + """Browser command that pauses execution for a fixed duration. |
| 76 | +
|
| 77 | + Attributes: |
| 78 | + milliseconds: The number of milliseconds to wait. Maximum is 15000. |
| 79 | + command_name: The wire command name. Always ``"wait"``. |
| 80 | + """ |
| 81 | + |
34 | 82 | milliseconds: int = 0 |
35 | 83 | command_name: str = "wait" |
36 | 84 |
|
37 | 85 | def to_wire(self) -> dict[str, Any]: |
| 86 | + """Serialize the wait command to its API wire representation. |
| 87 | +
|
| 88 | + Returns: |
| 89 | + A dictionary mapping ``"wait"`` to the millisecond duration. |
| 90 | + """ |
38 | 91 | return {self.command_name: self.milliseconds} |
39 | 92 |
|
40 | 93 |
|
41 | 94 | @dataclass(slots=True) |
42 | 95 | class WaitForCommand(BrowserCommand): |
| 96 | + """Browser command that waits until a specific element is present on the page. |
| 97 | +
|
| 98 | + Attributes: |
| 99 | + target_selector: The CSS or XPath selector used to find the target element. |
| 100 | + command_name: The wire command name. Always ``"wait_for"``. |
| 101 | + """ |
| 102 | + |
43 | 103 | target_selector: str = "" |
44 | 104 | command_name: str = "wait_for" |
45 | 105 |
|
46 | 106 | def to_wire(self) -> dict[str, Any]: |
| 107 | + """Serialize the wait-for command to its API wire representation. |
| 108 | +
|
| 109 | + Returns: |
| 110 | + A dictionary mapping ``"wait_for"`` to the target selector. |
| 111 | + """ |
47 | 112 | return {self.command_name: self.target_selector} |
48 | 113 |
|
49 | 114 |
|
50 | 115 | @dataclass(slots=True) |
51 | 116 | class InputCommand(BrowserCommand): |
| 117 | + """Browser command that types text into an input element. |
| 118 | +
|
| 119 | + Attributes: |
| 120 | + target_selector: The CSS or XPath selector used to find the input element. |
| 121 | + input_value: The text value to enter into the target element. |
| 122 | + command_name: The wire command name. Always ``"input"``. |
| 123 | + """ |
| 124 | + |
52 | 125 | target_selector: str = "" |
53 | 126 | input_value: str = "" |
54 | 127 | command_name: str = "input" |
55 | 128 |
|
56 | 129 | def to_wire(self) -> dict[str, Any]: |
| 130 | + """Serialize the input command to its API wire representation. |
| 131 | +
|
| 132 | + Returns: |
| 133 | + A dictionary mapping ``"input"`` to a selector/value pair. |
| 134 | + """ |
57 | 135 | return {self.command_name: {self.target_selector: self.input_value}} |
58 | 136 |
|
59 | 137 |
|
60 | 138 | @dataclass(slots=True) |
61 | 139 | class SelectCommand(BrowserCommand): |
| 140 | + """Browser command that selects an option from a ``<select>`` element. |
| 141 | +
|
| 142 | + Attributes: |
| 143 | + target_selector: The CSS or XPath selector used to find the ``<select>`` element. |
| 144 | + select_value: The option value to select on the target element. |
| 145 | + command_name: The wire command name. Always ``"select"``. |
| 146 | + """ |
| 147 | + |
62 | 148 | target_selector: str = "" |
63 | 149 | select_value: str = "" |
64 | 150 | command_name: str = "select" |
65 | 151 |
|
66 | 152 | def to_wire(self) -> dict[str, Any]: |
| 153 | + """Serialize the select command to its API wire representation. |
| 154 | +
|
| 155 | + Returns: |
| 156 | + A dictionary mapping ``"select"`` to a selector/value pair. |
| 157 | + """ |
67 | 158 | return {self.command_name: {self.target_selector: self.select_value}} |
68 | 159 |
|
69 | 160 |
|
70 | 161 | @dataclass(slots=True) |
71 | 162 | class JavaScriptCommand(BrowserCommand): |
| 163 | + """Browser command that evaluates a JavaScript snippet on the page. |
| 164 | +
|
| 165 | + Attributes: |
| 166 | + script: The JavaScript snippet to execute. Subject to a 5-second execution limit. |
| 167 | + command_name: The wire command name. Always ``"javascript"``. |
| 168 | + """ |
| 169 | + |
72 | 170 | script: str = "" |
73 | 171 | command_name: str = "javascript" |
74 | 172 |
|
75 | 173 | def to_wire(self) -> dict[str, Any]: |
| 174 | + """Serialize the JavaScript command to its API wire representation. |
| 175 | +
|
| 176 | + Returns: |
| 177 | + A dictionary mapping ``"javascript"`` to the script string. |
| 178 | + """ |
76 | 179 | return {self.command_name: self.script} |
77 | 180 |
|
78 | 181 |
|
79 | 182 | BrowserCommandType = Union[ClickCommand, ScrollCommand, WaitCommand, WaitForCommand, InputCommand, SelectCommand, JavaScriptCommand] |
80 | 183 |
|
81 | 184 |
|
82 | 185 | class BrowserCommandList(list[BrowserCommandType]): |
| 186 | + """An ordered list of browser commands to execute after a page has loaded. |
| 187 | +
|
| 188 | + Provides a fluent interface for building sequences of browser interactions. |
| 189 | + All methods return ``self`` to allow chaining. |
| 190 | +
|
| 191 | + Example:: |
| 192 | +
|
| 193 | + request.browser_commands \\ |
| 194 | + .click("#accept-cookies") \\ |
| 195 | + .wait(500) \\ |
| 196 | + .input("#search", "example query") \\ |
| 197 | + .click("#submit") |
| 198 | + """ |
| 199 | + |
83 | 200 | def click(self, target_selector: str) -> BrowserCommandList: |
| 201 | + """Append a click command targeting the specified element. |
| 202 | +
|
| 203 | + Args: |
| 204 | + target_selector: The CSS or XPath selector of the element to click. |
| 205 | +
|
| 206 | + Returns: |
| 207 | + This :class:`BrowserCommandList` instance for chaining. |
| 208 | + """ |
84 | 209 | self.append(ClickCommand(target_selector=target_selector)) |
85 | 210 | return self |
86 | 211 |
|
87 | 212 | def scroll(self, pixels: int = 1000) -> BrowserCommandList: |
| 213 | + """Append a scroll command to scroll the page by the given number of pixels. |
| 214 | +
|
| 215 | + Args: |
| 216 | + pixels: The number of pixels to scroll. Use a negative value to scroll |
| 217 | + up. Defaults to 1000. |
| 218 | +
|
| 219 | + Returns: |
| 220 | + This :class:`BrowserCommandList` instance for chaining. |
| 221 | + """ |
88 | 222 | self.append(ScrollCommand(pixels=pixels)) |
89 | 223 | return self |
90 | 224 |
|
91 | 225 | def wait(self, milliseconds: int) -> BrowserCommandList: |
| 226 | + """Append a wait command to pause execution for the given duration. |
| 227 | +
|
| 228 | + Args: |
| 229 | + milliseconds: The number of milliseconds to wait. Maximum is 15000. |
| 230 | +
|
| 231 | + Returns: |
| 232 | + This :class:`BrowserCommandList` instance for chaining. |
| 233 | +
|
| 234 | + Raises: |
| 235 | + ValueError: If ``milliseconds`` exceeds 15000. |
| 236 | + """ |
92 | 237 | if milliseconds > 15000: |
93 | 238 | raise ValueError("The maximum wait time is 15 seconds.") |
94 | 239 |
|
95 | 240 | self.append(WaitCommand(milliseconds=milliseconds)) |
96 | 241 | return self |
97 | 242 |
|
98 | 243 | def wait_for(self, target_selector: str) -> BrowserCommandList: |
| 244 | + """Append a command that waits until the specified element is present. |
| 245 | +
|
| 246 | + Args: |
| 247 | + target_selector: The CSS or XPath selector of the element to wait for. |
| 248 | +
|
| 249 | + Returns: |
| 250 | + This :class:`BrowserCommandList` instance for chaining. |
| 251 | + """ |
99 | 252 | self.append(WaitForCommand(target_selector=target_selector)) |
100 | 253 | return self |
101 | 254 |
|
102 | 255 | def input(self, target_selector: str, input_value: str) -> BrowserCommandList: |
| 256 | + """Append a command that types text into the specified input element. |
| 257 | +
|
| 258 | + Args: |
| 259 | + target_selector: The CSS or XPath selector of the input element. |
| 260 | + input_value: The text value to enter into the element. |
| 261 | +
|
| 262 | + Returns: |
| 263 | + This :class:`BrowserCommandList` instance for chaining. |
| 264 | + """ |
103 | 265 | self.append(InputCommand(target_selector=target_selector, input_value=input_value)) |
104 | 266 | return self |
105 | 267 |
|
106 | 268 | def select(self, target_selector: str, select_value: str) -> BrowserCommandList: |
| 269 | + """Append a command that selects an option from a ``<select>`` element. |
| 270 | +
|
| 271 | + Args: |
| 272 | + target_selector: The CSS or XPath selector of the ``<select>`` element. |
| 273 | + select_value: The option value to select. |
| 274 | +
|
| 275 | + Returns: |
| 276 | + This :class:`BrowserCommandList` instance for chaining. |
| 277 | + """ |
107 | 278 | self.append(SelectCommand(target_selector=target_selector, select_value=select_value)) |
108 | 279 | return self |
109 | 280 |
|
110 | 281 | def evaluate(self, javascript: str) -> BrowserCommandList: |
| 282 | + """Append a command that evaluates a JavaScript snippet on the page. |
| 283 | +
|
| 284 | + Args: |
| 285 | + javascript: The JavaScript snippet to execute. Subject to a 5-second |
| 286 | + execution limit. |
| 287 | +
|
| 288 | + Returns: |
| 289 | + This :class:`BrowserCommandList` instance for chaining. |
| 290 | + """ |
111 | 291 | self.append(JavaScriptCommand(script=javascript)) |
112 | 292 | return self |
113 | 293 |
|
114 | 294 | def to_wire(self) -> list[dict[str, Any]]: |
| 295 | + """Serialize all commands in the list to their API wire representation. |
| 296 | +
|
| 297 | + Returns: |
| 298 | + A list of command dictionaries suitable for inclusion in the API request. |
| 299 | + """ |
115 | 300 | return [command.to_wire() for command in self] |
116 | 301 |
|
117 | 302 |
|
|
0 commit comments