docs: improving the code copy-paste experience and use less tokens for the agent skill

This commit is contained in:
Karim shoair
2026-04-22 16:52:59 +02:00
parent b626b4d585
commit 9af644ef84
17 changed files with 345 additions and 363 deletions
@@ -27,23 +27,23 @@ The following table compares them and can be quickly used for guidance.
## Parser configuration in all fetchers ## Parser configuration in all fetchers
All fetchers share the same import method, as you will see in the upcoming pages All fetchers share the same import method, as you will see in the upcoming pages
```python ```python
>>> from scrapling.fetchers import Fetcher, AsyncFetcher, StealthyFetcher, DynamicFetcher from scrapling.fetchers import Fetcher, AsyncFetcher, StealthyFetcher, DynamicFetcher
``` ```
Then you use it right away without initializing like this, and it will use the default parser settings: Then you use it right away without initializing like this, and it will use the default parser settings:
```python ```python
>>> page = StealthyFetcher.fetch('https://example.com') page = StealthyFetcher.fetch('https://example.com')
``` ```
If you want to configure the parser ([Selector class](parsing/main_classes.md#selector)) that will be used on the response before returning it for you, then do this first: If you want to configure the parser ([Selector class](parsing/main_classes.md#selector)) that will be used on the response before returning it for you, then do this first:
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> Fetcher.configure(adaptive=True, keep_comments=False, keep_cdata=False) # and the rest Fetcher.configure(adaptive=True, keep_comments=False, keep_cdata=False) # and the rest
``` ```
or or
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> Fetcher.adaptive=True Fetcher.adaptive=True
>>> Fetcher.keep_comments=False Fetcher.keep_comments=False
>>> Fetcher.keep_cdata=False # and the rest Fetcher.keep_cdata=False # and the rest
``` ```
Then, continue your code as usual. Then, continue your code as usual.
@@ -59,19 +59,19 @@ If your use case requires a different configuration for each request/fetch, you
## Response Object ## Response Object
The `Response` object is the same as the [Selector](parsing/main_classes.md#selector) class, but it has additional details about the response, like response headers, status, cookies, etc., as shown below: The `Response` object is the same as the [Selector](parsing/main_classes.md#selector) class, but it has additional details about the response, like response headers, status, cookies, etc., as shown below:
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> page = Fetcher.get('https://example.com') page = Fetcher.get('https://example.com')
>>> page.status # HTTP status code page.status # HTTP status code
>>> page.reason # Status message page.reason # Status message
>>> page.cookies # Response cookies as a dictionary page.cookies # Response cookies as a dictionary
>>> page.headers # Response headers page.headers # Response headers
>>> page.request_headers # Request headers page.request_headers # Request headers
>>> page.history # Response history of redirections, if any page.history # Response history of redirections, if any
>>> page.body # Raw response body as bytes page.body # Raw response body as bytes
>>> page.encoding # Response encoding page.encoding # Response encoding
>>> page.meta # Response metadata dictionary (e.g., proxy used). Mainly helpful with the spiders system. page.meta # Response metadata dictionary (e.g., proxy used). Mainly helpful with the spiders system.
>>> page.captured_xhr # List of captured XHR/fetch responses (when capture_xhr is enabled on a browser session) page.captured_xhr # List of captured XHR/fetch responses (when capture_xhr is enabled on a browser session)
``` ```
All fetchers return the `Response` object. All fetchers return the `Response` object.
@@ -8,7 +8,7 @@ As we will explain later, to automate the page, you need some knowledge of [Play
You have one primary way to import this Fetcher, which is the same for all fetchers. You have one primary way to import this Fetcher, which is the same for all fetchers.
```python ```python
>>> from scrapling.fetchers import DynamicFetcher from scrapling.fetchers import DynamicFetcher
``` ```
Check out how to configure the parsing options [here](choosing.md#parser-configuration-in-all-fetchers) Check out how to configure the parsing options [here](choosing.md#parser-configuration-in-all-fetchers)
@@ -6,7 +6,7 @@ The `Fetcher` class provides rapid and lightweight HTTP requests using the high-
Import the Fetcher (same import pattern for all fetchers): Import the Fetcher (same import pattern for all fetchers):
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
``` ```
Check out how to configure the parsing options [here](choosing.md#parser-configuration-in-all-fetchers) Check out how to configure the parsing options [here](choosing.md#parser-configuration-in-all-fetchers)
@@ -47,41 +47,41 @@ Examples are the best way to explain this:
> Hence: `OPTIONS` and `HEAD` methods are not supported. > Hence: `OPTIONS` and `HEAD` methods are not supported.
#### GET #### GET
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> # Basic GET # Basic GET
>>> page = Fetcher.get('https://example.com') page = Fetcher.get('https://example.com')
>>> page = Fetcher.get('https://scrapling.requestcatcher.com/get', stealthy_headers=True) page = Fetcher.get('https://scrapling.requestcatcher.com/get', stealthy_headers=True)
>>> page = Fetcher.get('https://scrapling.requestcatcher.com/get', proxy='http://username:password@localhost:8030') page = Fetcher.get('https://scrapling.requestcatcher.com/get', proxy='http://username:password@localhost:8030')
>>> # With parameters # With parameters
>>> page = Fetcher.get('https://example.com/search', params={'q': 'query'}) page = Fetcher.get('https://example.com/search', params={'q': 'query'})
>>>
>>> # With headers # With headers
>>> page = Fetcher.get('https://example.com', headers={'User-Agent': 'Custom/1.0'}) page = Fetcher.get('https://example.com', headers={'User-Agent': 'Custom/1.0'})
>>> # Basic HTTP authentication # Basic HTTP authentication
>>> page = Fetcher.get("https://example.com", auth=("my_user", "password123")) page = Fetcher.get("https://example.com", auth=("my_user", "password123"))
>>> # Browser impersonation # Browser impersonation
>>> page = Fetcher.get('https://example.com', impersonate='chrome') page = Fetcher.get('https://example.com', impersonate='chrome')
>>> # HTTP/3 support # HTTP/3 support
>>> page = Fetcher.get('https://example.com', http3=True) page = Fetcher.get('https://example.com', http3=True)
``` ```
And for asynchronous requests, it's a small adjustment And for asynchronous requests, it's a small adjustment
```python ```python
>>> from scrapling.fetchers import AsyncFetcher from scrapling.fetchers import AsyncFetcher
>>> # Basic GET # Basic GET
>>> page = await AsyncFetcher.get('https://example.com') page = await AsyncFetcher.get('https://example.com')
>>> page = await AsyncFetcher.get('https://scrapling.requestcatcher.com/get', stealthy_headers=True) page = await AsyncFetcher.get('https://scrapling.requestcatcher.com/get', stealthy_headers=True)
>>> page = await AsyncFetcher.get('https://scrapling.requestcatcher.com/get', proxy='http://username:password@localhost:8030') page = await AsyncFetcher.get('https://scrapling.requestcatcher.com/get', proxy='http://username:password@localhost:8030')
>>> # With parameters # With parameters
>>> page = await AsyncFetcher.get('https://example.com/search', params={'q': 'query'}) page = await AsyncFetcher.get('https://example.com/search', params={'q': 'query'})
>>>
>>> # With headers # With headers
>>> page = await AsyncFetcher.get('https://example.com', headers={'User-Agent': 'Custom/1.0'}) page = await AsyncFetcher.get('https://example.com', headers={'User-Agent': 'Custom/1.0'})
>>> # Basic HTTP authentication # Basic HTTP authentication
>>> page = await AsyncFetcher.get("https://example.com", auth=("my_user", "password123")) page = await AsyncFetcher.get("https://example.com", auth=("my_user", "password123"))
>>> # Browser impersonation # Browser impersonation
>>> page = await AsyncFetcher.get('https://example.com', impersonate='chrome110') page = await AsyncFetcher.get('https://example.com', impersonate='chrome110')
>>> # HTTP/3 support # HTTP/3 support
>>> page = await AsyncFetcher.get('https://example.com', http3=True) page = await AsyncFetcher.get('https://example.com', http3=True)
``` ```
The `page` object in all cases is a [Response](choosing.md#response-object) object, which is a [Selector](parsing/main_classes.md#selector), so you can use it directly The `page` object in all cases is a [Response](choosing.md#response-object) object, which is a [Selector](parsing/main_classes.md#selector), so you can use it directly
```python ```python
@@ -102,62 +102,62 @@ The `page` object in all cases is a [Response](choosing.md#response-object) obje
``` ```
#### POST #### POST
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> # Basic POST # Basic POST
>>> page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, params={'q': 'query'}) page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, params={'q': 'query'})
>>> page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, stealthy_headers=True) page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, stealthy_headers=True)
>>> page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, proxy='http://username:password@localhost:8030', impersonate="chrome") page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, proxy='http://username:password@localhost:8030', impersonate="chrome")
>>> # Another example of form-encoded data # Another example of form-encoded data
>>> page = Fetcher.post('https://example.com/submit', data={'username': 'user', 'password': 'pass'}, http3=True) page = Fetcher.post('https://example.com/submit', data={'username': 'user', 'password': 'pass'}, http3=True)
>>> # JSON data # JSON data
>>> page = Fetcher.post('https://example.com/api', json={'key': 'value'}) page = Fetcher.post('https://example.com/api', json={'key': 'value'})
``` ```
And for asynchronous requests, it's a small adjustment And for asynchronous requests, it's a small adjustment
```python ```python
>>> from scrapling.fetchers import AsyncFetcher from scrapling.fetchers import AsyncFetcher
>>> # Basic POST # Basic POST
>>> page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}) page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'})
>>> page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, stealthy_headers=True) page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, stealthy_headers=True)
>>> page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, proxy='http://username:password@localhost:8030', impersonate="chrome") page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, proxy='http://username:password@localhost:8030', impersonate="chrome")
>>> # Another example of form-encoded data # Another example of form-encoded data
>>> page = await AsyncFetcher.post('https://example.com/submit', data={'username': 'user', 'password': 'pass'}, http3=True) page = await AsyncFetcher.post('https://example.com/submit', data={'username': 'user', 'password': 'pass'}, http3=True)
>>> # JSON data # JSON data
>>> page = await AsyncFetcher.post('https://example.com/api', json={'key': 'value'}) page = await AsyncFetcher.post('https://example.com/api', json={'key': 'value'})
``` ```
#### PUT #### PUT
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> # Basic PUT # Basic PUT
>>> page = Fetcher.put('https://example.com/update', data={'status': 'updated'}) page = Fetcher.put('https://example.com/update', data={'status': 'updated'})
>>> page = Fetcher.put('https://example.com/update', data={'status': 'updated'}, stealthy_headers=True, impersonate="chrome") page = Fetcher.put('https://example.com/update', data={'status': 'updated'}, stealthy_headers=True, impersonate="chrome")
>>> page = Fetcher.put('https://example.com/update', data={'status': 'updated'}, proxy='http://username:password@localhost:8030') page = Fetcher.put('https://example.com/update', data={'status': 'updated'}, proxy='http://username:password@localhost:8030')
>>> # Another example of form-encoded data # Another example of form-encoded data
>>> page = Fetcher.put("https://scrapling.requestcatcher.com/put", data={'key': ['value1', 'value2']}) page = Fetcher.put("https://scrapling.requestcatcher.com/put", data={'key': ['value1', 'value2']})
``` ```
And for asynchronous requests, it's a small adjustment And for asynchronous requests, it's a small adjustment
```python ```python
>>> from scrapling.fetchers import AsyncFetcher from scrapling.fetchers import AsyncFetcher
>>> # Basic PUT # Basic PUT
>>> page = await AsyncFetcher.put('https://example.com/update', data={'status': 'updated'}) page = await AsyncFetcher.put('https://example.com/update', data={'status': 'updated'})
>>> page = await AsyncFetcher.put('https://example.com/update', data={'status': 'updated'}, stealthy_headers=True, impersonate="chrome") page = await AsyncFetcher.put('https://example.com/update', data={'status': 'updated'}, stealthy_headers=True, impersonate="chrome")
>>> page = await AsyncFetcher.put('https://example.com/update', data={'status': 'updated'}, proxy='http://username:password@localhost:8030') page = await AsyncFetcher.put('https://example.com/update', data={'status': 'updated'}, proxy='http://username:password@localhost:8030')
>>> # Another example of form-encoded data # Another example of form-encoded data
>>> page = await AsyncFetcher.put("https://scrapling.requestcatcher.com/put", data={'key': ['value1', 'value2']}) page = await AsyncFetcher.put("https://scrapling.requestcatcher.com/put", data={'key': ['value1', 'value2']})
``` ```
#### DELETE #### DELETE
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> page = Fetcher.delete('https://example.com/resource/123') page = Fetcher.delete('https://example.com/resource/123')
>>> page = Fetcher.delete('https://example.com/resource/123', stealthy_headers=True, impersonate="chrome") page = Fetcher.delete('https://example.com/resource/123', stealthy_headers=True, impersonate="chrome")
>>> page = Fetcher.delete('https://example.com/resource/123', proxy='http://username:password@localhost:8030') page = Fetcher.delete('https://example.com/resource/123', proxy='http://username:password@localhost:8030')
``` ```
And for asynchronous requests, it's a small adjustment And for asynchronous requests, it's a small adjustment
```python ```python
>>> from scrapling.fetchers import AsyncFetcher from scrapling.fetchers import AsyncFetcher
>>> page = await AsyncFetcher.delete('https://example.com/resource/123') page = await AsyncFetcher.delete('https://example.com/resource/123')
>>> page = await AsyncFetcher.delete('https://example.com/resource/123', stealthy_headers=True, impersonate="chrome") page = await AsyncFetcher.delete('https://example.com/resource/123', stealthy_headers=True, impersonate="chrome")
>>> page = await AsyncFetcher.delete('https://example.com/resource/123', proxy='http://username:password@localhost:8030') page = await AsyncFetcher.delete('https://example.com/resource/123', proxy='http://username:password@localhost:8030')
``` ```
## Session Management ## Session Management
@@ -6,7 +6,7 @@
You have one primary way to import this Fetcher, which is the same for all fetchers. You have one primary way to import this Fetcher, which is the same for all fetchers.
```python ```python
>>> from scrapling.fetchers import StealthyFetcher from scrapling.fetchers import StealthyFetcher
``` ```
Check out how to configure the parsing options [here](choosing.md#parser-configuration-in-all-fetchers) Check out how to configure the parsing options [here](choosing.md#parser-configuration-in-all-fetchers)
@@ -68,22 +68,21 @@ To extract the Questions button from the old design, a selector like `#hmenus >
Testing the same selector in both versions: Testing the same selector in both versions:
```python ```python
>> from scrapling import Fetcher from scrapling import Fetcher
>> selector = '#hmenus > div:nth-child(1) > ul > li:nth-child(1) > a' selector = '#hmenus > div:nth-child(1) > ul > li:nth-child(1) > a'
>> old_url = "https://web.archive.org/web/20100102003420/http://stackoverflow.com/" old_url = "https://web.archive.org/web/20100102003420/http://stackoverflow.com/"
>> new_url = "https://stackoverflow.com/" new_url = "https://stackoverflow.com/"
>> Fetcher.configure(adaptive = True, adaptive_domain='stackoverflow.com') Fetcher.configure(adaptive = True, adaptive_domain='stackoverflow.com')
>>
>> page = Fetcher.get(old_url, timeout=30) page = Fetcher.get(old_url, timeout=30)
>> element1 = page.css(selector, auto_save=True)[0] element1 = page.css(selector, auto_save=True)[0]
>>
>> # Same selector but used in the updated website # Same selector but used in the updated website
>> page = Fetcher.get(new_url) page = Fetcher.get(new_url)
>> element2 = page.css(selector, adaptive=True)[0] element2 = page.css(selector, adaptive=True)[0]
>>
>> if element1.text == element2.text: if element1.text == element2.text:
... print('Scrapling found the same element in the old and new designs!') ... print('Scrapling found the same element in the old and new designs!')
'Scrapling found the same element in the old and new designs!'
``` ```
The `adaptive_domain` argument is used here because Scrapling sees `archive.org` and `stackoverflow.com` as two different domains and would isolate their `adaptive` data. Passing `adaptive_domain` tells Scrapling to treat them as the same website for adaptive data storage. The `adaptive_domain` argument is used here because Scrapling sees `archive.org` and `stackoverflow.com` as two different domains and would isolate their `adaptive` data. Passing `adaptive_domain` tells Scrapling to treat them as the same website for adaptive data storage.
@@ -127,11 +126,11 @@ First, enable the `adaptive` feature by passing `adaptive=True` to the [Selector
Examples: Examples:
```python ```python
>>> from scrapling import Selector, Fetcher from scrapling import Selector, Fetcher
>>> page = Selector(html_doc, adaptive=True) page = Selector(html_doc, adaptive=True)
# OR # OR
>>> Fetcher.adaptive = True Fetcher.adaptive = True
>>> page = Fetcher.get('https://example.com') page = Fetcher.get('https://example.com')
``` ```
When using the [Selector](main_classes.md#selector) class, pass the URL of the website with the `url` argument so Scrapling can separate the properties saved for each element by domain. When using the [Selector](main_classes.md#selector) class, pass the URL of the website with the `url` argument so Scrapling can separate the properties saved for each element by domain.
@@ -159,11 +158,11 @@ Elements can be manually saved, retrieved, and relocated within the `adaptive` f
Example of getting an element by text: Example of getting an element by text:
```python ```python
>>> element = page.find_by_text('Tipping the Velvet', first_match=True) element = page.find_by_text('Tipping the Velvet', first_match=True)
``` ```
Save its unique properties using the `save` method. The identifier must be set manually (use a meaningful identifier): Save its unique properties using the `save` method. The identifier must be set manually (use a meaningful identifier):
```python ```python
>>> page.save(element, 'my_special_element') page.save(element, 'my_special_element')
``` ```
Later, retrieve and relocate the element inside the page with `adaptive`: Later, retrieve and relocate the element inside the page with `adaptive`:
```python ```python
@@ -131,14 +131,14 @@ Getting the attributes of the element
``` ```
Access a specific attribute with any of the following Access a specific attribute with any of the following
```python ```python
>>> article.attrib['class'] article.attrib['class']
>>> article.attrib.get('class') article.attrib.get('class')
>>> article['class'] # new in v0.3 article['class'] # new in v0.3
``` ```
Check if the attributes contain a specific attribute with any of the methods below Check if the attributes contain a specific attribute with any of the methods below
```python ```python
>>> 'class' in article.attrib 'class' in article.attrib
>>> 'class' in article # new in v0.3 'class' in article # new in v0.3
``` ```
Get the HTML content of the element Get the HTML content of the element
```python ```python
@@ -279,13 +279,13 @@ In the [Selector](#selector) class, all methods/properties that should return a
Starting with v0.4, all selection methods consistently return [Selector](#selector)/[Selectors](#selectors) objects, even for text nodes and attribute values. Text nodes (selected via `::text`, `/text()`, `::attr()`, `/@attr`) are wrapped in [Selector](#selector) objects. These text node selectors have `tag` set to `"#text"`, and their `text` property returns the text value. You can still access the text value directly, and all other properties return empty/default values gracefully. Starting with v0.4, all selection methods consistently return [Selector](#selector)/[Selectors](#selectors) objects, even for text nodes and attribute values. Text nodes (selected via `::text`, `/text()`, `::attr()`, `/@attr`) are wrapped in [Selector](#selector) objects. These text node selectors have `tag` set to `"#text"`, and their `text` property returns the text value. You can still access the text value directly, and all other properties return empty/default values gracefully.
```python ```python
>>> page.css('a::text') # -> Selectors (of text node Selectors) page.css('a::text') # -> Selectors (of text node Selectors)
>>> page.xpath('//a/text()') # -> Selectors page.xpath('//a/text()') # -> Selectors
>>> page.css('a::text').get() # -> TextHandler (the first text value) page.css('a::text').get() # -> TextHandler (the first text value)
>>> page.css('a::text').getall() # -> TextHandlers (all text values) page.css('a::text').getall() # -> TextHandlers (all text values)
>>> page.css('a::attr(href)') # -> Selectors page.css('a::attr(href)') # -> Selectors
>>> page.xpath('//a/@href') # -> Selectors page.xpath('//a/@href') # -> Selectors
>>> page.css('.price_color') # -> Selectors page.css('.price_color') # -> Selectors
``` ```
### Data extraction methods ### Data extraction methods
@@ -346,8 +346,8 @@ It filters all elements in the current page/element in the following order:
### Examples ### Examples
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> page = Fetcher.get('https://quotes.toscrape.com/') page = Fetcher.get('https://quotes.toscrape.com/')
``` ```
Find all elements with the tag name `div`. Find all elements with the tag name `div`.
```python ```python
+54 -65
View File
@@ -43,25 +43,24 @@ Once launched, you'll see the Scrapling banner and can immediately start scrapin
```python ```python
# No imports needed - everything is ready! # No imports needed - everything is ready!
>>> get('https://news.ycombinator.com') get('https://news.ycombinator.com')
>>> # Explore the page structure # Explore the page structure
>>> page.css('a')[:5] # Look at first 5 links page.css('a')[:5] # Look at first 5 links
>>> # Refine your selectors # Refine your selectors
>>> stories = page.css('.titleline>a') stories = page.css('.titleline>a')
>>> len(stories) len(stories) # 30
30
>>> # Extract specific data # Extract specific data
>>> for story in stories[:3]: for story in stories[:3]:
... title = story.text ... title = story.text
... url = story['href'] ... url = story['href']
... print(f"{title}: {url}") ... print(f"{title}: {url}")
>>> # Try different approaches # Try different approaches
>>> titles = page.css('.titleline>a::text') # Direct text extraction titles = page.css('.titleline>a::text') # Direct text extraction
>>> urls = page.css('.titleline>a::attr(href)') # Direct attribute extraction urls = page.css('.titleline>a::attr(href)') # Direct attribute extraction
``` ```
## Built-in Shortcuts ## Built-in Shortcuts
@@ -86,12 +85,10 @@ The shell automatically tracks your requests and pages:
The `page` and `response` commands are automatically updated with the last fetched page: The `page` and `response` commands are automatically updated with the last fetched page:
```python ```python
>>> get('https://quotes.toscrape.com') get('https://quotes.toscrape.com')
>>> # 'page' and 'response' both refer to the last fetched page # 'page' and 'response' both refer to the last fetched page
>>> page.url page.url # 'https://quotes.toscrape.com'
'https://quotes.toscrape.com' response.status # Prints 200; Same as page.status
>>> response.status # Same as page.status
200
``` ```
- **Page History** - **Page History**
@@ -99,20 +96,17 @@ The shell automatically tracks your requests and pages:
The `pages` command keeps track of the last five pages (it's a `Selectors` object): The `pages` command keeps track of the last five pages (it's a `Selectors` object):
```python ```python
>>> get('https://site1.com') get('https://site1.com')
>>> get('https://site2.com') get('https://site2.com')
>>> get('https://site3.com') get('https://site3.com')
>>> # Access last 5 pages # Access last 5 pages
>>> len(pages) # `Selectors` object with `page` history len(pages) # `Selectors` object with `page` history -> 3
3 pages[0].url # First page in history -> 'https://site1.com'
>>> pages[0].url # First page in history pages[-1].url # Most recent page -> 'https://site3.com'
'https://site1.com'
>>> pages[-1].url # Most recent page
'https://site3.com'
>>> # Work with historical pages # Work with historical pages
>>> for i, old_page in enumerate(pages): for i, old_page in enumerate(pages):
... print(f"Page {i}: {old_page.url} - {old_page.status}") ... print(f"Page {i}: {old_page.url} - {old_page.status}")
``` ```
@@ -123,8 +117,8 @@ The shell automatically tracks your requests and pages:
View scraped pages in your browser: View scraped pages in your browser:
```python ```python
>>> get('https://quotes.toscrape.com') get('https://quotes.toscrape.com')
>>> view(page) # Opens the page HTML in your default browser view(page) # Opens the page HTML in your default browser
``` ```
### Curl Command Integration ### Curl Command Integration
@@ -138,29 +132,24 @@ First, you need to copy a request as a curl command like the following:
- **Convert Curl command to Request Object** - **Convert Curl command to Request Object**
```python ```python
>>> curl_cmd = '''curl 'https://scrapling.requestcatcher.com/post' \ curl_cmd = '''curl 'https://scrapling.requestcatcher.com/post' \
... -X POST \ ... -X POST \
... -H 'Content-Type: application/json' \ ... -H 'Content-Type: application/json' \
... -d '{"name": "test", "value": 123}' ''' ... -d '{"name": "test", "value": 123}' '''
>>> request = uncurl(curl_cmd) request = uncurl(curl_cmd)
>>> request.method request.method # -> 'post'
'post' request.url # -> 'https://scrapling.requestcatcher.com/post'
>>> request.url request.headers # -> {'Content-Type': 'application/json'}
'https://scrapling.requestcatcher.com/post'
>>> request.headers
{'Content-Type': 'application/json'}
``` ```
- **Execute Curl Command Directly** - **Execute Curl Command Directly**
```python ```python
>>> # Convert and execute in one step # Convert and execute in one step
>>> curl2fetcher(curl_cmd) curl2fetcher(curl_cmd)
>>> page.status page.status # -> 200
200 page.json()['json'] # -> {'name': 'test', 'value': 123}
>>> page.json()['json']
{'name': 'test', 'value': 123}
``` ```
### IPython Features ### IPython Features
@@ -168,17 +157,17 @@ First, you need to copy a request as a curl command like the following:
The shell inherits all IPython capabilities: The shell inherits all IPython capabilities:
```python ```python
>>> # Magic commands # Magic commands
>>> %time page = get('https://example.com') # Time execution %time page = get('https://example.com') # Time execution
>>> %history # Show command history %history # Show command history
>>> %save filename.py 1-10 # Save commands 1-10 to file %save filename.py 1-10 # Save commands 1-10 to file
>>> # Tab completion works everywhere # Tab completion works everywhere
>>> page.c<TAB> # Shows: css, cookies, headers, etc. page.c<TAB> # Shows: css, cookies, headers, etc.
>>> Fetcher.<TAB> # Shows all Fetcher methods Fetcher.<TAB> # Shows all Fetcher methods
>>> # Object inspection # Object inspection
>>> get? # Show get documentation get? # Show get documentation
``` ```
## Examples ## Examples
@@ -188,23 +177,23 @@ Here are a few examples generated via AI:
#### E-commerce Data Collection #### E-commerce Data Collection
```python ```python
>>> # Start with product listing page # Start with product listing page
>>> catalog = get('https://shop.example.com/products') catalog = get('https://shop.example.com/products')
>>> # Find product links # Find product links
>>> product_links = catalog.css('.product-link::attr(href)') product_links = catalog.css('.product-link::attr(href)')
>>> print(f"Found {len(product_links)} products") print(f"Found {len(product_links)} products")
>>> # Sample a few products first # Sample a few products first
>>> for link in product_links[:3]: for link in product_links[:3]:
... product = get(f"https://shop.example.com{link}") ... product = get(f"https://shop.example.com{link}")
... name = product.css('.product-name::text').get('') ... name = product.css('.product-name::text').get('')
... price = product.css('.price::text').get('') ... price = product.css('.price::text').get('')
... print(f"{name}: {price}") ... print(f"{name}: {price}")
>>> # Scale up with sessions for efficiency # Scale up with sessions for efficiency
>>> from scrapling.fetchers import FetcherSession from scrapling.fetchers import FetcherSession
>>> with FetcherSession() as session: with FetcherSession() as session:
... products = [] ... products = []
... for link in product_links: ... for link in product_links:
... product = session.get(f"https://shop.example.com{link}") ... product = session.get(f"https://shop.example.com{link}")
+5 -6
View File
@@ -4,13 +4,12 @@
### All current types can be imported alone, like below ### All current types can be imported alone, like below
```python ```python
>>> from scrapling.core.custom_types import TextHandler, AttributesHandler from scrapling.core.custom_types import TextHandler, AttributesHandler
>>> somestring = TextHandler('{}') somestring = TextHandler('{}')
>>> somestring.json() somestring.json() # '{}'
'{}' somedict_1 = AttributesHandler({'a': 1})
>>> somedict_1 = AttributesHandler({'a': 1}) somedict_2 = AttributesHandler(a=1)
>>> somedict_2 = AttributesHandler(a=1)
``` ```
Note that `TextHandler` is a subclass of Python's `str`, so all standard operations/methods that work with Python strings will work. Note that `TextHandler` is a subclass of Python's `str`, so all standard operations/methods that work with Python strings will work.
+20 -20
View File
@@ -31,23 +31,23 @@ In the following pages, we will talk about each one in detail.
## Parser configuration in all fetchers ## Parser configuration in all fetchers
All fetchers share the same import method, as you will see in the upcoming pages All fetchers share the same import method, as you will see in the upcoming pages
```python ```python
>>> from scrapling.fetchers import Fetcher, AsyncFetcher, StealthyFetcher, DynamicFetcher from scrapling.fetchers import Fetcher, AsyncFetcher, StealthyFetcher, DynamicFetcher
``` ```
Then you use it right away without initializing like this, and it will use the default parser settings: Then you use it right away without initializing like this, and it will use the default parser settings:
```python ```python
>>> page = StealthyFetcher.fetch('https://example.com') page = StealthyFetcher.fetch('https://example.com')
``` ```
If you want to configure the parser ([Selector class](../parsing/main_classes.md#selector)) that will be used on the response before returning it for you, then do this first: If you want to configure the parser ([Selector class](../parsing/main_classes.md#selector)) that will be used on the response before returning it for you, then do this first:
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> Fetcher.configure(adaptive=True, keep_comments=False, keep_cdata=False) # and the rest Fetcher.configure(adaptive=True, keep_comments=False, keep_cdata=False) # and the rest
``` ```
or or
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> Fetcher.adaptive=True Fetcher.adaptive=True
>>> Fetcher.keep_comments=False Fetcher.keep_comments=False
>>> Fetcher.keep_cdata=False # and the rest Fetcher.keep_cdata=False # and the rest
``` ```
Then, continue your code as usual. Then, continue your code as usual.
@@ -65,19 +65,19 @@ If your use case requires a different configuration for each request/fetch, you
## Response Object ## Response Object
The `Response` object is the same as the [Selector](../parsing/main_classes.md#selector) class, but it has additional details about the response, like response headers, status, cookies, etc., as shown below: The `Response` object is the same as the [Selector](../parsing/main_classes.md#selector) class, but it has additional details about the response, like response headers, status, cookies, etc., as shown below:
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> page = Fetcher.get('https://example.com') page = Fetcher.get('https://example.com')
>>> page.status # HTTP status code page.status # HTTP status code
>>> page.reason # Status message page.reason # Status message
>>> page.cookies # Response cookies as a dictionary page.cookies # Response cookies as a dictionary
>>> page.headers # Response headers page.headers # Response headers
>>> page.request_headers # Request headers page.request_headers # Request headers
>>> page.history # Response history of redirections, if any page.history # Response history of redirections, if any
>>> page.body # Raw response body as bytes page.body # Raw response body as bytes
>>> page.encoding # Response encoding page.encoding # Response encoding
>>> page.meta # Response metadata dictionary (e.g., proxy used). Mainly helpful with the spiders system. page.meta # Response metadata dictionary (e.g., proxy used). Mainly helpful with the spiders system.
>>> page.captured_xhr # List of captured XHR/fetch responses (when capture_xhr is enabled on a browser session) page.captured_xhr # List of captured XHR/fetch responses (when capture_xhr is enabled on a browser session)
``` ```
All fetchers return the `Response` object. All fetchers return the `Response` object.
+1 -1
View File
@@ -14,7 +14,7 @@ As we will explain later, to automate the page, you need some knowledge of [Play
You have one primary way to import this Fetcher, which is the same for all fetchers. You have one primary way to import this Fetcher, which is the same for all fetchers.
```python ```python
>>> from scrapling.fetchers import DynamicFetcher from scrapling.fetchers import DynamicFetcher
``` ```
Check out how to configure the parsing options [here](choosing.md#parser-configuration-in-all-fetchers) Check out how to configure the parsing options [here](choosing.md#parser-configuration-in-all-fetchers)
+74 -74
View File
@@ -12,7 +12,7 @@ The `Fetcher` class provides rapid and lightweight HTTP requests using the high-
You have one primary way to import this Fetcher, which is the same for all fetchers. You have one primary way to import this Fetcher, which is the same for all fetchers.
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
``` ```
Check out how to configure the parsing options [here](choosing.md#parser-configuration-in-all-fetchers) Check out how to configure the parsing options [here](choosing.md#parser-configuration-in-all-fetchers)
@@ -54,47 +54,47 @@ Examples are the best way to explain this:
> Hence: `OPTIONS` and `HEAD` methods are not supported. > Hence: `OPTIONS` and `HEAD` methods are not supported.
#### GET #### GET
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> # Basic GET # Basic GET
>>> page = Fetcher.get('https://example.com') page = Fetcher.get('https://example.com')
>>> page = Fetcher.get('https://scrapling.requestcatcher.com/get', stealthy_headers=True) page = Fetcher.get('https://scrapling.requestcatcher.com/get', stealthy_headers=True)
>>> page = Fetcher.get('https://scrapling.requestcatcher.com/get', proxy='http://username:password@localhost:8030') page = Fetcher.get('https://scrapling.requestcatcher.com/get', proxy='http://username:password@localhost:8030')
>>> # With parameters # With parameters
>>> page = Fetcher.get('https://example.com/search', params={'q': 'query'}) page = Fetcher.get('https://example.com/search', params={'q': 'query'})
>>>
>>> # With headers # With headers
>>> page = Fetcher.get('https://example.com', headers={'User-Agent': 'Custom/1.0'}) page = Fetcher.get('https://example.com', headers={'User-Agent': 'Custom/1.0'})
>>> # Basic HTTP authentication # Basic HTTP authentication
>>> page = Fetcher.get("https://example.com", auth=("my_user", "password123")) page = Fetcher.get("https://example.com", auth=("my_user", "password123"))
>>> # Browser impersonation # Browser impersonation
>>> page = Fetcher.get('https://example.com', impersonate='chrome') page = Fetcher.get('https://example.com', impersonate='chrome')
>>> # HTTP/3 support # HTTP/3 support
>>> page = Fetcher.get('https://example.com', http3=True) page = Fetcher.get('https://example.com', http3=True)
``` ```
And for asynchronous requests, it's a small adjustment And for asynchronous requests, it's a small adjustment
```python ```python
>>> from scrapling.fetchers import AsyncFetcher from scrapling.fetchers import AsyncFetcher
>>> # Basic GET # Basic GET
>>> page = await AsyncFetcher.get('https://example.com') page = await AsyncFetcher.get('https://example.com')
>>> page = await AsyncFetcher.get('https://scrapling.requestcatcher.com/get', stealthy_headers=True) page = await AsyncFetcher.get('https://scrapling.requestcatcher.com/get', stealthy_headers=True)
>>> page = await AsyncFetcher.get('https://scrapling.requestcatcher.com/get', proxy='http://username:password@localhost:8030') page = await AsyncFetcher.get('https://scrapling.requestcatcher.com/get', proxy='http://username:password@localhost:8030')
>>> # With parameters # With parameters
>>> page = await AsyncFetcher.get('https://example.com/search', params={'q': 'query'}) page = await AsyncFetcher.get('https://example.com/search', params={'q': 'query'})
>>> >>>
>>> # With headers # With headers
>>> page = await AsyncFetcher.get('https://example.com', headers={'User-Agent': 'Custom/1.0'}) page = await AsyncFetcher.get('https://example.com', headers={'User-Agent': 'Custom/1.0'})
>>> # Basic HTTP authentication # Basic HTTP authentication
>>> page = await AsyncFetcher.get("https://example.com", auth=("my_user", "password123")) page = await AsyncFetcher.get("https://example.com", auth=("my_user", "password123"))
>>> # Browser impersonation # Browser impersonation
>>> page = await AsyncFetcher.get('https://example.com', impersonate='chrome110') page = await AsyncFetcher.get('https://example.com', impersonate='chrome110')
>>> # HTTP/3 support # HTTP/3 support
>>> page = await AsyncFetcher.get('https://example.com', http3=True) page = await AsyncFetcher.get('https://example.com', http3=True)
``` ```
Needless to say, the `page` object in all cases is [Response](choosing.md#response-object) object, which is a [Selector](../parsing/main_classes.md#selector) as we said, so you can use it directly Needless to say, the `page` object in all cases is [Response](choosing.md#response-object) object, which is a [Selector](../parsing/main_classes.md#selector) as we said, so you can use it directly
```python ```python
>>> page.css('.something.something') page.css('.something.something')
>>> page = Fetcher.get('https://api.github.com/events') page = Fetcher.get('https://api.github.com/events')
>>> page.json() >>> page.json()
[{'id': '<redacted>', [{'id': '<redacted>',
'type': 'PushEvent', 'type': 'PushEvent',
@@ -109,62 +109,62 @@ Needless to say, the `page` object in all cases is [Response](choosing.md#respon
``` ```
#### POST #### POST
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> # Basic POST # Basic POST
>>> page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, params={'q': 'query'}) page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, params={'q': 'query'})
>>> page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, stealthy_headers=True) page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, stealthy_headers=True)
>>> page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, proxy='http://username:password@localhost:8030', impersonate="chrome") page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, proxy='http://username:password@localhost:8030', impersonate="chrome")
>>> # Another example of form-encoded data # Another example of form-encoded data
>>> page = Fetcher.post('https://example.com/submit', data={'username': 'user', 'password': 'pass'}, http3=True) page = Fetcher.post('https://example.com/submit', data={'username': 'user', 'password': 'pass'}, http3=True)
>>> # JSON data # JSON data
>>> page = Fetcher.post('https://example.com/api', json={'key': 'value'}) page = Fetcher.post('https://example.com/api', json={'key': 'value'})
``` ```
And for asynchronous requests, it's a small adjustment And for asynchronous requests, it's a small adjustment
```python ```python
>>> from scrapling.fetchers import AsyncFetcher from scrapling.fetchers import AsyncFetcher
>>> # Basic POST # Basic POST
>>> page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}) page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'})
>>> page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, stealthy_headers=True) page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, stealthy_headers=True)
>>> page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, proxy='http://username:password@localhost:8030', impersonate="chrome") page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, proxy='http://username:password@localhost:8030', impersonate="chrome")
>>> # Another example of form-encoded data # Another example of form-encoded data
>>> page = await AsyncFetcher.post('https://example.com/submit', data={'username': 'user', 'password': 'pass'}, http3=True) page = await AsyncFetcher.post('https://example.com/submit', data={'username': 'user', 'password': 'pass'}, http3=True)
>>> # JSON data # JSON data
>>> page = await AsyncFetcher.post('https://example.com/api', json={'key': 'value'}) page = await AsyncFetcher.post('https://example.com/api', json={'key': 'value'})
``` ```
#### PUT #### PUT
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> # Basic PUT # Basic PUT
>>> page = Fetcher.put('https://example.com/update', data={'status': 'updated'}) page = Fetcher.put('https://example.com/update', data={'status': 'updated'})
>>> page = Fetcher.put('https://example.com/update', data={'status': 'updated'}, stealthy_headers=True, impersonate="chrome") page = Fetcher.put('https://example.com/update', data={'status': 'updated'}, stealthy_headers=True, impersonate="chrome")
>>> page = Fetcher.put('https://example.com/update', data={'status': 'updated'}, proxy='http://username:password@localhost:8030') page = Fetcher.put('https://example.com/update', data={'status': 'updated'}, proxy='http://username:password@localhost:8030')
>>> # Another example of form-encoded data # Another example of form-encoded data
>>> page = Fetcher.put("https://scrapling.requestcatcher.com/put", data={'key': ['value1', 'value2']}) page = Fetcher.put("https://scrapling.requestcatcher.com/put", data={'key': ['value1', 'value2']})
``` ```
And for asynchronous requests, it's a small adjustment And for asynchronous requests, it's a small adjustment
```python ```python
>>> from scrapling.fetchers import AsyncFetcher from scrapling.fetchers import AsyncFetcher
>>> # Basic PUT # Basic PUT
>>> page = await AsyncFetcher.put('https://example.com/update', data={'status': 'updated'}) page = await AsyncFetcher.put('https://example.com/update', data={'status': 'updated'})
>>> page = await AsyncFetcher.put('https://example.com/update', data={'status': 'updated'}, stealthy_headers=True, impersonate="chrome") page = await AsyncFetcher.put('https://example.com/update', data={'status': 'updated'}, stealthy_headers=True, impersonate="chrome")
>>> page = await AsyncFetcher.put('https://example.com/update', data={'status': 'updated'}, proxy='http://username:password@localhost:8030') page = await AsyncFetcher.put('https://example.com/update', data={'status': 'updated'}, proxy='http://username:password@localhost:8030')
>>> # Another example of form-encoded data # Another example of form-encoded data
>>> page = await AsyncFetcher.put("https://scrapling.requestcatcher.com/put", data={'key': ['value1', 'value2']}) page = await AsyncFetcher.put("https://scrapling.requestcatcher.com/put", data={'key': ['value1', 'value2']})
``` ```
#### DELETE #### DELETE
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> page = Fetcher.delete('https://example.com/resource/123') page = Fetcher.delete('https://example.com/resource/123')
>>> page = Fetcher.delete('https://example.com/resource/123', stealthy_headers=True, impersonate="chrome") page = Fetcher.delete('https://example.com/resource/123', stealthy_headers=True, impersonate="chrome")
>>> page = Fetcher.delete('https://example.com/resource/123', proxy='http://username:password@localhost:8030') page = Fetcher.delete('https://example.com/resource/123', proxy='http://username:password@localhost:8030')
``` ```
And for asynchronous requests, it's a small adjustment And for asynchronous requests, it's a small adjustment
```python ```python
>>> from scrapling.fetchers import AsyncFetcher from scrapling.fetchers import AsyncFetcher
>>> page = await AsyncFetcher.delete('https://example.com/resource/123') page = await AsyncFetcher.delete('https://example.com/resource/123')
>>> page = await AsyncFetcher.delete('https://example.com/resource/123', stealthy_headers=True, impersonate="chrome") page = await AsyncFetcher.delete('https://example.com/resource/123', stealthy_headers=True, impersonate="chrome")
>>> page = await AsyncFetcher.delete('https://example.com/resource/123', proxy='http://username:password@localhost:8030') page = await AsyncFetcher.delete('https://example.com/resource/123', proxy='http://username:password@localhost:8030')
``` ```
## Session Management ## Session Management
+1 -1
View File
@@ -15,7 +15,7 @@ As with [DynamicFetcher](dynamic.md#introduction), you will need some knowledge
You have one primary way to import this Fetcher, which is the same for all fetchers. You have one primary way to import this Fetcher, which is the same for all fetchers.
```python ```python
>>> from scrapling.fetchers import StealthyFetcher from scrapling.fetchers import StealthyFetcher
``` ```
Check out how to configure the parsing options [here](choosing.md#parser-configuration-in-all-fetchers) Check out how to configure the parsing options [here](choosing.md#parser-configuration-in-all-fetchers)
+28 -30
View File
@@ -263,19 +263,19 @@ page = Fetcher.get('https://scrapling.requestcatcher.com/get', impersonate="chro
``` ```
With that out of the way, here's how to do all HTTP methods: With that out of the way, here's how to do all HTTP methods:
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> page = Fetcher.get('https://scrapling.requestcatcher.com/get', stealthy_headers=True) page = Fetcher.get('https://scrapling.requestcatcher.com/get', stealthy_headers=True)
>>> page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, proxy='http://username:password@localhost:8030') page = Fetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, proxy='http://username:password@localhost:8030')
>>> page = Fetcher.put('https://scrapling.requestcatcher.com/put', data={'key': 'value'}) page = Fetcher.put('https://scrapling.requestcatcher.com/put', data={'key': 'value'})
>>> page = Fetcher.delete('https://scrapling.requestcatcher.com/delete') page = Fetcher.delete('https://scrapling.requestcatcher.com/delete')
``` ```
For Async requests, you will replace the import like below: For Async requests, you will replace the import like below:
```python ```python
>>> from scrapling.fetchers import AsyncFetcher from scrapling.fetchers import AsyncFetcher
>>> page = await AsyncFetcher.get('https://scrapling.requestcatcher.com/get', stealthy_headers=True) page = await AsyncFetcher.get('https://scrapling.requestcatcher.com/get', stealthy_headers=True)
>>> page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, proxy='http://username:password@localhost:8030') page = await AsyncFetcher.post('https://scrapling.requestcatcher.com/post', data={'key': 'value'}, proxy='http://username:password@localhost:8030')
>>> page = await AsyncFetcher.put('https://scrapling.requestcatcher.com/put', data={'key': 'value'}) page = await AsyncFetcher.put('https://scrapling.requestcatcher.com/put', data={'key': 'value'})
>>> page = await AsyncFetcher.delete('https://scrapling.requestcatcher.com/delete') page = await AsyncFetcher.delete('https://scrapling.requestcatcher.com/delete')
``` ```
!!! note "Notes:" !!! note "Notes:"
@@ -291,14 +291,13 @@ We have you covered if you deal with dynamic websites like most today!
The `DynamicFetcher` class (formerly `PlayWrightFetcher`) offers many options for fetching and loading web pages using Chromium-based browsers. The `DynamicFetcher` class (formerly `PlayWrightFetcher`) offers many options for fetching and loading web pages using Chromium-based browsers.
```python ```python
>>> from scrapling.fetchers import DynamicFetcher from scrapling.fetchers import DynamicFetcher
>>> page = DynamicFetcher.fetch('https://www.google.com/search?q=%22Scrapling%22', disable_resources=True) # Vanilla Playwright option page = DynamicFetcher.fetch('https://www.google.com/search?q=%22Scrapling%22', disable_resources=True) # Vanilla Playwright option
>>> page.css("#search a::attr(href)").get() page.css("#search a::attr(href)").get() # -> 'https://github.com/D4Vinci/Scrapling'
'https://github.com/D4Vinci/Scrapling'
>>> # The async version of fetch # The async version of fetch
>>> page = await DynamicFetcher.async_fetch('https://www.google.com/search?q=%22Scrapling%22', disable_resources=True) page = await DynamicFetcher.async_fetch('https://www.google.com/search?q=%22Scrapling%22', disable_resources=True)
>>> page.css("#search a::attr(href)").get() page.css("#search a::attr(href)").get() # -> 'https://github.com/D4Vinci/Scrapling'
'https://github.com/D4Vinci/Scrapling'
``` ```
It's built on top of [Playwright](https://playwright.dev/python/), and it's currently providing two main run options that can be mixed as you want: It's built on top of [Playwright](https://playwright.dev/python/), and it's currently providing two main run options that can be mixed as you want:
@@ -323,18 +322,17 @@ Some of the things it does:
6. and other anti-protection options... 6. and other anti-protection options...
```python ```python
>>> from scrapling.fetchers import StealthyFetcher from scrapling.fetchers import StealthyFetcher
>>> page = StealthyFetcher.fetch('https://www.browserscan.net/bot-detection') # Running headless by default page = StealthyFetcher.fetch('https://www.browserscan.net/bot-detection') # Running headless by default
>>> page.status == 200 page.status == 200 # -> True
True
>>> page = StealthyFetcher.fetch('https://nopecha.com/demo/cloudflare', solve_cloudflare=True) # Solve Cloudflare captcha automatically if presented page = StealthyFetcher.fetch('https://nopecha.com/demo/cloudflare', solve_cloudflare=True) # Solve Cloudflare captcha automatically if presented
>>> page.status == 200 page.status == 200 # -> True
True
>>> page = StealthyFetcher.fetch('https://www.browserscan.net/bot-detection', humanize=True, os_randomize=True) # and the rest of arguments... page = StealthyFetcher.fetch('https://www.browserscan.net/bot-detection', humanize=True, os_randomize=True) # and the rest of arguments...
>>> # The async version of fetch # The async version of fetch
>>> page = await StealthyFetcher.async_fetch('https://www.browserscan.net/bot-detection') page = await StealthyFetcher.async_fetch('https://www.browserscan.net/bot-detection')
>>> page.status == 200 page.status == 200 # -> True
True
``` ```
Again, this is just the tip of the iceberg with this fetcher. Check out the rest from [here](fetching/stealthy.md) for all details and the complete list of arguments. Again, this is just the tip of the iceberg with this fetcher. Check out the rest from [here](fetching/stealthy.md) for all details and the complete list of arguments.
+19 -22
View File
@@ -76,22 +76,19 @@ If I want to extract the Questions button from the old design, I can use a selec
Now, let's test the same selector in both versions Now, let's test the same selector in both versions
```python ```python
>> from scrapling import Fetcher from scrapling import Fetcher
>> selector = '#hmenus > div:nth-child(1) > ul > li:nth-child(1) > a' selector = '#hmenus > div:nth-child(1) > ul > li:nth-child(1) > a'
>> old_url = "https://web.archive.org/web/20100102003420/http://stackoverflow.com/" old_url = "https://web.archive.org/web/20100102003420/http://stackoverflow.com/"
>> new_url = "https://stackoverflow.com/" new_url = "https://stackoverflow.com/"
>> Fetcher.configure(adaptive = True, adaptive_domain='stackoverflow.com') Fetcher.configure(adaptive = True, adaptive_domain='stackoverflow.com')
>> page = Fetcher.get(old_url, timeout=30)
>> page = Fetcher.get(old_url, timeout=30) element1 = page.css(selector, auto_save=True)[0]
>> element1 = page.css(selector, auto_save=True)[0] # Same selector but used in the updated website
>> page = Fetcher.get(new_url)
>> # Same selector but used in the updated website element2 = page.css(selector, adaptive=True)[0]
>> page = Fetcher.get(new_url)
>> element2 = page.css(selector, adaptive=True)[0] if element1.text == element2.text:
>> print('Scrapling found the same element in the old and new designs!') # Spoiler alert: it does!
>> if element1.text == element2.text:
... print('Scrapling found the same element in the old and new designs!')
'Scrapling found the same element in the old and new designs!'
``` ```
Note that I introduced a new argument called `adaptive_domain`. This is because, for Scrapling, these are two different domains (`archive.org` and `stackoverflow.com`), so Scrapling will isolate their `adaptive` data. To inform Scrapling that they are the same website, we must pass the custom domain we wish to use while saving `adaptive` data for both, ensuring Scrapling doesn't isolate them. Note that I introduced a new argument called `adaptive_domain`. This is because, for Scrapling, these are two different domains (`archive.org` and `stackoverflow.com`), so Scrapling will isolate their `adaptive` data. To inform Scrapling that they are the same website, we must pass the custom domain we wish to use while saving `adaptive` data for both, ensuring Scrapling doesn't isolate them.
@@ -141,11 +138,11 @@ First, you must enable the `adaptive` feature by passing `adaptive=True` to the
Examples: Examples:
```python ```python
>>> from scrapling import Selector, Fetcher from scrapling import Selector, Fetcher
>>> page = Selector(html_doc, adaptive=True) page = Selector(html_doc, adaptive=True)
# OR # OR
>>> Fetcher.adaptive = True Fetcher.adaptive = True
>>> page = Fetcher.get('https://example.com') page = Fetcher.get('https://example.com')
``` ```
If you are using the [Selector](main_classes.md#selector) class, you need to pass the url of the website you are using with the argument `url` so Scrapling can separate the properties saved for each element by domain. If you are using the [Selector](main_classes.md#selector) class, you need to pass the url of the website you are using with the argument `url` so Scrapling can separate the properties saved for each element by domain.
@@ -175,11 +172,11 @@ You manually save and retrieve an element, then relocate it, which all happens w
First, let's say you got an element like this by text: First, let's say you got an element like this by text:
```python ```python
>>> element = page.find_by_text('Tipping the Velvet', first_match=True) element = page.find_by_text('Tipping the Velvet', first_match=True)
``` ```
You can save its unique properties using the `save` method, as shown below, but you must set the identifier yourself. For this example, I chose `my_special_element` as an identifier, but it's best to use a meaningful identifier in your code for the same reason you use meaningful variable names :) You can save its unique properties using the `save` method, as shown below, but you must set the identifier yourself. For this example, I chose `my_special_element` as an identifier, but it's best to use a meaningful identifier in your code for the same reason you use meaningful variable names :)
```python ```python
>>> page.save(element, 'my_special_element') page.save(element, 'my_special_element')
``` ```
Now, later, when you want to retrieve it and relocate it inside the page with `adaptive`, it would be like this Now, later, when you want to retrieve it and relocate it inside the page with `adaptive`, it would be like this
```python ```python
+12 -12
View File
@@ -140,14 +140,14 @@ Getting the attributes of the element
``` ```
Access a specific attribute with any of the following Access a specific attribute with any of the following
```python ```python
>>> article.attrib['class'] article.attrib['class']
>>> article.attrib.get('class') article.attrib.get('class')
>>> article['class'] # new in v0.3 article['class'] # new in v0.3
``` ```
Check if the attributes contain a specific attribute with any of the methods below Check if the attributes contain a specific attribute with any of the methods below
```python ```python
>>> 'class' in article.attrib 'class' in article.attrib
>>> 'class' in article # new in v0.3 'class' in article # new in v0.3
``` ```
Get the HTML content of the element Get the HTML content of the element
```python ```python
@@ -292,13 +292,13 @@ In the [Selector](#selector) class, all methods/properties that should return a
Starting with v0.4, all selection methods consistently return [Selector](#selector)/[Selectors](#selectors) objects, even for text nodes and attribute values. Text nodes (selected via `::text`, `/text()`, `::attr()`, `/@attr`) are wrapped in [Selector](#selector) objects. These text node selectors have `tag` set to `"#text"`, and their `text` property returns the text value. You can still access the text value directly, and all other properties return empty/default values gracefully. Starting with v0.4, all selection methods consistently return [Selector](#selector)/[Selectors](#selectors) objects, even for text nodes and attribute values. Text nodes (selected via `::text`, `/text()`, `::attr()`, `/@attr`) are wrapped in [Selector](#selector) objects. These text node selectors have `tag` set to `"#text"`, and their `text` property returns the text value. You can still access the text value directly, and all other properties return empty/default values gracefully.
```python ```python
>>> page.css('a::text') # -> Selectors (of text node Selectors) page.css('a::text') # -> Selectors (of text node Selectors)
>>> page.xpath('//a/text()') # -> Selectors page.xpath('//a/text()') # -> Selectors
>>> page.css('a::text').get() # -> TextHandler (the first text value) page.css('a::text').get() # -> TextHandler (the first text value)
>>> page.css('a::text').getall() # -> TextHandlers (all text values) page.css('a::text').getall() # -> TextHandlers (all text values)
>>> page.css('a::attr(href)') # -> Selectors page.css('a::attr(href)') # -> Selectors
>>> page.xpath('//a/@href') # -> Selectors page.xpath('//a/@href') # -> Selectors
>>> page.css('.price_color') # -> Selectors page.css('.price_color') # -> Selectors
``` ```
### Data extraction methods ### Data extraction methods
+2 -2
View File
@@ -362,8 +362,8 @@ Check examples to clear any confusion :)
### Examples ### Examples
```python ```python
>>> from scrapling.fetchers import Fetcher from scrapling.fetchers import Fetcher
>>> page = Fetcher.get('https://quotes.toscrape.com/') page = Fetcher.get('https://quotes.toscrape.com/')
``` ```
Find all elements with the tag name `div`. Find all elements with the tag name `div`.
```python ```python