使用 Pruning 和 BM25 来适配 Markdown

Fit Markdown 是页面 Markdown 的专门过滤版本,专注于最相关的内容。默认情况下,Crawl4AI 会将整个 HTML 转换为宽泛的 raw_markdown。使用 Fit Markdown,我们会应用内容过滤算法(例如 Pruning 或 BM25)来移除或排序低价值部分(例如重复的侧边栏、浅显的文本块或不相关的内容),从而留下简洁的文本“核心”。


1.“Fit Markdown”的工作原理

1.1content_filter

CrawlerRunConfig,您可以指定content_filter决定在最终生成 Markdown 之前如何修剪或排序内容。过滤器的逻辑会在 HTML→Markdown 过程之前或过程中应用,从而产生:

  • (未过滤)
  • (过滤版或“适合”版)
  • (相应的 HTML 代码片段fit_markdown)

1.2 常用过滤器

1. PruningContentFilter – 根据文本密度、链接密度和标签重要性对每个节点进行评分,丢弃低于阈值的节点。2. BM25ContentFilter – 使用 BM25 排名关注文本相关性,尤其适用于特定的用户查询(例如“机器学习”或“食品营养”)。


2. PruningContentFilter

修剪会根据文本密度、链接密度和标签重要性丢弃不太相关的节点。这是一种基于启发式的方法——如果某些部分显得过于“单薄”或过于“垃圾”,就会被修剪掉。

2.1 使用示例

import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.content_filter_strategy import PruningContentFilter
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator

async def main():
    # Step 1: Create a pruning filter
    prune_filter = PruningContentFilter(
        # Lower → more content retained, higher → more content pruned
        threshold=0.45,           
        # "fixed" or "dynamic"
        threshold_type="dynamic",  
        # Ignore nodes with <5 words
        min_word_threshold=5      
    )

    # Step 2: Insert it into a Markdown Generator
    md_generator = DefaultMarkdownGenerator(content_filter=prune_filter)

    # Step 3: Pass it to CrawlerRunConfig
    config = CrawlerRunConfig(
        markdown_generator=md_generator
    )

    async with AsyncWebCrawler() as crawler:
        result = await crawler.arun(
            url="https://news.ycombinator.com", 
            config=config
        )

        if result.success:
            # 'fit_markdown' is your pruned content, focusing on "denser" text
            print("Raw Markdown length:", len(result.markdown.raw_markdown))
            print("Fit Markdown length:", len(result.markdown.fit_markdown))
        else:
            print("Error:", result.error_message)

if __name__ == "__main__":
    asyncio.run(main())

2.2 关键参数

  • (int):如果一个块中的单词数少于此数,则会被修剪。
  • (字符串):
  • → 每个节点必须超过threshold(0–1)。
  • → 节点评分根据标签类型、文本/链接密度等进行调整。
  • (浮点数,默认值~0.48):基准或“锚点”截止值。

算法因素:

  • 文本密度——鼓励文本与整体内容的比例更高的块。
  • 链接密度——惩罚大部分是链接的部分。
  • 标签重要性——例如<article>或者<p>可能比<div>
  • 结构上下文——如果节点嵌套很深或位于可疑的侧边栏中,则它可能会被降低优先级。

3. BM25内容过滤器

BM25 是一种经典的文本排名算法,常用于搜索引擎。如果您有用户查询或依赖页面元数据来推导查询,BM25 可以识别哪些文本块与该查询最匹配。

3.1 使用示例

import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.content_filter_strategy import BM25ContentFilter
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator

async def main():
    # 1) A BM25 filter with a user query
    bm25_filter = BM25ContentFilter(
        user_query="startup fundraising tips",
        # Adjust for stricter or looser results
        bm25_threshold=1.2  
    )

    # 2) Insert into a Markdown Generator
    md_generator = DefaultMarkdownGenerator(content_filter=bm25_filter)

    # 3) Pass to crawler config
    config = CrawlerRunConfig(
        markdown_generator=md_generator
    )

    async with AsyncWebCrawler() as crawler:
        result = await crawler.arun(
            url="https://news.ycombinator.com", 
            config=config
        )
        if result.success:
            print("Fit Markdown (BM25 query-based):")
            print(result.markdown.fit_markdown)
        else:
            print("Error:", result.error_message)

if __name__ == "__main__":
    asyncio.run(main())

3.2 参数

  • (str,可选):例如"machine learning"。如果为空,过滤器会尝试从页面元数据中收集查询。
  • (浮点数,默认 1.0):
  • 更高→块数更少但相关性更高。
  • 更低→更具包容性。
在更高级的情况下,您可能会看到类似以下参数languagecase_sensitive , 或者priority_tags改进文本的标记或加权方式。

4. 访问“Fit”输出

抓取后,您的“合适”内容位于result.markdown.fit_markdown

fit_md = result.markdown.fit_markdown
fit_html = result.markdown.fit_html

如果内容过滤器是 BM25,您可能会看到额外的逻辑或引用fit_markdown突出显示相关片段。如果是修剪,文本通常会被很好地清理,但不一定与查询匹配。


5. 代码模式回顾

5.1 修剪

prune_filter = PruningContentFilter(
    threshold=0.5,
    threshold_type="fixed",
    min_word_threshold=10
)
md_generator = DefaultMarkdownGenerator(content_filter=prune_filter)
config = CrawlerRunConfig(markdown_generator=md_generator)

5.2 BM25

bm25_filter = BM25ContentFilter(
    user_query="health benefits fruit",
    bm25_threshold=1.2
)
md_generator = DefaultMarkdownGenerator(content_filter=bm25_filter)
config = CrawlerRunConfig(markdown_generator=md_generator)

6. 结合“word_count_threshold”和排除项

请记住,您还可以指定:

config = CrawlerRunConfig(
    word_count_threshold=10,
    excluded_tags=["nav", "footer", "header"],
    exclude_external_links=True,
    markdown_generator=DefaultMarkdownGenerator(
        content_filter=PruningContentFilter(threshold=0.5)
    )
)

因此,发生多级过滤:

  1. 爬虫的excluded_tags首先从 HTML 中删除。
  2. 内容过滤器(修剪、BM25 或自定义)会修剪或排列剩余的文本块。
  3. 最终“适合”的内容是在result.markdown.fit_markdown

7.自定义过滤器

如果您需要不同的方法(例如专门的 ML 模型或特定于站点的启发式方法),您可以创建一个继承自RelevantContentFilter并实施filter_content(html).然后将其注入到你的 markdown 生成器中:

from crawl4ai.content_filter_strategy import RelevantContentFilter

class MyCustomFilter(RelevantContentFilter):
    def filter_content(self, html, min_word_threshold=None):
        # parse HTML, implement custom logic
        return [block for block in ... if ... some condition...]

步骤:

  1. 子类RelevantContentFilter
  2. 实施filter_content(...)
  3. 使用它在你的DefaultMarkdownGenerator(content_filter=MyCustomFilter(...))

8. 最后的想法

Fit Markdown 对于以下方面至关重要:

  • 摘要:从杂乱的页面中快速获取重要文本。
  • 搜索:与 BM25 结合生成与查询相关的内容。
  • AI 管道:过滤掉样板,以便基于 LLM 的提取或摘要在更密集的文本上运行。

要点: - PruningContentFilter:如果您只想要“最充实”的文本而不需要用户查询,那么它非常适合。 - BM25ContentFilter:非常适合基于查询的提取或搜索。 - 结合excluded_tagsexclude_external_linksword_count_threshold完善最终的“合适”文本。- Fit markdown 最终result.markdown.fit_markdown; 最终result.markdown.fit_markdown在未来的版本中。

借助这些工具,您可以专注于真正重要的文本,忽略垃圾内容或样板内容,并为您的 AI 或数据管道生成简洁、相关的“合适 Markdown”。祝您修剪和搜索愉快!

  • 最后更新时间:2025-01-01

> Feedback