D4Vinci/Scrapling · error · AttributeError
module {__name__!r} has no attribute {name!r}
Error message
module {__name__!r} has no attribute {name!r} What it means
Scrapling uses a lazy-import __getattr__ in its package __init__: only names listed in the internal _LAZY_IMPORTS map (Selector, Fetcher, AsyncFetcher, StealthyFetcher, DynamicFetcher) are resolved on first access. Any other attribute access on the top-level `scrapling` module falls through to this AttributeError, because the submodules (fetchers, parser, cli, core) are not imported eagerly. This is by design to keep import time low.
Source
Thrown at scrapling/__init__.py:33
"Fetcher": ("scrapling.fetchers", "Fetcher"),
"Selector": ("scrapling.parser", "Selector"),
"Selectors": ("scrapling.parser", "Selectors"),
"AttributesHandler": ("scrapling.core.custom_types", "AttributesHandler"),
"TextHandler": ("scrapling.core.custom_types", "TextHandler"),
"AsyncFetcher": ("scrapling.fetchers", "AsyncFetcher"),
"StealthyFetcher": ("scrapling.fetchers", "StealthyFetcher"),
"DynamicFetcher": ("scrapling.fetchers", "DynamicFetcher"),
}
__all__ = ["Selector", "Fetcher", "AsyncFetcher", "StealthyFetcher", "DynamicFetcher"]
def __getattr__(name: str) -> Any:
if name in _LAZY_IMPORTS:
module_path, class_name = _LAZY_IMPORTS[name]
module = __import__(module_path, fromlist=[class_name])
return getattr(module, class_name)
else:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
def __dir__() -> list[str]:
"""Support for dir() and autocomplete."""
return sorted(__all__ + ["fetchers", "parser", "cli", "core", "__author__", "__version__", "__copyright__"])
View on GitHub (pinned to 5d213a2d47)
Solutions
- Check the valid top-level names with `dir(scrapling)` or __all__: Selector, Fetcher, AsyncFetcher, StealthyFetcher, DynamicFetcher
- Import the name from its submodule, e.g. `from scrapling.parser import Selectors` or `from scrapling.fetchers import Fetcher`
- Fix typos in the attribute name (e.g. StealthyFetcher, not StealthFetcher)
- If code worked before, check the Scrapling changelog for renamed/moved public API and pin the version you tested against
Example fix
# before import scrapling fetcher = scrapling.StealthFetcher() # after from scrapling import StealthyFetcher # or scrapling.fetchers.StealthyFetcher fetcher = StealthyFetcher()
Defensive patterns
Strategy: validation
Validate before calling
import scrapling
name = 'StealthyFetcher'
if not hasattr(scrapling, name):
raise SystemExit(f'{name} is not a public top-level API; see dir(scrapling)') Type guard
from typing import Any
PUBLIC = {"Selector", "Fetcher", "AsyncFetcher", "StealthyFetcher", "DynamicFetcher"}
def is_public_scrapling_attr(name: Any) -> bool:
return isinstance(name, str) and name in PUBLIC Prevention
- Import public names explicitly (from scrapling import X) so failures happen at import time with clear errors
- Pin the scrapling version you developed against so API moves do not introduce unknown attributes
- Use dir(scrapling) when scripting against the package dynamically
When it happens
Trigger: Accessing any attribute not in _LAZY_IMPORTS/__all__, e.g. `scrapling.Selectors`, `scrapling.session`, `scrapling.tools`, or a typo like `scrapling.StealthFetcher`. It also fires for names that exist only in submodules, e.g. `scrapling.Fetch` instead of `scrapling.fetchers.Fetch`.
Common situations: Autocomplete-driven guesses (IDE suggests submodule names), copy-pasting code from older/newer Scrapling versions where public names moved, or assuming a helper like `scrapling.Selectors` (the container class) is top-level when it lives in scrapling.parser.
Related errors
AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14).
Data as JSON: /api/errors/3a03865034aaf946.
Report an issue: GitHub.