diff --git a/crawl4ai/html2text/__init__.py b/crawl4ai/html2text/__init__.py index ff39fb211..7184c7da0 100644 --- a/crawl4ai/html2text/__init__.py +++ b/crawl4ai/html2text/__init__.py @@ -1119,17 +1119,17 @@ def handle_tag(self, tag, attrs, start): if self.preserve_depth == 0: self.current_preserved_tag = tag self.preserved_content = [] - # Format opening tag with attributes - attr_str = "".join( - f' {k}="{v}"' for k, v in attrs.items() if v is not None - ) - self.preserved_content.append(f"<{tag}{attr_str}>") + # Record nested preserved tags as well as the outer block. + attr_str = "".join( + f' {k}="{v}"' for k, v in attrs.items() if v is not None + ) + self.preserved_content.append(f"<{tag}{attr_str}>") self.preserve_depth += 1 return else: + self.preserved_content.append(f"") self.preserve_depth -= 1 if self.preserve_depth == 0: - self.preserved_content.append(f"") # Output the preserved HTML block with proper spacing preserved_html = "".join(self.preserved_content) self.o("\n" + preserved_html + "\n") diff --git a/tests/unit/test_markdown_preserved_tags.py b/tests/unit/test_markdown_preserved_tags.py new file mode 100644 index 000000000..6bff13cf3 --- /dev/null +++ b/tests/unit/test_markdown_preserved_tags.py @@ -0,0 +1,46 @@ +"""Regression tests for nested tags preserved during Markdown generation.""" + +import pytest + +from crawl4ai import DefaultMarkdownGenerator + + +@pytest.mark.parametrize( + ("html", "preserve_tags"), + [ + ( + '
outer
inner
end
', + ["div"], + ), + ( + '
outer
inner
end
', + ["section", "div"], + ), + ( + "
outer
inner
" + "end
", + ["table"], + ), + ( + '
' + "content
", + ["div"], + ), + ( + '
outerinnerend
', + ["div"], + ), + ( + '
one
two
', + ["div"], + ), + ], + ids=["same-tag", "mixed-tags", "nested-table", "three-levels", "child", "siblings"], +) +def test_markdown_preserves_nested_html_tags(html, preserve_tags): + generator = DefaultMarkdownGenerator(options={"preserve_tags": preserve_tags}) + + result = generator.generate_markdown(html, citations=False) + + # Preserved blocks gain separating newlines, but retain their HTML structure. + assert result.raw_markdown.strip().replace("\n", "") == html