Rust HTML5解析库html5gum的使用:高效、安全的HTML5标记解析与处理

Rust HTML5解析库html5gum的使用:高效、安全的HTML5标记解析与处理

html5gum是一个符合WHATWG标准的HTML tokenizer。

基本使用示例

use std::fmt::Write;
use html5gum::{Tokenizer, Token};

let html = "<title   >hello world</title>";
let mut new_html = String::new();

for Ok(token) in Tokenizer::new(html) {
    match token {
        Token::StartTag(tag) => {
            write!(new_html, "<{}>", String::from_utf8_lossy(&tag.name)).unwrap();
        }
        Token::String(hello_world) => {
            write!(new_html, "{}", String::from_utf8_lossy(&hello_world)).unwrap();
        }
        Token::EndTag(tag) => {
            write!(new_html, "</{}>", String::from_utf8_lossy(&tag.name)).unwrap();
        }
        _ => panic!("unexpected input"),
    }
}

assert_eq!(new_html, "<title>hello world</title>");

API特性

html5gum提供了多种API:

  • 如上所示迭代tokens
  • 实现您自己的Emitter以获得最大性能
  • 基于回调的API,在便利性和性能之间取得平衡
  • 通过tree-builder功能,可以与html5ever和scraper集成

完整示例代码

use std::fmt::Write;
use html5gum::{Tokenizer, Token};

fn main() {
    // 示例HTML内容
    let html = r#"
        <!DOCTYPE html>
        <html>
            <head>
                <title>Test Page</title>
            </head>
            <body>
                <h1>Hello World</h1>
                <p>This is a test paragraph.</p>
                <div class="container">
                    <span>Nested element</span>
                </div>
            </body>
        </html>
    "#;
    
    // 创建新的字符串来存储格式化后的HTML
    let mut formatted_html = String::new();
    
    // 创建Tokenizer实例并处理每个token
    for token_result in Tokenizer::new(html) {
        match token_result {
            Ok(token) => {
                match token {
                    Token::StartTag(tag) => {
                        // 处理开始标签
                        write!(formatted_html, "<{}", String::from_utf8_lossy(&tag.name)).unwrap();
                        
                        // 处理属性
                        for attr in tag.attributes {
                            write!(
                                formatted_html, 
                                " {}=\"{}\"", 
                                String::from_utf8_lossy(&attr.name),
                                String::from_utf8_lossy(&attr.value)
                            ).unwrap();
                        }
                        
                        write!(formatted_html, ">").unwrap();
                    }
                    Token::EndTag(tag) => {
                        // 处理结束标签
                        write!(formatted_html, "</{}>", String::from_utf8_lossy(&tag.name)).unwrap();
                    }
                    Token::String(text) => {
                        // 处理文本内容
                        write!(formatted_html, "{}", String::from_utf8_lossy(&text)).unwrap();
                    }
                    Token::Comment(comment) => {
                        // 处理注释
                        write!(formatted_html, "<!--{}-->", String::from_utf8_lossy(&comment)).unwrap();
                    }
                    Token::Doctype(doctype) => {
                        // 处理DOCTYPE声明
                        write!(formatted_html, "<!DOCTYPE html>").unwrap();
                    }
                    _ => {
                        // 忽略其他类型的token
                    }
                }
            }
            Err(e) => {
                eprintln!("Error tokenizing HTML: {:?}", e);
            }
        }
    }
    
    // 输出格式化后的HTML
    println!("Formatted HTML:\n{}", formatted_html);
}

Tokenizer的功能与限制

html5gum完全实现了WHATWG HTML规范的13.2.5节,能够tokenize HTML文档并通过html5lib的tokenizer测试套件。作为tokenizer,这意味着:

  • html5gum不实现字符集检测。该实现接受并返回字节,但假定为UTF-8。它能优雅地从无效UTF-8中恢复
  • html5gum不纠正错位的标签
  • html5gum不实现DOM,在HTML规范中,DOM构造(“树构建”)会影响tokenization的方式
  • html5gum通常不符合WHATWG规范中的浏览器级HTML解析器标准

其他特性

  • 不使用unsafe Rust
  • 唯一依赖是jetscii,可以通过crate特性禁用

许可证

MIT许可证


1 回复

Rust HTML5解析库html5gum的使用:高效、安全的HTML5标记解析与处理

简介

html5gum是一个用Rust编写的HTML5解析库,专注于提供高效且符合标准的HTML5解析功能。它具有以下特点:

  • 完全符合HTML5规范
  • 高性能解析
  • 内存安全(得益于Rust的所有权系统)
  • 支持Tokenizer和Tree Builder两种解析模式
  • 良好的错误恢复能力

安装

在Cargo.toml中添加依赖:

[dependencies]
html5gum = "0.2"

基本使用方法

1. 简单Tokenizer使用

use html5gum::{Tokenizer, Token};

let html = "<div>Hello, world!</div>";
let mut tokenizer = Tokenizer::new(html).infallible();

for token in tokenizer {
    match token {
        Token::StartTag(tag) => println!("Start tag: {}", tag.name),
        Token::EndTag(tag) => println!("End tag: {}", tag.name),
        Token::Text(text) => println!("Text: {}", text),
        _ => {} // 忽略其他类型的token
    }
}

2. 带错误处理的Tokenizer

use html5gum::{Tokenizer, Token, Error};

let html = "<div>Invalid<html>";
let mut tokenizer = Tokenizer::new(html);

while let Some(result) = tokenizer.next() {
    match result {
        Ok(token) => {
            // 处理token
            println!("Token: {:?}", token);
        }
        Err(e) => {
            // 处理错误
            eprintln!("Error: {:?}", e);
        }
    }
}

3. 使用Tree Builder构建DOM树

use html5gum::{Tokenizer, TreeBuilder, DefaultTree};

let html = r#"
<!DOCTYPE html>
<html>
<head>
    <title>Test</title>
</head>
<body>
    <h1>Hello</h1>
</body>
</html>
"#;

let tree: DefaultTree = TreeBuilder::new().parse(html).unwrap();

// 遍历DOM树
for node in tree.traverse() {
    println!("Node: {:?}", node);
}

高级功能

1. 自定义状态处理

use html5gum::{Tokenizer, State, TokenizerOptions};

let html = "<div>Custom state</div>";
let options = TokenizerOptions {
    initial_state: Some(State::Data),
    ..Default::default()
};

let mut tokenizer = Tokenizer::new_with_options(html, options).infallible();

for token in tokenizer {
    println!("Token: {:?}", token);
}

2. 处理属性

use html5gum::{Tokenizer, Token};

let html = r#"<a href="https://example.com" target="_blank">Link</a>"#;
let mut tokenizer = Tokenizer::new(html).infallible();

for token in tokenizer {
    if let Token::StartTag(tag) = token {
        println!("Tag name: {}", tag.name);
        for attr in tag.attributes {
            println!("Attribute: {}='{}'", attr.name, attr.value);
        }
    }
}

3. 处理注释和DOCTYPE

use html5gum::{Tokenizer, Token};

let html = r#"<!-- comment --><!DOCTYPE html><html></html>"#;
let mut tokenizer = Tokenizer::new(html).infallible();

for token in tokenizer {
    match token {
        Token::Comment(comment) => println!("Comment: {}", comment),
        Token::Doctype(doctype) => println!("Doctype: {:?}", doctype),
        _ => {}
    }
}

性能提示

  1. 对于不需要错误处理的场景,使用infallible()可以获得更好的性能
  2. 如果只需要部分解析结果,可以考虑提前终止解析
  3. 重用Tokenizer实例可以避免重复分配内存

错误处理

html5gum提供了详细的错误信息:

use html5gum::{Tokenizer, Error};

let html = "<div><invalid";
let mut tokenizer = Tokenizer::new(html);

while let Some(result) = tokenizer.next() {
    match result {
        Ok(token) => println!("Token: {:?}", token),
        Err(Error::ParseError(e)) => {
            eprintln!("Parse error at {}:{} - {}", e.position.line, e.position.column, e.kind)
        },
        Err(e) => eprintln!("Other error: {:?}", e),
    }
}

完整示例

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

use html5gum::{Tokenizer, Token, Error, TreeBuilder, DefaultTree};

fn main() {
    // 示例1: 简单Tokenizer使用
    println!("=== 简单Tokenizer示例 ===");
    let html_simple = "<div class='container'><p>Hello</p><p>World</p></div>";
    let mut tokenizer = Tokenizer::new(html_simple).infallible();
    
    for token in tokenizer {
        match token {
            Token::StartTag(tag) => println!("发现开始标签: {}", tag.name),
            Token::EndTag(tag) => println!("发现结束标签: {}", tag.name),
            Token::Text(text) => println!("发现文本内容: {}", text),
            _ => {}
        }
    }

    // 示例2: 带错误处理的解析
    println!("\n=== 带错误处理的示例 ===");
    let html_with_errors = "<div><p>Hello</invalid>";
    let mut tokenizer_with_errors = Tokenizer::new(html_with_errors);
    
    while let Some(result) = tokenizer_with_errors.next() {
        match result {
            Ok(token) => println!("解析到的Token: {:?}", token),
            Err(Error::ParseError(e)) => {
                eprintln!("解析错误: 行 {} 列 {} - {}", e.position.line, e.position.column, e.kind)
            },
            Err(e) => eprintln!("其他错误: {:?}", e),
        }
    }

    // 示例3: 构建DOM树
    println!("\n=== DOM树构建示例 ===");
    let html_dom = r#"
    <!DOCTYPE html>
    <html>
    <head>
        <title>示例页面</title>
    </head>
    <body>
        <h1>标题</h1>
        <p>段落内容</p>
    </body>
    </html>
    "#;

    let tree: DefaultTree = TreeBuilder::new().parse(html_dom).unwrap();
    
    println!("DOM树节点数: {}", tree.nodes().count());
    for node in tree.traverse() {
        println!("节点: {:?}", node);
    }
}

总结

html5gum是一个功能强大且符合标准的HTML5解析库,适合需要高效、安全解析HTML的场景。无论是简单的文本提取还是完整的DOM树构建,html5gum都能提供良好的支持。

回到顶部