Rust HTML解析库tree-sitter-html的使用,高效语法分析与DOM树构建工具
tree-sitter-html
用于tree-sitter的HTML语法。
参考
- HTML5规范
安装命令:
cargo add tree-sitter-html
或在Cargo.toml中添加:
tree-sitter-html = "0.23.2"
完整示例代码:
use tree_sitter::Parser;
use tree_sitter_html::language;
fn main() {
// 创建解析器
let mut parser = Parser::new();
// 设置HTML语言
parser.set_language(language()).expect("Error loading HTML grammar");
// HTML源代码
let source_code = r#"
<!DOCTYPE html>
<html>
<head>
<title>示例页面</title>
</head>
<body>
<h1>欢迎使用tree-sitter-html</h1>
<p>这是一个HTML解析示例</p>
</body>
</html>
"#;
// 解析HTML
let tree = parser.parse(source_code, None).unwrap();
// 获取根节点
let root_node = tree.root_node();
// 打印语法树结构
println!("语法树:");
print_tree(&root_node, source_code, 0);
// 遍历所有元素节点
println!("\n所有元素节点:");
traverse_nodes(&root_node, source_code);
}
fn print_tree(node: &tree_sitter::Node, source: &str, depth: usize) {
let indent = " ".repeat(depth);
let node_type = node.kind();
let node_text = node.utf8_text(source.as_bytes()).unwrap_or("");
println!("{}{}: '{}'", indent, node_type, node_text);
// 递归打印子节点
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
print_tree(&child, source, depth + 1);
}
}
}
fn traverse_nodes(node: &tree_sitter::Node, source: &str) {
if node.kind() == "element" || node.kind() == "start_tag" {
let text = node.utf8_text(source.as_bytes()).unwrap_or("");
println!("{}: {}", node.kind(), text);
}
// 递归遍历子节点
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
traverse_nodes(&child, source);
}
}
}
这个示例展示了如何使用tree-sitter-html库来解析HTML代码,构建语法树,并遍历节点结构。代码包含了完整的解析过程和树结构打印功能。
1 回复
Rust HTML解析库tree-sitter-html的使用指南
概述
tree-sitter-html是一个基于Tree-sitter语法分析器的HTML解析库,专门为Rust语言设计。它能够高效解析HTML文档,构建精确的语法树,并提供DOM树构建功能。
主要特性
- 高性能的增量解析
- 精确的错误恢复机制
- 支持HTML5标准
- 提供丰富的节点查询API
- 内存安全的Rust实现
安装方法
在Cargo.toml中添加依赖:
[dependencies]
tree-sitter = "0.20"
tree-sitter-html = "0.19"
基本使用示例
use tree_sitter::Parser;
use tree_sitter_html::language;
fn main() {
// 创建解析器
let mut parser = Parser::new();
// 设置HTML语言
parser.set_language(language()).unwrap();
// 解析HTML内容
let html_content = r#"
<!DOCTYPE html>
<html>
<head>
<title>示例页面</title>
</head>
<body>
<h1>Hello World</h1>
<p>这是一个段落</p>
</body>
</html>
"#;
let tree = parser.parse(html_content, None).unwrap();
// 获取根节点
let root_node = tree.root_node();
// 遍历语法树
let mut cursor = root_node.walk();
traverse_tree(&mut cursor, 0);
}
fn traverse_tree(cursor: &mut tree_sitter::TreeCursor, depth: usize) {
let node = cursor.node();
let indent = " ".repeat(depth);
println!("{}{:?}: {}", indent, node.kind(), node.utf8_text(cursor.node().kind().as_bytes()).unwrap_or(""));
if cursor.goto_first_child() {
traverse_tree(cursor, depth + 1);
while cursor.goto_next_sibling() {
traverse_tree(cursor, depth + 1);
}
cursor.goto_parent();
}
}
查询节点示例
use tree_sitter::{Query, QueryCursor};
fn query_elements() {
let html = r#"<div class="container"><p>内容</p></div>"#;
let mut parser = Parser::new();
parser.set_language(language()).unwrap();
let tree = parser.parse(html, None).unwrap();
// 创建查询来查找所有元素
let query = Query::new(
language(),
"(element (start_tag (tag_name) @tag) @element"
).unwrap();
let mut query_cursor = QueryCursor::new();
let matches = query_cursor.matches(&query, tree.root_node(), html.as_bytes());
for mat in matches {
for capture in mat.captures {
let node = capture.node;
println!("找到元素: {}", node.utf8_text(html.as_bytes()).unwrap());
}
}
}
错误处理
fn parse_with_error_handling(html: &str) -> Result<(), Box<dyn std::error::Error>> {
let mut parser = Parser::new();
parser.set_language(language())?;
match parser.parse(html, None) {
Ok(tree) => {
// 检查是否有语法错误
if tree.root_node().has_error() {
eprintln!("解析完成,但包含语法错误");
}
Ok(())
}
Err(e) => {
eprintln!("解析失败: {}", e);
Err(Box::new(e))
}
}
}
完整示例demo
use tree_sitter::{Parser, Query, QueryCursor};
use tree_sitter_html::language;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
// 示例HTML内容
let html_content = r#"
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>示例页面</title>
<style>
body { font-family: Arial, sans-serif; }
</style>
</head>
<body>
<div class="container">
<h1>欢迎使用tree-sitter-html</h1>
<p>这是一个使用Rust解析HTML的示例</p>
<ul>
<li>项目1</li>
<li>项目2</li>
<li>项目3</li>
</ul>
</div>
<script>
console.log("页面加载完成");
</script>
</body>
</html>
"#;
// 创建解析器实例
let mut parser = Parser::new();
// 设置HTML语言
parser.set_language(language())?;
// 解析HTML内容
let tree = parser.parse(html_content, None)?;
// 检查解析结果
let root_node = tree.root_node();
println!("解析完成,根节点类型: {:?}", root_node.kind());
// 检查是否有语法错误
if root_node.has_error() {
eprintln!("警告:HTML包含语法错误");
}
// 遍历语法树
println!("\n=== 语法树遍历 ===");
let mut cursor = root_node.walk();
traverse_tree(&mut cursor, 0);
// 查询特定元素
println!("\n=== 元素查询 ===");
query_specific_elements(html_content)?;
// 错误处理示例
println!("\n=== 错误处理示例 ===");
let invalid_html = "<div><p>未闭合的标签";
parse_with_error_handling(invalid_html)?;
Ok(())
}
/// 递归遍历语法树
fn traverse_tree(cursor: &mut tree_sitter::TreeCursor, depth: usize) {
let node = cursor.node();
let indent = " ".repeat(depth);
// 输出节点信息
let node_text = node.utf8_text(cursor.node().kind().as_bytes()).unwrap_or("");
println!("{}{:?}: '{}'", indent, node.kind(), node_text);
// 递归遍历子节点
if cursor.goto_first_child() {
traverse_tree(cursor, depth + 1);
while cursor.goto_next_sibling() {
traverse_tree(cursor, depth + 1);
}
cursor.goto_parent();
}
}
/// 查询特定HTML元素
fn query_specific_elements(html: &str) -> Result<(), Box<dyn Error>> {
let mut parser = Parser::new();
parser.set_language(language())?;
let tree = parser.parse(html, None)?;
// 查询所有标题元素
let query = Query::new(
language(),
"(element (start_tag (tag_name) @tag_name) @element"
)?;
let mut query_cursor = QueryCursor::new();
let matches = query_cursor.matches(&query, tree.root_node(), html.as_bytes());
println!("找到的元素:");
for mat in matches {
for capture in mat.captures {
let node = capture.node;
if node.kind() == "tag_name" {
let tag_name = node.utf8_text(html.as_bytes())?;
println!(" - 标签名: {}", tag_name);
}
}
}
Ok(())
}
/// 带错误处理的解析函数
fn parse_with_error_handling(html: &str) -> Result<(), Box<dyn Error>> {
let mut parser = Parser::new();
parser.set_language(language())?;
match parser.parse(html, None) {
Ok(tree) => {
if tree.root_node().has_error() {
println!("解析完成,但文档包含语法错误");
// 可以在这里添加更详细的错误处理逻辑
} else {
println!("文档解析成功");
}
Ok(())
}
Err(e) => {
eprintln!("解析失败: {}", e);
Err(Box::new(e))
}
}
}
/// 性能优化示例:重用解析器实例
struct HtmlParser {
parser: Parser,
}
impl HtmlParser {
fn new() -> Result<Self, Box<dyn Error>> {
let mut parser = Parser::new();
parser.set_language(language())?;
Ok(Self { parser })
}
fn parse(&mut self, html: &str) -> Result<tree_sitter::Tree, Box<dyn Error>> {
Ok(self.parser.parse(html, None)?)
}
}
性能优化提示
- 重用Parser实例以避免重复分配
- 对于大文件,使用增量解析功能
- 合理使用查询缓存
- 及时释放不再使用的语法树
注意事项
- 确保输入的HTML编码为UTF-8
- 处理不完整HTML时注意错误恢复机制
- 对于生产环境,建议添加适当的错误处理和日志记录
这个库特别适合需要高性能HTML解析的场景,如网页爬虫、静态网站生成器、代码编辑器等工具的开发。