v0.3.12
This commit is contained in:
@@ -58,7 +58,7 @@ jobs:
|
|||||||
- name: Install all browsers dependencies
|
- name: Install all browsers dependencies
|
||||||
run: |
|
run: |
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
python3 -m pip install playwright>=1.55.0 patchright>=1.55.0 camoufox
|
python3 -m pip install playwright==1.56.0 patchright==1.56.0 camoufox>=0.4.11
|
||||||
|
|
||||||
- name: Get Playwright version
|
- name: Get Playwright version
|
||||||
id: playwright-version
|
id: playwright-version
|
||||||
@@ -83,7 +83,11 @@ jobs:
|
|||||||
- name: Install Playwright browsers
|
- name: Install Playwright browsers
|
||||||
run: |
|
run: |
|
||||||
echo "Cache hit: ${{ steps.playwright-cache.outputs.cache-hit }}"
|
echo "Cache hit: ${{ steps.playwright-cache.outputs.cache-hit }}"
|
||||||
python3 -m playwright install chromium
|
if [ "${{ steps.playwright-cache.outputs.cache-hit }}" != "true" ]; then
|
||||||
|
python3 -m playwright install chromium
|
||||||
|
else
|
||||||
|
echo "Skipping install - using cached Playwright browsers"
|
||||||
|
fi
|
||||||
python3 -m playwright install-deps chromium firefox
|
python3 -m playwright install-deps chromium firefox
|
||||||
|
|
||||||
- name: Get Camoufox version
|
- name: Get Camoufox version
|
||||||
@@ -108,7 +112,11 @@ jobs:
|
|||||||
- name: Install Camoufox browser
|
- name: Install Camoufox browser
|
||||||
run: |
|
run: |
|
||||||
echo "Cache hit: ${{ steps.camoufox-cache.outputs.cache-hit }}"
|
echo "Cache hit: ${{ steps.camoufox-cache.outputs.cache-hit }}"
|
||||||
python3 -m camoufox fetch --browserforge
|
if [ "${{ steps.camoufox-cache.outputs.cache-hit }}" != "true" ]; then
|
||||||
|
python3 -m camoufox fetch --browserforge
|
||||||
|
else
|
||||||
|
echo "Skipping fetch - using cached Camoufox browser"
|
||||||
|
fi
|
||||||
|
|
||||||
# Cache tox environments
|
# Cache tox environments
|
||||||
- name: Cache tox environments
|
- name: Cache tox environments
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ Scrapling provides many options with this fetcher and its session classes. To ma
|
|||||||
| stealth | Enables stealth mode; you should always check the documentation to see what the stealth mode does currently. | ✔️ |
|
| stealth | Enables stealth mode; you should always check the documentation to see what the stealth mode does currently. | ✔️ |
|
||||||
| real_chrome | If you have a Chrome browser installed on your device, enable this, and the Fetcher will launch and use an instance of your browser. | ✔️ |
|
| real_chrome | If you have a Chrome browser installed on your device, enable this, and the Fetcher will launch and use an instance of your browser. | ✔️ |
|
||||||
| locale | Set the locale for the browser if wanted. The default value is `en-US`. | ✔️ |
|
| locale | Set the locale for the browser if wanted. The default value is `en-US`. | ✔️ |
|
||||||
|
| timezone_id | Set the timezone for the browser if wanted. | ✔️ |
|
||||||
| cdp_url | Instead of launching a new browser instance, connect to this CDP URL to control real browsers through CDP. | ✔️ |
|
| cdp_url | Instead of launching a new browser instance, connect to this CDP URL to control real browsers through CDP. | ✔️ |
|
||||||
| user_data_dir | Path to a User Data Directory, which stores browser session data like cookies and local storage. The default is to create a temporary directory. **Only Works with sessions** | ✔️ |
|
| user_data_dir | Path to a User Data Directory, which stores browser session data like cookies and local storage. The default is to create a temporary directory. **Only Works with sessions** | ✔️ |
|
||||||
| extra_flags | A list of additional browser flags to pass to the browser on launch. | ✔️ |
|
| extra_flags | A list of additional browser flags to pass to the browser on launch. | ✔️ |
|
||||||
|
|||||||
+6
-6
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
[project]
|
[project]
|
||||||
name = "scrapling"
|
name = "scrapling"
|
||||||
# Static version instead of a dynamic version so we can get better layer caching while building docker, check the docker file to understand
|
# Static version instead of a dynamic version so we can get better layer caching while building docker, check the docker file to understand
|
||||||
version = "0.3.11"
|
version = "0.3.12"
|
||||||
description = "Scrapling is an undetectable, powerful, flexible, high-performance Python library that makes Web Scraping easy and effortless as it should be!"
|
description = "Scrapling is an undetectable, powerful, flexible, high-performance Python library that makes Web Scraping easy and effortless as it should be!"
|
||||||
readme = {file = "docs/README.md", content-type = "text/markdown"}
|
readme = {file = "docs/README.md", content-type = "text/markdown"}
|
||||||
license = {file = "LICENSE"}
|
license = {file = "LICENSE"}
|
||||||
@@ -59,22 +59,22 @@ classifiers = [
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"lxml>=6.0.2",
|
"lxml>=6.0.2",
|
||||||
"cssselect>=1.3.0",
|
"cssselect>=1.3.0",
|
||||||
"orjson>=3.11.4",
|
"orjson>=3.11.5",
|
||||||
"tldextract>=5.3.0",
|
"tldextract>=5.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
fetchers = [
|
fetchers = [
|
||||||
"click>=8.3.0",
|
"click>=8.3.0",
|
||||||
"curl_cffi>=0.13.0",
|
"curl_cffi>=0.14.0",
|
||||||
"playwright>=1.56.0",
|
"playwright==1.56.0",
|
||||||
"patchright>=1.56.0",
|
"patchright==1.56.0",
|
||||||
"camoufox>=0.4.11",
|
"camoufox>=0.4.11",
|
||||||
"geoip2>=5.2.0",
|
"geoip2>=5.2.0",
|
||||||
"msgspec>=0.20.0",
|
"msgspec>=0.20.0",
|
||||||
]
|
]
|
||||||
ai = [
|
ai = [
|
||||||
"mcp>=1.23.0",
|
"mcp>=1.24.0",
|
||||||
"markdownify>=1.2.0",
|
"markdownify>=1.2.0",
|
||||||
"scrapling[fetchers]",
|
"scrapling[fetchers]",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
__author__ = "Karim Shoair (karim.shoair@pm.me)"
|
__author__ = "Karim Shoair (karim.shoair@pm.me)"
|
||||||
__version__ = "0.3.11"
|
__version__ = "0.3.12"
|
||||||
__copyright__ = "Copyright (c) 2024 Karim Shoair"
|
__copyright__ = "Copyright (c) 2024 Karim Shoair"
|
||||||
|
|
||||||
from typing import Any, TYPE_CHECKING
|
from typing import Any, TYPE_CHECKING
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ from pathlib import Path
|
|||||||
from subprocess import check_output
|
from subprocess import check_output
|
||||||
from sys import executable as python_executable
|
from sys import executable as python_executable
|
||||||
|
|
||||||
from curl_cffi.requests import impersonate
|
|
||||||
|
|
||||||
from scrapling.core.utils import log
|
from scrapling.core.utils import log
|
||||||
from scrapling.engines.toolbelt.custom import Response
|
from scrapling.engines.toolbelt.custom import Response
|
||||||
from scrapling.core.utils._shell import _CookieParser, _ParseHeaders
|
from scrapling.core.utils._shell import _CookieParser, _ParseHeaders
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class SyncSession:
|
|||||||
self.context: BrowserContext | Any = None
|
self.context: BrowserContext | Any = None
|
||||||
self._closed = False
|
self._closed = False
|
||||||
|
|
||||||
def __create__(self):
|
def start(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def close(self): # pragma: no cover
|
def close(self): # pragma: no cover
|
||||||
@@ -59,7 +59,7 @@ class SyncSession:
|
|||||||
self._closed = True
|
self._closed = True
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.__create__()
|
self.start()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
@@ -145,7 +145,7 @@ class AsyncSession:
|
|||||||
self._closed = False
|
self._closed = False
|
||||||
self._lock = Lock()
|
self._lock = Lock()
|
||||||
|
|
||||||
async def __create__(self):
|
async def start(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
@@ -164,7 +164,7 @@ class AsyncSession:
|
|||||||
self._closed = True
|
self._closed = True
|
||||||
|
|
||||||
async def __aenter__(self):
|
async def __aenter__(self):
|
||||||
await self.__create__()
|
await self.start()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||||
@@ -281,6 +281,7 @@ class DynamicSessionMixin:
|
|||||||
self._wait_selector_state = config.wait_selector_state
|
self._wait_selector_state = config.wait_selector_state
|
||||||
self._extra_flags = config.extra_flags
|
self._extra_flags = config.extra_flags
|
||||||
self._selector_config = config.selector_config
|
self._selector_config = config.selector_config
|
||||||
|
self._timezone_id = config.timezone_id
|
||||||
self._additional_args = config.additional_args
|
self._additional_args = config.additional_args
|
||||||
self._page_action = config.page_action
|
self._page_action = config.page_action
|
||||||
self._user_data_dir = config.user_data_dir
|
self._user_data_dir = config.user_data_dir
|
||||||
@@ -304,6 +305,7 @@ class DynamicSessionMixin:
|
|||||||
self._stealth,
|
self._stealth,
|
||||||
self._hide_canvas,
|
self._hide_canvas,
|
||||||
self._disable_webgl,
|
self._disable_webgl,
|
||||||
|
self._timezone_id,
|
||||||
tuple(self._extra_flags) if self._extra_flags else tuple(),
|
tuple(self._extra_flags) if self._extra_flags else tuple(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -102,16 +102,19 @@ class StealthySession(StealthySessionMixin, SyncSession):
|
|||||||
self.__validate__(**kwargs)
|
self.__validate__(**kwargs)
|
||||||
super().__init__(max_pages=self._max_pages)
|
super().__init__(max_pages=self._max_pages)
|
||||||
|
|
||||||
def __create__(self):
|
def start(self):
|
||||||
"""Create a browser for this instance and context."""
|
"""Create a browser for this instance and context."""
|
||||||
self.playwright = sync_playwright().start()
|
if not self.playwright:
|
||||||
self.context = self.playwright.firefox.launch_persistent_context(**self.launch_options)
|
self.playwright = sync_playwright().start()
|
||||||
|
self.context = self.playwright.firefox.launch_persistent_context(**self.launch_options)
|
||||||
|
|
||||||
if self._init_script: # pragma: no cover
|
if self._init_script: # pragma: no cover
|
||||||
self.context.add_init_script(path=self._init_script)
|
self.context.add_init_script(path=self._init_script)
|
||||||
|
|
||||||
if self._cookies: # pragma: no cover
|
if self._cookies: # pragma: no cover
|
||||||
self.context.add_cookies(self._cookies)
|
self.context.add_cookies(self._cookies)
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Session has been already started")
|
||||||
|
|
||||||
def _cloudflare_solver(self, page: Page) -> None: # pragma: no cover
|
def _cloudflare_solver(self, page: Page) -> None: # pragma: no cover
|
||||||
"""Solve the cloudflare challenge displayed on the playwright page passed
|
"""Solve the cloudflare challenge displayed on the playwright page passed
|
||||||
@@ -299,18 +302,21 @@ class AsyncStealthySession(StealthySessionMixin, AsyncSession):
|
|||||||
self.__validate__(**kwargs)
|
self.__validate__(**kwargs)
|
||||||
super().__init__(max_pages=self._max_pages)
|
super().__init__(max_pages=self._max_pages)
|
||||||
|
|
||||||
async def __create__(self):
|
async def start(self):
|
||||||
"""Create a browser for this instance and context."""
|
"""Create a browser for this instance and context."""
|
||||||
self.playwright: AsyncPlaywright = await async_playwright().start()
|
if not self.playwright:
|
||||||
self.context: AsyncBrowserContext = await self.playwright.firefox.launch_persistent_context(
|
self.playwright: AsyncPlaywright = await async_playwright().start()
|
||||||
**self.launch_options
|
self.context: AsyncBrowserContext = await self.playwright.firefox.launch_persistent_context(
|
||||||
)
|
**self.launch_options
|
||||||
|
)
|
||||||
|
|
||||||
if self._init_script: # pragma: no cover
|
if self._init_script: # pragma: no cover
|
||||||
await self.context.add_init_script(path=self._init_script)
|
await self.context.add_init_script(path=self._init_script)
|
||||||
|
|
||||||
if self._cookies:
|
if self._cookies:
|
||||||
await self.context.add_cookies(self._cookies) # pyright: ignore [reportArgumentType]
|
await self.context.add_cookies(self._cookies) # pyright: ignore [reportArgumentType]
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Session has been already started")
|
||||||
|
|
||||||
async def _cloudflare_solver(self, page: async_Page): # pragma: no cover
|
async def _cloudflare_solver(self, page: async_Page): # pragma: no cover
|
||||||
"""Solve the cloudflare challenge displayed on the playwright page passed. The async version
|
"""Solve the cloudflare challenge displayed on the playwright page passed. The async version
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ def _launch_kwargs(
|
|||||||
stealth,
|
stealth,
|
||||||
hide_canvas,
|
hide_canvas,
|
||||||
disable_webgl,
|
disable_webgl,
|
||||||
|
timezone_id,
|
||||||
extra_flags: Tuple,
|
extra_flags: Tuple,
|
||||||
) -> Tuple:
|
) -> Tuple:
|
||||||
"""Creates the arguments we will use while launching playwright's browser"""
|
"""Creates the arguments we will use while launching playwright's browser"""
|
||||||
@@ -79,6 +80,7 @@ def _launch_kwargs(
|
|||||||
|
|
||||||
launch_kwargs = {
|
launch_kwargs = {
|
||||||
"locale": locale,
|
"locale": locale,
|
||||||
|
"timezone_id": timezone_id or None,
|
||||||
"headless": headless,
|
"headless": headless,
|
||||||
"args": base_args,
|
"args": base_args,
|
||||||
"color_scheme": "dark", # Bypasses the 'prefersLightColor' check in creepjs
|
"color_scheme": "dark", # Bypasses the 'prefersLightColor' check in creepjs
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ class DynamicSession(DynamicSessionMixin, SyncSession):
|
|||||||
:param wait_selector: Wait for a specific CSS selector to be in a specific state.
|
:param wait_selector: Wait for a specific CSS selector to be in a specific state.
|
||||||
:param init_script: An absolute path to a JavaScript file to be executed on page creation for all pages in this session.
|
:param init_script: An absolute path to a JavaScript file to be executed on page creation for all pages in this session.
|
||||||
:param locale: Set the locale for the browser if wanted. The default value is `en-US`.
|
:param locale: Set the locale for the browser if wanted. The default value is `en-US`.
|
||||||
|
:param timezone_id: Set the timezone for the browser if wanted.
|
||||||
:param wait_selector_state: The state to wait for the selector given with `wait_selector`. The default state is `attached`.
|
:param wait_selector_state: The state to wait for the selector given with `wait_selector`. The default state is `attached`.
|
||||||
:param stealth: Enables stealth mode, check the documentation to see what stealth mode does currently.
|
:param stealth: Enables stealth mode, check the documentation to see what stealth mode does currently.
|
||||||
:param real_chrome: If you have a Chrome browser installed on your device, enable this, and the Fetcher will launch an instance of your browser and use it.
|
:param real_chrome: If you have a Chrome browser installed on your device, enable this, and the Fetcher will launch an instance of your browser and use it.
|
||||||
@@ -95,24 +96,27 @@ class DynamicSession(DynamicSessionMixin, SyncSession):
|
|||||||
self.__validate__(**kwargs)
|
self.__validate__(**kwargs)
|
||||||
super().__init__(max_pages=self._max_pages)
|
super().__init__(max_pages=self._max_pages)
|
||||||
|
|
||||||
def __create__(self):
|
def start(self):
|
||||||
"""Create a browser for this instance and context."""
|
"""Create a browser for this instance and context."""
|
||||||
sync_context = sync_patchright if self._stealth else sync_playwright
|
if not self.playwright:
|
||||||
|
sync_context = sync_patchright if self._stealth else sync_playwright
|
||||||
|
|
||||||
self.playwright: Playwright = sync_context().start() # pyright: ignore [reportAttributeAccessIssue]
|
self.playwright: Playwright = sync_context().start() # pyright: ignore [reportAttributeAccessIssue]
|
||||||
|
|
||||||
if self._cdp_url: # pragma: no cover
|
if self._cdp_url: # pragma: no cover
|
||||||
self.context = self.playwright.chromium.connect_over_cdp(endpoint_url=self._cdp_url).new_context(
|
self.context = self.playwright.chromium.connect_over_cdp(endpoint_url=self._cdp_url).new_context(
|
||||||
**self.context_options
|
**self.context_options
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
self.context = self.playwright.chromium.launch_persistent_context(**self.launch_options)
|
||||||
|
|
||||||
|
if self._init_script: # pragma: no cover
|
||||||
|
self.context.add_init_script(path=self._init_script)
|
||||||
|
|
||||||
|
if self._cookies: # pragma: no cover
|
||||||
|
self.context.add_cookies(self._cookies)
|
||||||
else:
|
else:
|
||||||
self.context = self.playwright.chromium.launch_persistent_context(**self.launch_options)
|
raise RuntimeError("Session has been already started")
|
||||||
|
|
||||||
if self._init_script: # pragma: no cover
|
|
||||||
self.context.add_init_script(path=self._init_script)
|
|
||||||
|
|
||||||
if self._cookies: # pragma: no cover
|
|
||||||
self.context.add_cookies(self._cookies)
|
|
||||||
|
|
||||||
def fetch(self, url: str, **kwargs: Unpack[PlaywrightFetchParams]) -> Response:
|
def fetch(self, url: str, **kwargs: Unpack[PlaywrightFetchParams]) -> Response:
|
||||||
"""Opens up the browser and do your request based on your chosen options.
|
"""Opens up the browser and do your request based on your chosen options.
|
||||||
@@ -209,6 +213,7 @@ class AsyncDynamicSession(DynamicSessionMixin, AsyncSession):
|
|||||||
:param wait_selector: Wait for a specific CSS selector to be in a specific state.
|
:param wait_selector: Wait for a specific CSS selector to be in a specific state.
|
||||||
:param init_script: An absolute path to a JavaScript file to be executed on page creation for all pages in this session.
|
:param init_script: An absolute path to a JavaScript file to be executed on page creation for all pages in this session.
|
||||||
:param locale: Set the locale for the browser if wanted. The default value is `en-US`.
|
:param locale: Set the locale for the browser if wanted. The default value is `en-US`.
|
||||||
|
:param timezone_id: Set the timezone for the browser if wanted.
|
||||||
:param wait_selector_state: The state to wait for the selector given with `wait_selector`. The default state is `attached`.
|
:param wait_selector_state: The state to wait for the selector given with `wait_selector`. The default state is `attached`.
|
||||||
:param stealth: Enables stealth mode, check the documentation to see what stealth mode does currently.
|
:param stealth: Enables stealth mode, check the documentation to see what stealth mode does currently.
|
||||||
:param real_chrome: If you have a Chrome browser installed on your device, enable this, and the Fetcher will launch an instance of your browser and use it.
|
:param real_chrome: If you have a Chrome browser installed on your device, enable this, and the Fetcher will launch an instance of your browser and use it.
|
||||||
@@ -227,25 +232,28 @@ class AsyncDynamicSession(DynamicSessionMixin, AsyncSession):
|
|||||||
self.__validate__(**kwargs)
|
self.__validate__(**kwargs)
|
||||||
super().__init__(max_pages=self._max_pages)
|
super().__init__(max_pages=self._max_pages)
|
||||||
|
|
||||||
async def __create__(self):
|
async def start(self):
|
||||||
"""Create a browser for this instance and context."""
|
"""Create a browser for this instance and context."""
|
||||||
async_context = async_patchright if self._stealth else async_playwright
|
if not self.playwright:
|
||||||
|
async_context = async_patchright if self._stealth else async_playwright
|
||||||
|
|
||||||
self.playwright: AsyncPlaywright = await async_context().start() # pyright: ignore [reportAttributeAccessIssue]
|
self.playwright: AsyncPlaywright = await async_context().start() # pyright: ignore [reportAttributeAccessIssue]
|
||||||
|
|
||||||
if self._cdp_url:
|
if self._cdp_url:
|
||||||
browser = await self.playwright.chromium.connect_over_cdp(endpoint_url=self._cdp_url)
|
browser = await self.playwright.chromium.connect_over_cdp(endpoint_url=self._cdp_url)
|
||||||
self.context: AsyncBrowserContext = await browser.new_context(**self.context_options)
|
self.context: AsyncBrowserContext = await browser.new_context(**self.context_options)
|
||||||
|
else:
|
||||||
|
self.context: AsyncBrowserContext = await self.playwright.chromium.launch_persistent_context(
|
||||||
|
**self.launch_options
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._init_script: # pragma: no cover
|
||||||
|
await self.context.add_init_script(path=self._init_script)
|
||||||
|
|
||||||
|
if self._cookies:
|
||||||
|
await self.context.add_cookies(self._cookies) # pyright: ignore
|
||||||
else:
|
else:
|
||||||
self.context: AsyncBrowserContext = await self.playwright.chromium.launch_persistent_context(
|
raise RuntimeError("Session has been already started")
|
||||||
**self.launch_options
|
|
||||||
)
|
|
||||||
|
|
||||||
if self._init_script: # pragma: no cover
|
|
||||||
await self.context.add_init_script(path=self._init_script)
|
|
||||||
|
|
||||||
if self._cookies:
|
|
||||||
await self.context.add_cookies(self._cookies) # pyright: ignore
|
|
||||||
|
|
||||||
async def fetch(self, url: str, **kwargs: Unpack[PlaywrightFetchParams]) -> Response:
|
async def fetch(self, url: str, **kwargs: Unpack[PlaywrightFetchParams]) -> Response:
|
||||||
"""Opens up the browser and do your request based on your chosen options.
|
"""Opens up the browser and do your request based on your chosen options.
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ class PlaywrightConfig(Struct, kw_only=True, frozen=False, weakref=True):
|
|||||||
load_dom: bool = True
|
load_dom: bool = True
|
||||||
wait_selector_state: SelectorWaitStates = "attached"
|
wait_selector_state: SelectorWaitStates = "attached"
|
||||||
user_data_dir: str = ""
|
user_data_dir: str = ""
|
||||||
|
timezone_id: str = ""
|
||||||
extra_flags: Optional[List[str]] = None
|
extra_flags: Optional[List[str]] = None
|
||||||
selector_config: Optional[Dict] = {}
|
selector_config: Optional[Dict] = {}
|
||||||
additional_args: Optional[Dict] = {}
|
additional_args: Optional[Dict] = {}
|
||||||
|
|||||||
+7
-2
@@ -939,8 +939,13 @@ class Selector(SelectorsGeneration):
|
|||||||
# Operations on text functions
|
# Operations on text functions
|
||||||
def json(self) -> Dict:
|
def json(self) -> Dict:
|
||||||
"""Return JSON response if the response is jsonable otherwise throws error"""
|
"""Return JSON response if the response is jsonable otherwise throws error"""
|
||||||
if self._raw_body and isinstance(self._raw_body, str):
|
if self._raw_body and isinstance(self._raw_body, (str, bytes)):
|
||||||
return TextHandler(self._raw_body).json()
|
if isinstance(self._raw_body, str):
|
||||||
|
return TextHandler(self._raw_body).json()
|
||||||
|
else:
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
assert isinstance(self._raw_body, bytes)
|
||||||
|
return TextHandler(self._raw_body.decode()).json()
|
||||||
elif self.text:
|
elif self.text:
|
||||||
return self.text.json()
|
return self.text.json()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = scrapling
|
name = scrapling
|
||||||
version = 0.3.11
|
version = 0.3.12
|
||||||
author = Karim Shoair
|
author = Karim Shoair
|
||||||
author_email = karim.shoair@pm.me
|
author_email = karim.shoair@pm.me
|
||||||
description = Scrapling is an undetectable, powerful, flexible, high-performance Python library that makes Web Scraping easy and effortless as it should be!
|
description = Scrapling is an undetectable, powerful, flexible, high-performance Python library that makes Web Scraping easy and effortless as it should be!
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ class TestDynamicFetcher:
|
|||||||
"real_chrome": True,
|
"real_chrome": True,
|
||||||
"wait": 10,
|
"wait": 10,
|
||||||
"locale": "en-US",
|
"locale": "en-US",
|
||||||
|
"timezone_id": "America/New_York",
|
||||||
"extra_headers": {"ayo": ""},
|
"extra_headers": {"ayo": ""},
|
||||||
"useragent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0",
|
"useragent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0",
|
||||||
"cookies": [{"name": "test", "value": "123", "domain": "example.com", "path": "/"}],
|
"cookies": [{"name": "test", "value": "123", "domain": "example.com", "path": "/"}],
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ envlist = pre-commit,py{310,311,312,313}
|
|||||||
usedevelop = True
|
usedevelop = True
|
||||||
changedir = tests
|
changedir = tests
|
||||||
deps =
|
deps =
|
||||||
playwright>=1.55.0
|
playwright>=1.56.0
|
||||||
patchright>=1.55.0
|
patchright>=1.56.0
|
||||||
camoufox
|
camoufox>=0.4.11
|
||||||
-r{toxinidir}/tests/requirements.txt
|
-r{toxinidir}/tests/requirements.txt
|
||||||
extras = ai,shell
|
extras = ai,shell
|
||||||
commands =
|
commands =
|
||||||
|
|||||||
Reference in New Issue
Block a user