Commit Graph

562 Commits

Author SHA1 Message Date
Karim shoair afaf68e7d5 fix(spider robots): removing dead code 2026-04-05 02:32:33 +02:00
Karim shoair ea2dd7866b refactor(spiders): Make Robots.txt compliance turned off by default
Scrapy is turning it off by default
2026-04-05 01:55:17 +02:00
Karim shoair 556a90f645 refactor(spider): prefetch robots.txt from start_urls only
Stop using allowed_domains for robots.txt prefetch since bare domain strings have no scheme info.
Domains discovered mid-crawl via requests still fetch robots.txt lazily.
2026-04-05 01:43:06 +02:00
Karim shoair ec487d37e8 fix(spider): Make delay in robots file don't affect user's concurrency settings 2026-04-05 01:39:05 +02:00
Karim shoair af83a11aa7 fix(spider robots): solve multiple issues with cache prefetch 2026-04-05 00:09:17 +02:00
Karim shoair 854daac794 style(spiders robots feat): Adjustments for maintainability 2026-04-04 21:06:35 +02:00
Abdullah a134fdb8cc feat(spiders): enable robots.txt compliance by default
robots_txt_obey now defaults to True. Spiders must explicitly opt out
with robots_txt_obey = False rather than opt in, making ethical
crawling the default behaviour.

File: scrapling/spiders/spider.py
2026-04-04 03:10:17 +02:00
Abdullah a86e9709ea feat(spiders): pre-warm robots.txt cache before crawl loop starts
Previously robots.txt was fetched lazily on the first request per
domain, causing early concurrent requests to each stall waiting for
the same network fetch. The cache is now warmed before the crawl loop
starts, making all subsequent robots.txt lookups a local read.

- RobotsTxtManager gains a prefetch(urls, sid) method that fetches all domains concurrently via a task group
- CrawlerEngine._prefetch_robots_txt() is called after on_start():
  uses allowed_domains if configured, otherwise falls back to unique
  domains extracted from start_urls
- Mid-crawl domain discovery (not covered by prefetch) still fetches
  lazily; two concurrent callbacks on the same new domain can each
  trigger a fetch — accepted tradeoff, documented in _get_domain_delay

Files: scrapling/spiders/robotstxt.py, scrapling/spiders/engine.py, tests/spiders/test_engine.py
2026-04-04 03:00:15 +02:00
Abdullah e2b293f41c refactor(spiders): simplify robots.txt cache to domain-only key
robots.txt is a domain-level document and does not vary by session.

Keying the cache by (domain, sid) was both wasteful and incorrect —
it caused redundant fetches when the same domain was accessed by different sessions.

- Cache is now keyed by domain string only; all sessions share one entry
- Removed asyncio.Event inflight-deduplication mechanism (superseded by the prefetch approach added in the next commit)
- clear_cache() loses the `sid` parameter (breaking change); clearing a domain now evicts the single shared entry for all sessions
- Updated tests to reflect shared-cache semantics

Files: scrapling/spiders/robotstxt.py, tests/spiders/test_robotstxt.py
2026-04-04 03:00:15 +02:00
Abdullah 5c40c6a853 feat(spiders): integrate robots.txt compliance into the crawl engine 2026-04-03 15:08:33 +02:00
Abdullah 0bbe62fc7f feat(spiders): implement RobotsTxtManager with concurrent fetch deduplication 2026-04-03 15:08:33 +02:00
Abdullah 1d15349e07 feat(deps): add protego for robots.txt parsing and fix pyright type error in static.py 2026-04-03 15:08:33 +02:00
yetval 6c6aabeb73 fix: proxy rotation page pool leak 2026-04-02 11:57:17 -04:00
Karim shoair 966e17a1dc build: pump version up 2026-04-01 17:38:54 +02:00
Karim shoair f614651a97 fix(Proxy Rotation): Fix an MRO issue
The stub shadows the real implementation, and proxy rotation always hits NotImplementedError.

Possible fix for #215

Co-Authored-By: Yuval Dinodia <102706514+yetval@users.noreply.github.com>
2026-04-01 17:36:35 +02:00
Karim shoair a403156b2e docs: style adjustment 2026-03-30 03:53:56 +02:00
Karim shoair 8a4c5ffa3b feat(browsers): Add a new option to set browser path
Solves #202
2026-03-30 03:06:58 +02:00
Karim shoair 4efbffa1dc feat(cli): Add an option to make content safe/targets AI 2026-03-30 02:44:14 +02:00
Karim shoair 375951bd49 feat(mcp): Protect from Prompt Injection by removing hidden content
Solves #214 as well
2026-03-30 02:31:14 +02:00
Karim shoair 61cda587be docs: update pages with the XHR feature 2026-03-29 23:12:56 +02:00
Karim shoair 5c450a3b52 fix: improve type hints for the static checkers 2026-03-29 23:01:02 +02:00
Karim shoair 68f7c5c36f feat(browser sessions): Collect XHR requests done while loading the page
Solves #159
2026-03-29 22:53:54 +02:00
Karim shoair 0f6dcccf5f fix(mcp): remove unneeded code and fix type hint for mypy 2026-03-28 17:19:42 +02:00
Karim shoair c458ab65a2 feat(mcp): Add three new tools to control browser sessions
Now you can open a browser, keep using it for other requests as you want, and close it when you want.
2026-03-28 00:16:14 +02:00
Karim shoair a356dd2f2b refactor(mcp)!: Cleaning and unifying functions to async
- `get()` now delegates to `bulk_get([url])[0]` (was a separate sync implementation)
- `fetch()` now delegates to `bulk_fetch([url])[0]` (eliminated duplicate fetcher call)
- `stealthy_fetch()` now delegates to `bulk_stealthy_fetch([url])[0]` (same)
- Replaced 6x repeated `_content_translator(Convertor._extract_content(...), page)` with a single `_translate_response()` helper
- Removed unused imports (`Fetcher`, `DynamicFetcher`, `StealthyFetcher`, `Generator`)
2026-03-27 19:30:58 +02:00
Karim shoair 8e147db7f8 refactor(cli): Code cleaning for easier maintenance/adding new features
Shortened the code by 210 lines. Also, removed docstrings because they are not needed for CLI commands (more maintenance burden).

- `_common_http_options`: shared decorator for 10 Click options used by get/post/put/delete (was repeated 4x)
- `_common_browser_options`: shared decorator for 11 Click options used by fetch/stealthy_fetch (was repeated 2x)
- `_data_options`: shared decorator for `--data`/`--json` options used by post/put
- `__http_command()`: shared implementation body for all HTTP commands (was 4 separate `from scrapling.fetchers import Fetcher` + `__Request_and_Save` blocks)
- `__build_browser_kwargs()`: shared kwargs builder for fetch/stealthy_fetch (was duplicated)
2026-03-27 18:02:33 +02:00
Karim shoair 422b4713ec build: pump up version 2026-03-25 00:19:58 +02:00
Karim shoair 1dc0b7a1bd fix(fetchers/content): increase the default max number of retries and raise error on max retries
Ref.: https://github.com/D4Vinci/Scrapling/pull/197#issuecomment-4077705587
2026-03-17 22:14:02 +02:00
Karim shoair 8e4e59e13e Merge branch 'dev' into fix/page-content-infinite-loop 2026-03-17 22:08:47 +02:00
Karim shoair 4c07b294ae Merge branch 'dev' into fix/preserve-http-method-on-retry 2026-03-17 22:04:57 +02:00
Karim shoair 27f306259e Merge branch 'dev' into fix/page-content-infinite-loop 2026-03-17 21:55:26 +02:00
Karim shoair 136c389787 fix(Texthandler): Replace get_all with getall to match the Selector class 2026-03-17 21:53:17 +02:00
karesansui 5bf921b308 fix: preserve HTTP method across retries in spider session
SessionManager.fetch() pops `method` from `_session_kwargs`,
which mutates the original request dict. When the engine retries
a blocked request via request.copy(), the copy no longer has
`method`, so it defaults to GET.

Steps to reproduce:
1. Yield Request(url, method="POST", data=...)
2. Target returns a response that triggers is_blocked()
3. Engine retries via request.copy() → second fetch uses GET

Fix: copy the kwargs dict before popping, so the original
request stays intact.
2026-03-17 00:53:52 +09:00
haosenwang1018 d3c251c1ab fix: add max retry limit to _get_page_content to prevent infinite loop
Both _get_page_content and _get_async_page_content use a while-True
loop that retries page.content() on PlaywrightError with no upper
bound. If the page is in a permanently broken state (crashed tab,
closed context), this loops forever and hangs the process.

Replace with a bounded for-loop (default 10 retries = 5s), returning
an empty string if all attempts fail. This preserves the existing
retry behavior for the transient Windows issue (playwright#16108)
while preventing hangs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:53:23 +08:00
haosenwang1018 a31763afde fix: replace bare raise with return False in _restore_from_checkpoint
When _checkpoint_system_enabled is False, the method uses a bare
`raise` with no active exception, which causes RuntimeError at
runtime. The method's docstring says it returns False when restoration
is not possible, so return False is the correct behavior.

The caller in crawl() currently guards with `if
self._checkpoint_system_enabled`, but the method's own contract
should be self-consistent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:52:57 +08:00
Karim shoair f80743a97b style: remove unused import 2026-03-09 00:35:06 +02:00
Karim shoair a929de6ca4 fix: update code and docstrings to remove the old google referer logic 2026-03-09 00:25:19 +02:00
Karim shoair 27409927a3 build: pump up deps and browsers versions 2026-03-08 23:57:10 +02:00
Karim shoair b0c8448f81 Merge branch 'dev' into fix/google-referrer-spoof 2026-03-08 19:01:20 +02:00
Andrew Barnes 68b9e128bc fix: return bare Google URL in referer instead of search query
Real browsers send `https://www.google.com/` as the Referer header
when clicking search results, not the full search URL with query
parameters. The previous format was a fingerprinting signal that
the referer was spoofed.

Closes #172
2026-03-08 11:32:04 -04:00
Matt Hillebrand 5806b8e014 - Add pre-compiled XPath text selector
- Avoid recursion
2026-03-07 17:11:39 -08:00
Matt Hillebrand 5457a697fa fix: Selector.get_all_text() doesn't get all text #167
- Updated text extraction using recursion
- Added new unit test
2026-03-07 17:11:39 -08:00
Karim shoair c8e1d8d75d fix(type hints): use correct import for Python < 3.12 (Fixes #163 ) 2026-03-05 16:45:10 +02:00
Karim shoair 37f5c48a5f build: pump up the version
Co-Authored-By: Christian Rost <chr@baltic-online.de>
2026-03-03 23:09:36 +02:00
Karim shoair 1839506622 fix(browsers): make flag concatenation type-safe
Better implementation of #157 that's accepted by pyright/mypy.
Thanks to @rostchri .

Co-Authored-By: Christian Rost <chr@baltic-online.de>
2026-03-03 23:08:15 +02:00
Karim shoair e34651d887 fix(stealth): improve stealth mode by removing unnecessary scripts
It doesn't add anything to the table as before (it might turn out I was wrong and add them again lol)
2026-02-27 03:50:14 +02:00
Karim shoair b50e8f050f fix(mcp): make mcp use less tokens by striping useless tags 2026-02-27 03:39:48 +02:00
Karim shoair d750fec468 fix(solver): Solve CF faster and handle websites that show captcha twice 2026-02-27 02:32:21 +02:00
Karim shoair a8359a4878 build: pump version up and the deps 2026-02-27 00:32:25 +02:00
Karim shoair 921314c314 fix: complete MCP schema validation for remaining bare array types
- Fix params (Dict|List|Tuple → Dict), urls (Tuple[str,...] → List[str]),
- Renamed _NormalizeCredentials/_ContentTranslator to snake_case.
- Also, raise a ValueError on invalid credentials instead of returning None silently.
2026-02-27 00:28:44 +02:00