Rust HTML解析库swc_html_ast的使用,高效处理HTML文档结构与AST转换

Rust HTML解析库swc_html_ast的使用,高效处理HTML文档结构与AST转换

安装

在你的项目目录中运行以下Cargo命令:

cargo add swc_html_ast

或者在你的Cargo.toml中添加以下行:

swc_html_ast = "14.0.0"

基本使用示例

以下是使用swc_html_ast解析HTML文档并转换为AST的基本示例:

use swc_html_ast::{Document, DocumentFragment, Element, Node, Text};
use swc_html_parser::{parse_document, parse_fragment};

fn main() {
    // 示例1:解析完整的HTML文档
    let html_doc = r#"<!DOCTYPE html>
    <html>
    <head>
        <title>示例文档</title>
    </head>
    <body>
        <h1>Hello World</h1>
        <p>这是一个示例段落</p>
    </body>
    </html>"#;
    
    let document: Document = parse_document(html_doc, Default::default()).unwrap();
    
    // 遍历文档节点
    for node in document.children {
        match node {
            Node::Element(element) => {
                println!("发现元素: {}", element.tag_name);
                // 处理元素...
            }
            Node::Text(text) => {
                println!("发现文本: {}", text.value);
                // 处理文本...
            }
            _ => {}
        }
    }
    
    // 示例2:解析HTML片段
    let html_fragment = r#"<div class="container">
        <span>片段内容</span>
    </div>"#;
    
    let fragment: DocumentFragment = parse_fragment(html_fragment, Default::default()).unwrap();
    
    // 处理片段节点
    process_nodes(&fragment.children);
}

fn process_nodes(nodes: &[Node]) {
    for node in nodes {
        match node {
            Node::Element(element) => {
                println!("元素: {} 有 {} 个属性", 
                    element.tag_name, 
                    element.attributes.len());
                
                // 递归处理子节点
                process_nodes(&element.children);
            }
            Node::Text(text) => {
                println!("文本内容: {}", text.value.trim());
            }
            _ => {}
        }
    }
}

完整示例demo

以下是一个更完整的示例,展示了如何解析HTML并提取特定信息:

use swc_html_ast::{Document, DocumentFragment, Element, Node, Text};
use swc_html_parser::{parse_document, parse_fragment};

fn main() {
    // 示例:解析HTML并提取所有链接
    let html_content = r#"
    <html>
    <body>
        <div class="content">
            <h1>网页标题</h1>
            <p>这是一个包含<a href="https://example.com">示例链接</a>的段落</p>
            <ul>
                <li><a href="/about">关于我们</a></li>
                <li><a href="/contact" class="external">联系我们</a></li>
            </ul>
        </div>
    </body>
    </html>
    "#;

    let document: Document = parse_document(html_content, Default::default()).unwrap();
    
    println!("开始提取所有链接:");
    extract_links(&document.children);
}

// 递归提取所有<a>标签的href属性
fn extract_links(nodes: &[Node]) {
    for node in nodes {
        match node {
            Node::Element(element) => {
                if element.tag_name == "a" {
                    // 查找href属性
                    if let Some(href) = element.attributes.iter().find(|attr| attr.name == "href") {
                        println!("找到链接: {} - 文本: {}", 
                            href.value,
                            extract_text(&element.children)
                        );
                    }
                }
                // 递归处理子元素
                extract_links(&element.children);
            }
            _ => {}
        }
    }
}

// 提取元素内的文本内容
fn extract_text(nodes: &[Node]) -> String {
    let mut text = String::new();
    for node in nodes {
        match node {
            Node::Text(text_node) => {
                text.push_str(&text_node.value);
            }
            Node::Element(element) => {
                text.push_str(&extract_text(&element.children));
            }
            _ => {}
        }
    }
    text.trim().to_string()
}

特点

  1. 高效解析:swc_html_ast提供了快速的HTML解析能力
  2. 完整的AST支持:可以表示HTML文档中的所有节点类型
  3. 灵活的API:支持完整文档和片段解析
  4. 与SWC生态系统集成:是SWC工具链的一部分

注意事项

  1. 使用时需要处理可能的解析错误
  2. 对于大型HTML文档,考虑性能优化
  3. AST遍历是递归的,对于深度嵌套的文档要注意栈大小

1 回复

Rust HTML解析库swc_html_ast的使用指南

介绍

swc_html_ast是SWC生态系统中的一个HTML解析库,它能够高效地将HTML文档解析为抽象语法树(AST),便于开发者对HTML文档进行结构化处理和分析。这个库特别适合需要处理HTML文档结构、进行语法转换或构建HTML处理工具的场景。

主要特性

  • 高性能的HTML解析器
  • 完整的AST节点类型表示
  • 支持HTML5规范
  • 易于遍历和转换的AST结构
  • 与SWC工具链良好集成

基本使用方法

添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
swc_html_ast = "0.7"
swc_html_parser = "0.7"

解析HTML文档

use swc_html_parser::parser::ParserConfig;
use swc_html_ast::{Document, DocumentFragment};
use swc_html_parser::{lexer::Lexer, parser::Parser};

fn parse_html(html: &str) -> Document {
    let lexer = Lexer::new(html);
    let mut parser = Parser::new(lexer, ParserConfig::default());
    
    parser.parse_document()
}

遍历AST节点

use swc_html_ast::*;
use swc_html_visit::{Visit, VisitWith};

struct ElementCounter {
    count: usize,
}

impl Visit for ElementCounter {
    fn visit_element(&mut self, element: &Element) {
        self.count += 1;
        element.visit_children_with(self);
    }
}

fn count_elements(document: &Document) -> usize {
    let mut counter = ElementCounter { count: 0 };
    document.visit_with(&mut counter);
    counter.count
}

修改AST节点

use swc_html_ast::*;
use swc_html_visit::{VisitMut, VisitMutWith};

struct ClassAdder;

impl VisitMut for ClassAdder {
    fn visit_mut_element(&mut self, element: &mut Element) {
        if element.tag_name == "div" {
            element.attributes.push(Attribute {
                name: "class".into(),
                value: Some("container".into()),
                ..Default::default()
            });
        }
        element.visit_mut_children_with(self);
    }
}

fn add_classes(document: &mut Document) {
    let mut visitor = ClassAdder;
    document.visit_mut_with(&mut visitor);
}

生成HTML字符串

use swc_html_codegen::{
    writer::basic::{BasicHtmlWriter, BasicHtmlWriterConfig},
    CodeGenerator, CodegenConfig, Emit,
};

fn generate_html(document: &Document) -> String {
    let mut buf = String::new();
    let writer = BasicHtmlWriter::new(&mut buf, None, BasicHtmlWriterConfig::default());
    let mut gen = CodeGenerator::new(
        writer,
        CodegenConfig {
            minify: false,
            ..Default::default()
        },
    );
    
    gen.emit(document).unwrap();
    buf
}

高级用法示例

提取所有链接

use swc_html_ast::*;
use swc_html_visit::{Visit, VisitWith};

struct LinkExtractor {
    links: Vec<String>,
}

impl Visit for LinkExtractor {
    fn visit_element(&mut self, element: &Element) {
        if element.tag_name == "a" {
            if let Some(href) = element.attributes.iter().find(|attr| attr.name == "href") {
                if let Some(value) = &href.value {
                    self.links.push(value.to_string());
                }
            }
        }
        element.visit_children_with(self);
    }
}

fn extract_links(document: &Document) ->

Vec<String> {
    let mut extractor = LinkExtractor { links: Vec::new() };
    document.visit_with(&mut extractor);
    extractor.links
}

HTML文档转换

use swc_html_ast::*;
use swc_html_visit::{VisitMut, VisitMutWith};

struct DivToSectionConverter;

impl VisitMut for DivToSectionConverter {
    fn visit_mut_element(&mut self, element: &mut Element) {
        if element.tag_name == "div" {
            element.tag_name = "section".into();
        }
        element.visit_mut_children_with(self);
    }
}

fn convert_divs_to_sections(document: &mut Document) {
    let mut converter = DivToSectionConverter;
    document.visit_mut_with(&mut converter);
}

性能提示

  1. 对于大型HTML文档,考虑使用swc_html_parserParserConfig调整解析行为
  2. 批量处理AST修改比多次遍历更高效
  3. 如果只需要部分信息,可以实现定制的访问者来避免完整遍历

swc_html_ast提供了强大而灵活的工具来处理HTML文档,无论是简单的分析还是复杂的转换任务都能胜任。


完整示例demo

下面是一个结合了上述功能的完整示例,展示如何解析HTML、修改节点、提取信息并重新生成HTML:

use swc_html_ast::*;
use swc_html_parser::{lexer::Lexer, parser::{Parser, ParserConfig}};
use swc_html_visit::{Visit, VisitMut, VisitWith, VisitMutWith};
use swc_html_codegen::{CodeGenerator, CodegenConfig, Emit, writer::basic::{BasicHtmlWriter, BasicHtmlWriterConfig}};

fn main() {
    // 示例HTML文档
    let html = r#"
    <!DOCTYPE html>
    <html>
    <head><title>Test Page</title></head>
    <body>
        <div>Content 1</div>
        <div>Content 2</div>
        <a href="https://example.com">Link</a>
    </body>
    </html>
    "#;

    // 1. 解析HTML文档
    let document = parse_html(html);
    println!("解析完成,文档结构已构建");

    // 2. 统计元素数量
    let count = count_elements(&document);
    println!("文档中共有{}个元素", count);

    // 3. 修改文档 - 添加类名
    let mut document = document;
    add_classes(&mut document);
    println!("已为所有div元素添加class");

    // 4. 提取链接
    let links = extract_links(&document);
    println!("提取到的链接: {:?}", links);

    // 5. 转换文档 - div转section
    convert_divs_to_sections(&mut document);
    println!("已将div元素转换为section");

    // 6. 重新生成HTML
    let new_html = generate_html(&document);
    println!("重新生成的HTML:\n{}", new_html);
}

// 以下是与上面相同的函数实现
fn parse_html(html: &str) -> Document {
    let lexer = Lexer::new(html);
    let mut parser = Parser::new(lexer, ParserConfig::default());
    parser.parse_document()
}

fn count_elements(document: &Document) -> usize {
    struct ElementCounter { count: usize }
    impl Visit for ElementCounter {
        fn visit_element(&mut self, _: &Element) {
            self.count += 1;
        }
    }
    let mut counter = ElementCounter { count: 0 };
    document.visit_with(&mut counter);
    counter.count
}

fn add_classes(document: &mut Document) {
    struct ClassAdder;
    impl VisitMut for ClassAdder {
        fn visit_mut_element(&mut self, element: &mut Element) {
            if element.tag_name == "div" {
                element.attributes.push(Attribute {
                    name: "class".into(),
                    value: Some("container".into()),
                    ..Default::default()
                });
            }
            element.visit_mut_children_with(self);
        }
    }
    let mut visitor = ClassAdder;
    document.visit_mut_with(&mut visitor);
}

fn extract_links(document: &Document) -> Vec<String> {
    struct LinkExtractor { links: Vec<String> }
    impl Visit for LinkExtractor {
        fn visit_element(&mut self, element: &Element) {
            if element.tag_name == "a" {
                if let Some(href) = element.attributes.iter().find(|attr| attr.name == "href") {
                    if let Some(value) = &href.value {
                        self.links.push(value.to_string());
                    }
                }
            }
            element.visit_children_with(self);
        }
    }
    let mut extractor = LinkExtractor { links: Vec::new() };
    document.visit_with(&mut extractor);
    extractor.links
}

fn convert_divs_to_sections(document: &mut Document) {
    struct DivToSectionConverter;
    impl VisitMut for DivToSectionConverter {
        fn visit_mut_element(&mut self, element: &mut Element) {
            if element.tag_name == "div" {
                element.tag_name = "section".into();
            }
            element.visit_mut_children_with(self);
        }
    }
    let mut converter = DivToSectionConverter;
    document.visit_mut_with(&mut converter);
}

fn generate_html(document: &Document) -> String {
    let mut buf = String::new();
    let writer = BasicHtmlWriter::new(&mut buf, None, BasicHtmlWriterConfig::default());
    let mut gen = CodeGenerator::new(writer, CodegenConfig::default());
    gen.emit(document).unwrap();
    buf
}

这个完整示例展示了swc_html_ast库的主要功能,包括:

  1. 解析HTML文档为AST
  2. 遍历和统计元素数量
  3. 修改AST节点(添加类名)
  4. 提取特定信息(链接)
  5. 转换文档结构(div转section)
  6. 重新生成HTML字符串

您可以根据实际需求调整或组合这些功能来构建自己的HTML处理工具。

回到顶部