Commit Graph

505 Commits

Author SHA1 Message Date
Karim shoair 0ab54852c4 fix: use more accurate naming for proxy rotation strategy
It can be considered the simplest form of round robin since we don't have weights or anything (as was originally planned), but let's change it to avoid confusion or useless debates. Here goes nothing
2026-02-14 23:19:54 +02:00
Karim shoair e785cf26ce fix(parser)!: Optimize parser for repeated operations 2026-02-14 03:34:13 +02:00
Karim shoair 2eea66dad1 style(spiders): Lower the default number of concurrent requests 2026-02-13 17:32:17 +02:00
Karim shoair a62330d2f1 fix: add typed overloads to Selectors.get() for proper default type inference
Type checkers now correctly infer the return type based on the default value:
- .get() → TextHandler | None
- .get("") → TextHandler | str
- .get(0) → TextHandler | int
2026-02-13 17:30:55 +02:00
Karim shoair 0489923b06 build: pump up the version status 2026-02-10 21:53:00 +02:00
Karim shoair 4e3829fced style: expose ProxyRotator class to the public API 2026-02-10 21:20:07 +02:00
Karim shoair 16852cb1ac style: Use shorter and more accurate naming for constants 2026-02-07 16:37:47 +02:00
Karim shoair e777342741 build: pump up version and deps 2026-02-07 16:30:32 +02:00
Karim shoair 5ec929435b style: Fix all mypy errors and add type hints to untyped function bodies
**Resolved all 65 mypy errors across 14 files and added type annotations to all previously untyped function bodies. Final result: 0 errors with --check-untyped-defs enabled, all 454 tests pass.**

`scrapling/core/_types.py`

  - Removed broken Self = object fallback — now requires typing_extensions for Python < 3.11

`scrapling/core/storage.py`

  - Fixed str/bytes mismatch in _get_hash() — used separate _identifier_bytes variable instead of reassigning from str to bytes

`scrapling/core/custom_types.py`

  - split() return type: Union[List, "TextHandlers"] → list[Any] (avoids LSP violation with parent list[str])
  - format() kwargs: **kwargs: str → **kwargs: object (matches parent str.format signature)
  - AttributesHandler.__init__: Added mapping: Any = None, **kwargs: Any and -> None
  - json_string property: Added -> bytes return type

`scrapling/core/mixins.py`

  - Changed self: "Selector" to self: Any on all mixin methods (mypy can't handle forward-reference self types on non-subclass mixins)
  - Added Dict[str, int] annotation for counter variable
  - Removed unused TYPE_CHECKING / Selector imports

`scrapling/parser.py (~30 errors)`

  - Added body: str | bytes pre-annotation for dual-type if/elif assignment
  - Used Dict[str, Any] kwargs dict for HTMLParser(...) to bypass incomplete lxml stubs missing default_doctype
  - Changed base_url=url or None → base_url=url or "" (avoids str | None vs str | bytes)
  - bool(adaptive) to guarantee bool type for __adaptive_enabled
  - Declared __text: Optional[TextHandler], __tag: Optional[str], __attributes: Optional[AttributesHandler] at top of __init__
  - cast(List, ...) for all XPath() call results (_find_all_elements, _find_all_elements_with_spaces)
  - Added Dict[float, List[Any]] for score_table, Dict[str, Any] for attributes
  - Changed score, checks = 0, 0 → score: float = 0; checks: int = 0 (two locations)
  - Renamed target → target_element in save() to avoid variable redefinition with different types
  - Wrapped node_text.clean() / .lower() in TextHandler(...) to preserve type

`scrapling/engines/_browsers/_page.py`

  - Added PageInfo[SyncPage] | PageInfo[AsyncPage] union type annotation to page_info variable

`scrapling/engines/_browsers/_validators.py`

  - Convert method_kwargs (TypedDict) to plain Dict[str, Any] before dynamic key access

`scrapling/engines/_browsers/_base.py`

  - Added _config declaration to BaseSessionMixin
  - Used cast(StealthConfig, self._config) in __generate_stealth_options to access stealth-only attributes
  - Added Tuple[str, ...] annotation for flags
  - Removed redundant narrower StealthConfig type annotation on self._config in StealthySessionMixin.__validate__
  - Widened SyncSession and AsyncSession fields (playwright, context, browser) to Any to support both playwright and patchright types
  - Added -> None to both start() methods

`scrapling/engines/_browsers/_stealth.py`

  - Added Optional, ProxyType imports
  - Annotated proxy: Optional[ProxyType] in both sync/async fetch loops
  - Annotated outer_box: Any at first declaration, removed duplicate type annotations in subsequent branches
  - Added -> None to sync and async start()
  - Added config: Any parameter type to _initialize_context
  - Removed redundant self.context: AsyncBrowserContext re-annotations in conditional branches

`scrapling/engines/_browsers/_controllers.py`

  - Added Optional, ProxyType imports
  - Annotated proxy: Optional[ProxyType] in both sync/async fetch loops
  - Added -> None to async start()
  - Removed redundant self.context: AsyncBrowserContext re-annotations

`scrapling/spiders/request.py`

  - Added Optional import, typed _fp: Optional[bytes] = None
  - Removed redundant body: bytes re-annotation

`scrapling/spiders/session.py`

  - Used separate client variable instead of reassigning session = session._client (avoids type incompatibility and fixes a bug where session._make_request was called instead of client._make_request)
  - Added -> None to SessionManager.__init__

`scrapling/engines/toolbelt/convertor.py`

  - Added list[Response] annotation for history in both sync/async methods

`scrapling/engines/static.py`

  - FetcherClient.__init__ and AsyncFetcherClient.__init__: Added **kwargs: Any and -> None

`scrapling/core/shell.py`

  - Wrapped re_sub(...) result in TextHandler(...) to maintain correct type
  - Added -> None to CurlParser.__init__
  - Added full type signature to create_wrapper, replaced wrapper.__signature__ = ... with setattr(wrapper, "__signature__", ...) to satisfy mypy
  - Added Callable to imports
2026-02-07 16:30:00 +02:00
Karim shoair 66476d42be feat(browsers): Add option to block requests to specific domains 2026-02-07 01:26:21 +02:00
Karim shoair d00c34ee7f style(parser): Improve the type hint for find_by_text and find_by_regex 2026-02-06 03:19:00 +02:00
Karim shoair 05f62fba81 fix(response): Force the body of the response to always be bytes 2026-02-06 03:14:23 +02:00
Karim shoair aa7a95fb70 feat(parser)!: Make all selection return selector objects by default
- The strings/Texthandlers are only returned by `get`/`getall`/`extract`/`extract_first`. This makes the type checking/autocompletion experience consistent.
- Removed `css_first` and `xpath_first` since it doesn't make sense to leave them now.
- Made the type hints more accurate in multiple places.
2026-02-06 02:43:51 +02:00
Karim shoair f88502718f feat(proxy control): Force a proxy at request level at any given point
And merge request's meta with response's meta
2026-02-02 14:26:52 +02:00
Karim shoair ef8c5bc7d6 feat(spiders/fetchers): Adding proxy rotation logic and change retry logic
- User passes a single proxy to the browser session, and it will keep using the same context. Pass a proxy manager that automatically refreshes the IP with each proxy, and you get speed but sacrifice some stealth.

- User imports the proxy rotator class and passes proxies to it and a rotation strategy (Round robin by default). Then pass the instance to the session class, which will create a context and a tab for each proxy returned by the rotator. This way, you sacrifice speed a bit, but you get maximum stealth since each context is created with the proxy that will be used.

- Normal requests use what you pass without issues, of course.

- All errors are retried now, and proxies are rotated on retries automatically.
2026-02-02 00:16:22 +02:00
Karim shoair 65f1a23358 fix(browsers): Improve stealth and speed by adjusting flags 2026-01-29 00:58:04 +02:00
Karim shoair 0c3d359b46 fix(browsers): disable autoplay for all browsers 2026-01-29 00:57:30 +02:00
Karim shoair 90c52c45c7 feat(parser): replacing tldextract with tld library
This might break the adaptive data users have for websites BUT:
1. tld uses ~3.7x less memory during extraction operations (1.5 MB vs 5.7 MB).
2. tld uses ~56% less memory on import (5.2 MB vs 11.9 MB).
3. Zero dependencies (vs 3 for tldextract).

In return, it's 30ms slower for extracting 5000 URLs, which is negligible. Also, the type hints aren't always accurate, but it's fine; I corrected them.
2026-01-23 00:00:48 +02:00
Karim shoair 5dbbd84b3d fix(requests): return the cached fingerprint on second call 2026-01-20 20:28:13 +02:00
Karim shoair 122923c80e feat(spiders/requests): Use a reliable method for fingerprint generation 2026-01-20 20:21:42 +02:00
Karim shoair 5aa178a76e style: update post requests type hints to be more accurate 2026-01-20 18:27:52 +02:00
Karim shoair 95955af3be build: Add w3lib to deps 2026-01-20 16:36:34 +02:00
Karim shoair 6947066761 feat(spiders): Make graceful shutdown always enabled 2026-01-19 16:40:02 +02:00
Karim shoair 25850db92c fix(spiders): handle errors with manual pause during stream mode 2026-01-19 16:13:03 +02:00
Karim shoair fbc8be5bcc fix(spiders): fix a bug with session stats counter 2026-01-19 16:00:06 +02:00
Karim shoair 4ecf8a64d3 fix(spiders): Fix for when the pausing system has large queue 2026-01-19 15:04:40 +02:00
Karim shoair 1f0f86e3a2 feat(spiders): Add pause/resume system for crawls 2026-01-19 03:43:10 +02:00
Karim shoair bf5aa021d4 fix(browsers): solving an issues with leaving playwright loop open when cdp connection fails
This has been causing issues with tests for a long time, and now finally found the reason.
2026-01-18 16:46:48 +02:00
Karim shoair 02c9dffc77 style(spiders): make parse function abstract 2026-01-18 00:01:04 +02:00
Karim shoair afe6b2e97b feat(spiders): Change items hook to make it for processing items + add a stat for this 2026-01-17 20:52:49 +02:00
Karim shoair 41c7a7e793 fix(spiders engine): Improve logging 2026-01-17 19:57:04 +02:00
Karim shoair dac854a129 fix(parser): handle responses with empty body
It makes root's value None
2026-01-13 02:35:26 +02:00
Karim shoair 35d570120a feat(spiders): Add option to logging format and date format 2026-01-12 17:11:52 +02:00
Karim shoair 3466bcab2c feat: add hook to run after collecting an item 2026-01-12 15:58:33 +02:00
Karim shoair e0f94cf7d0 feat(spiders): Add a stream mode to collect items in realtime 2026-01-12 15:53:29 +02:00
Karim shoair 927cfd85ff feat(spiders): The option to start sessions lazily 2026-01-12 02:09:50 +02:00
Karim shoair f2c6216968 style: remove unwanted import from spiders main init file 2026-01-12 01:21:26 +02:00
Karim shoair 72c3c2792a feat(spiders): add simple export system for the results 2026-01-12 01:21:05 +02:00
Karim shoair a17bb1d976 feat(browsers): Add option to retry tabs that gives errors 2026-01-11 23:40:26 +02:00
Karim shoair 5a11c2466b fix: make retry_blocked_request take the response as well 2026-01-11 22:54:17 +02:00
Karim shoair 736873a7c6 feat(spiders): Add logic to detect blocked requests and retry them 2026-01-11 21:56:57 +02:00
Karim shoair 774364b6d3 style(spiders): make the usage of uvloop optional 2026-01-11 19:57:03 +02:00
Karim shoair 0bfdab7158 feat: spiders use uvloop/winloop when available 2026-01-11 18:39:42 +02:00
Karim shoair d5b9ed11b2 fix(browsers): Close pages that give error
This was causing the browser and the whole code to stand still.
2026-01-11 18:34:13 +02:00
Karim shoair 059a708b6d feat(spiders system): a prototype of the new spiders system
- A modern spider design that uses AnyIO and asyncio, yet it's very similar to Scrapy spiders API because it's the easiest design for users, and to make it easier for new users.
- Spiders can have multiple sessions per crawl, and users decide which session to use with each request.
- A scheduler system that uses heapq logic.
- The user can set the number of concurrent requests for a spider globally or per domain.
- The user can set a download delay to control the speed of the spider more.
- There's a global function that can be overridden to handle errors for all requests. (Similar to errback in scrapy).
- There's a spider argument to set the allowed domains for the spider to stay in.
- Each spider has a very detailed crawl stats that can be accessed right away from the code after the crawl finishes. Same case with scraped items.
- The whole spider as written as any other script and you just run it. No command-line arguments, and no need to run it from the terminal through the library like other known alternatives.
- Each spider has its own logger that forces sessions to use it.
- Each spider has functions to override that run before start and after close.
- There's a spider argument to set the logging level and another one to make the spider write to a log file.

- This is only the start. A lot more features are coming in the way.
2026-01-11 16:53:18 +02:00
Karim shoair dd5c30c521 feat(response): Option to set the referer as the previous response url. 2026-01-11 03:24:30 +02:00
Karim shoair 1443a469a3 fix: handle a bug with response follow referer flow argument 2026-01-11 03:22:51 +02:00
Karim shoair aa4817f349 feat: Change logger to be flexible enough for the spiders classes 2026-01-11 03:22:20 +02:00
Karim shoair c5bc82deb8 fix(browsers): solve a bug with setting referer on the request level 2026-01-11 03:21:03 +02:00
Karim shoair 0634f5796c feat(spiders): Add follow function to the response 2026-01-10 20:39:43 +02:00