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

  1. Check the valid top-level names with `dir(scrapling)` or __all__: Selector, Fetcher, AsyncFetcher, StealthyFetcher, DynamicFetcher
  2. Import the name from its submodule, e.g. `from scrapling.parser import Selectors` or `from scrapling.fetchers import Fetcher`
  3. Fix typos in the attribute name (e.g. StealthyFetcher, not StealthFetcher)
  4. 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

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.