Repository navigation
[Bug]: PruningContentFilter cuts inline spans out of sentences it keeps (e.g. "renamed Tokyo (lit. '')") #2417
Description
crawl4ai version
0.9.4 (reproduced). PruningContentFilter (crawl4ai/content_filter_strategy.py) is unchanged in v0.9.5 and on main (checked 2026-10-19).
Expected Behavior
When PruningContentFilter keeps a paragraph, the words inside that paragraph stay. A filter that removes a whole block is expected; a kept sentence with words missing from its middle is not.
Current Behavior
A <span> whose content is another element, such as a span around a link or a span around a span, is removed from a paragraph that is otherwise kept. Wikipedia marks up glosses and native-script names this way, so on https://en.wikipedia.org/wiki/Tokyo fit_markdown reads:
the city was renamed Tokyo (lit. '')
The source HTML is (<abbr>lit.</abbr> ... <span class="gloss-text"><a href="...">Eastern Capital</a></span> ...).
In the snippet below, plain spans, <strong>, <em> and links placed directly in the paragraph are kept; only the nested spans are lost.
My reading of the cause: _prune_tree scores every descendant element as if it were a block, and the score includes text density (text length over markup length) and link density. A short span whose only content is another element has little text against its markup, because the inner element's tags count as markup too. When the inner element is a link, the link density is also 1. Either way the span scores under the threshold and is removed.
Is this reproducible?
Yes
Inputs Causing the Bug
HTML with short nested inline spans inside a paragraph, for example https://en.wikipedia.org/wiki/Tokyo.
Steps to Reproduce
- Run the snippet below.
- Compare the
defaultoutput with the paragraphs inHTML.
Code snippets
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode, DefaultMarkdownGenerator, PruningContentFilter
HTML = """<html><body><article>
<p>After the <a href="/wiki/Meiji_Restoration">Meiji Restoration</a> (1868), the imperial capital in <a href="/wiki/Kyoto">Kyoto</a> was moved to Edo, and the city was renamed Tokyo (<abbr title="literal translation">lit.</abbr> <span class="gloss-quot">'</span><span class="gloss-text"><a href="/wiki/Capital_of_Japan">Eastern Capital</a></span><span class="gloss-quot">'</span>). The city was greatly damaged by the 1923 earthquake and rebuilt in the following decades.</p>
<p>Tokyo (<span title="Japanese-language text"><span lang="ja">東京</span></span>) is the capital of Japan and one of the most populous cities in the world, with a population of about fourteen million.</p>
<p>The museum near the harbour holds <span>old maps</span> of the bay, <strong>ship models</strong> and <em>letters</em> written by the first merchants who settled on the northern quay.</p>
</article></body></html>"""
async def main():
async with AsyncWebCrawler() as crawler:
for name, flt in [("default", PruningContentFilter()), ("preserve_tags=['span']", PruningContentFilter(preserve_tags=["span"]))]:
config = CrawlerRunConfig(cache_mode=CacheMode.BYPASS, markdown_generator=DefaultMarkdownGenerator(content_filter=flt))
result = await crawler.arun("raw:" + HTML, config=config)
print(f"--- {name}")
print(result.markdown.fit_markdown)
asyncio.run(main())Output on 0.9.4:
--- default
After the [Meiji Restoration](/wiki/Meiji_Restoration) (1868), the imperial capital in [Kyoto](/wiki/Kyoto) was moved to Edo, and the city was renamed Tokyo (lit. ''). The city was greatly damaged by the 1923 earthquake and rebuilt in the following decades.
Tokyo () is the capital of Japan and one of the most populous cities in the world, with a population of about fourteen million.
The museum near the harbour holds old maps of the bay, **ship models** and _letters_ written by the first merchants who settled on the northern quay.
*[lit.]: literal translation
--- preserve_tags=['span']
After the [Meiji Restoration](/wiki/Meiji_Restoration) (1868), the imperial capital in [Kyoto](/wiki/Kyoto) was moved to Edo, and the city was renamed Tokyo (lit. '[Eastern Capital](/wiki/Capital_of_Japan)'). The city was greatly damaged by the 1923 earthquake and rebuilt in the following decades.
Tokyo (東京) is the capital of Japan and one of the most populous cities in the world, with a population of about fourteen million.
The museum near the harbour holds old maps of the bay, **ship models** and _letters_ written by the first merchants who settled on the northern quay.
*[lit.]: literal translation
Workaround and suggestion
preserve_tags=["span"] fixes it: a preserved element is kept whole and not scored. On the Tokyo page it grows fit_markdown by about 2% (139,263 to 141,985 characters), so it does not let noticeably more noise through. Lowering the threshold, the workaround discussed in #582, applies to the whole page instead.
Since a filter that keeps a block should keep the block's words, a possible fix is to stop scoring inline (phrasing) elements on their own and let them follow their nearest block ancestor. Making span part of the default preserve_tags would cover the cases above with a smaller change.
#582 reported a similar symptom for <a> and <strong> and was closed after tuning the threshold. This report is narrower: the kept sentence itself is left with holes.
OS
Debian GNU/Linux 12 (bookworm), in the official unclecode/crawl4ai:0.9.4 Docker image
Python version
3.12.14
Error logs & Screenshots (if applicable)
None.
Activity
siva23367 commentedon Oct 19, 2026
I would like to fix this issue, please assign it to me @siosig