#html-parser #html #dom #parser #document #optimization #tags

tl

纯Rust编写的快速HTML解析器

24次发布

0.7.8 2024年1月29日
0.7.7 2022年8月25日
0.7.5 2022年4月18日
0.7.4 2022年3月20日
0.2.0 2021年3月27日

#57 in 解析器实现

Download history 2095/week @ 2024-03-14 3104/week @ 2024-03-21 3785/week @ 2024-03-28 2926/week @ 2024-04-04 2324/week @ 2024-04-11 2955/week @ 2024-04-18 3645/week @ 2024-04-25 3094/week @ 2024-05-02 2998/week @ 2024-05-09 3349/week @ 2024-05-16 3530/week @ 2024-05-23 3416/week @ 2024-05-30 4595/week @ 2024-06-06 4138/week @ 2024-06-13 5100/week @ 2024-06-20 5266/week @ 2024-06-27

19,739每月下载量
用于 43 个crate(34个直接使用)

MIT 协议

145KB
3.5K SLoC

tl

tl是一个纯Rust编写的快速HTML解析器。

这个crate(目前)并不严格遵循HTML标准的完整规范,然而这通常对大多数用例来说不是问题。这个crate通常尝试支持大多数“合理”的HTML。不受规范的限制提供了更多的优化机会。如果您需要一个可以(非常快速)解析典型HTML文档且需要一个简单的API来操作DOM的解析器,请尝试这个crate。

如果您需要一个严格遵循标准的解析器,请考虑使用 html5everlol-htmlhtml5gum

使用方法

tl 添加到依赖中。

[dependencies]
tl = "0.7.8"
# or, with explicit SIMD support
# (requires a nightly compiler!)
tl = { version = "0.7.8", features = ["simd"] }

主函数是 tl::parse()。它接受一个HTML源代码字符串并将其解析。需要注意的是,tl目前会静默忽略无效的标签,就像浏览器一样。有时,这意味着HTML文档的大块内容不会出现在结果树中。

let dom = tl::parse(r#"<p id="text">Hello</p>"#, tl::ParserOptions::default()).unwrap();
let parser = dom.parser();
let element = dom.get_element_by_id("text")
  .expect("Failed to find element")
  .get(parser)
  .unwrap();

assert_eq!(element.inner_text(parser), "Hello");

示例

使用查询选择器API查找标签
let dom = tl::parse(r#"<div><img src="cool-image.png" /></div>"#, tl::ParserOptions::default()).unwrap();
let img = dom.query_selector("img[src]").unwrap().next();
    
assert!(img.is_some());
遍历HTML文档的子节点
let dom = tl::parse(r#"<div><img src="cool-image.png" /></div>"#, tl::ParserOptions::default()).unwrap();
let img = dom.nodes()
  .iter()
  .find(|node| {
    node.as_tag().map_or(false, |tag| tag.name() == "img")
  });
    
assert!(img.is_some());
修改锚标签的`href`属性

在实际场景中,您应该正确处理错误,而不是简单地解包。

let input = r#"<div><a href="/about">About</a></div>"#;
let mut dom = tl::parse(input, tl::ParserOptions::default())
  .expect("HTML string too long");
  
let anchor = dom.query_selector("a[href]")
  .expect("Failed to parse query selector")
  .next()
  .expect("Failed to find anchor tag");

let parser_mut = dom.parser_mut();

let anchor = anchor.get_mut(parser_mut)
  .expect("Failed to resolve node")
  .as_tag_mut()
  .expect("Failed to cast Node to HTMLTag");

let attributes = anchor.attributes_mut();

attributes.get_mut("href")
  .flatten()
  .expect("Attribute not found or malformed")
  .set("https://127.0.0.1/about");

assert_eq!(attributes.get("href").flatten(), Some(&"https://127.0.0.1/about".into()));

SIMD加速解析

这个crate有由解析器使用的工具函数,它们利用SIMD(例如,通过一次查看下一个16个字节来查找特定的字节,而不是逐个遍历字符串)。这些函数默认是禁用的,并且必须通过传递simd功能标志显式启用,因为portable_simd功能不稳定。这需要**夜间**编译器!

如果未启用simd功能,它将回退到稳定的替代方案,这些替代方案不明确使用SIMD内省,但仍然得到了很好的优化,使用诸如手动循环展开等技术来移除边界检查和其他分支,这也有助于LLVM进一步优化代码,并可能自行生成SIMD指令。

无运行时依赖