D4Vinci/Scrapling · error · TypeError
Can't pickle Selectors object
Error message
Can't pickle Selectors object
What it means
Selectors (the list-like result container returned by css()/xpath()/find_all()) implements __getstate__ to raise TypeError on any pickling attempt. Each item wraps an lxml HtmlElement, which cannot be serialized, so copying or pickling the whole collection is blocked the same way as for a single Selector.
Source
Thrown at scrapling/parser.py:1376
@property
def first(self) -> Optional[Selector]:
"""Returns the first Selector item of the current list or `None` if the list is empty"""
return self[0] if len(self) > 0 else None
@property
def last(self) -> Optional[Selector]:
"""Returns the last Selector item of the current list or `None` if the list is empty"""
return self[-1] if len(self) > 0 else None
@property
def length(self) -> int:
"""Returns the length of the current list"""
return len(self)
def __getstate__(self) -> Any: # pragma: no cover
# lxml don't like it :)
raise TypeError("Can't pickle Selectors object")
# For backward compatibility
Adaptor = Selector
Adaptors = Selectors
View on GitHub (pinned to 5d213a2d47)
Solutions
- Convert to plain data before crossing a serialization boundary: [{'tag': s.tag, 'text': s.text, 'attrs': s.attrib} for s in results].
- Use .html strings to carry element fragments across processes and re-parse on the other side.
- Keep parsing inside one process; ship only URLs/HTML in, extracted primitives out.
Example fix
# before
results = page.css('a')
pickle.dumps(results) # TypeError
# after
results = [{'text': s.text, 'href': s.attrib.get('href')} for s in page.css('a')]
pickle.dumps(results) Defensive patterns
Strategy: validation
Validate before calling
results = page.css('a')
picklable = [s.html for s in results] # str fragments
# or full data:
picklable = [{'tag': s.tag, 'text': s.text, 'attrib': dict(s.attrib)} for s in results] Type guard
from scrapling.parser import Selectors
def is_selectors(obj) -> bool:
# true means: convert to primitives before pickling
return isinstance(obj, (Selectors, list)) and any(type(x).__name__ == 'Selector' for x in (obj if isinstance(obj, list) else list(obj))) Try / catch
try:
pickle.dumps(payload)
except TypeError as e:
if "Can't pickle Selectors" in str(e):
payload = [s.html for s in payload]
else:
raise Prevention
- Convert Selectors to str/dict immediately after querying when results must be cached or shipped.
- Keep extraction functions (Selector in -> primitives out) as the boundary for multiprocessing workers.
When it happens
Trigger: pickle.dumps(page.css('a')), copy.deepcopy(results), multiprocessing.Pool.map over functions returning Selectors, or asyncio task result pickling.
Common situations: Parallelizing scraping across processes and returning matched element lists; memoizing query results; joblib caching of extraction functions.
Related errors
AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14).
Data as JSON: /api/errors/d3d8e51f908f2a64.
Report an issue: GitHub.