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()
}
特点
- 高效解析:swc_html_ast提供了快速的HTML解析能力
- 完整的AST支持:可以表示HTML文档中的所有节点类型
- 灵活的API:支持完整文档和片段解析
- 与SWC生态系统集成:是SWC工具链的一部分
注意事项
- 使用时需要处理可能的解析错误
- 对于大型HTML文档,考虑性能优化
- 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);
}
性能提示
- 对于大型HTML文档,考虑使用
swc_html_parser
的ParserConfig
调整解析行为 - 批量处理AST修改比多次遍历更高效
- 如果只需要部分信息,可以实现定制的访问者来避免完整遍历
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库的主要功能,包括:
- 解析HTML文档为AST
- 遍历和统计元素数量
- 修改AST节点(添加类名)
- 提取特定信息(链接)
- 转换文档结构(div转section)
- 重新生成HTML字符串
您可以根据实际需求调整或组合这些功能来构建自己的HTML处理工具。