Rust网页解析与操作库webpage的使用,高效处理HTML解析、DOM操作和网页内容提取

Rust网页解析与操作库webpage的使用,高效处理HTML解析、DOM操作和网页内容提取

crates.io docs.rs

小型库,用于获取网页信息:标题、描述、语言、HTTP信息、链接、RSS订阅、Opengraph、Schema.org等

使用方法

use webpage::{Webpage, WebpageOptions};

let info = Webpage::from_url("http://www.rust-lang.org/en-US/", WebpageOptions::default())
    .expect("Could not read from URL");

// HTTP传输信息
let http = info.http;

assert_eq!(http.ip, "54.192.129.71".to_string());
assert!(http.headers[0].starts_with("HTTP"));
assert!(http.body.starts_with("<!DOCTYPE html>"));
assert_eq!(http.url, "https://www.rust-lang.org/en-US/".to_string()); // 跟随重定向(HTTPS)
assert_eq!(http.content_type, "text/html".to_string());

// 解析的HTML信息
let html = info.html;

assert_eq!(html.title, Some("The Rust Programming Language".to_string()));
assert_eq!(html.description, Some("A systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety.".to_string()));
assert_eq!(html.opengraph.og_type, "website".to_string());

你也可以获取本地数据的HTML信息:

use webpage::HTML;
let html = HTML::from_file("index.html", None);
// 或者 let html = HTML::from_string(input, None);

完整示例

以下是一个完整的示例,展示如何使用webpage库解析网页并提取各种信息:

use webpage::{Webpage, WebpageOptions};
use std::time::Duration;

fn main() {
    // 自定义选项
    let mut options = WebpageOptions::default();
    options.timeout = Duration::from_secs(15); // 设置超时为15秒
    options.useragent = "My Rust Web Scraper".to_string();
    
    // 从URL获取网页信息
    let webpage = Webpage::from_url("https://www.example.com", options)
        .expect("Failed to fetch webpage");
    
    // 打印HTTP信息
    println!("HTTP Info:");
    println!("URL: {}", webpage.http.url);
    println!("IP: {}", webpage.http.ip);
    println!("Response Code: {}", webpage.http.response_code);
    println!("Content Type: {}", webpage.http.content_type);
    println!("Transfer Time: {:?}", webpage.http.transfer_time);
    
    // 打印HTML信息
    println!("\nHTML Info:");
    println!("Title: {:?}", webpage.html.title);
    println!("Description: {:?}", webpage.html.description);
    println!("Language: {:?}", webpage.html.language);
    
    // 打印所有链接
    println!("\nLinks ({} found):", webpage.html.links.len());
    for (i, link) in webpage.html.links.iter().enumerate().take(5) {
        println!("{}. {} -> {}", i + 1, link.text, link.url);
    }
    
    // 打印Opengraph信息
    println!("\nOpengraph Info:");
    println!("Type: {}", webpage.html.opengraph.og_type);
    println!("Properties:");
    for (key, value) in &webpage.html.opengraph.properties {
        println!("- {}: {}", key, value);
    }
    
    // 打印Schema.org信息
    println!("\nSchema.org Info:");
    for schema in &webpage.html.schema_org {
        println!("- Type: {}", schema.schema_type);
        println!("  Value: {}", schema.value);
    }
    
    // 打印纯文本内容(前200个字符)
    println!("\nText Content (first 200 chars):");
    println!("{}", &webpage.html.text_content[..200.min(webpage.html.text_content.len())]);
}

特性

序列化

如果需要使用serde序列化库提供的数据,你可以在Cargo.toml中指定serde特性:

webpage = { version = "2.0", features = ["serde"] }

不依赖curl

默认启用了curl特性,但是可选的。如果你不需要HTTP客户端但已经有HTML数据在手,这很有用。

所有字段

pub struct Webpage {
    pub http: HTTP, // HTTP传输信息
    pub html: HTML, // 从解析的HTML文档中获得的信息
}

pub struct HTTP {
    pub ip: String,
    pub transfer_time: Duration,
    pub redirect_count: u32,
    pub content_type: String,
    pub response_code: u32,
    pub headers: Vec<String>, // 最终请求的原始头信息
    pub url: String, // 有效URL
    pub body: String,
}

pub struct HTML {
    pub title: Option<String>,
    pub description: Option<String>,

    pub url: Option<String>, // 规范URL
    pub feed: Option<String>, // 通常是RSS订阅

    pub language: Option<String>, // 指定的语言,不是检测的
    pub text_content: String, // 从正文中剥离所有标签
    pub links: Vec<Link>, // 文档中的所有链接

    pub meta: HashMap<String, String>, // 扁平化的元属性列表

    pub opengraph: Opengraph,
    pub schema_org: Vec<SchemaOrg>,
}

pub struct Link {
    pub url: String, // 解析后的链接URL
    pub text: String, // 锚文本
}

pub struct Opengraph {
    pub og_type: String,
    pub properties: HashMap<String, String>,

    pub images: Vec<Object>,
    pub videos: Vec<Object>,
    pub audios: Vec<Object>,
}

// Facebook的Opengraph结构化数据
pub struct OpengraphObject {
    pub url: String,
    pub properties: HashMap<String, String>,
}

// Google的schema.org结构化数据
pub struct SchemaOrg {
    pub schema_type: String,
    pub value: serde_json::Value,
}

选项

以下HTTP配置可用:

pub struct WebpageOptions {
    allow_insecure: false,
    follow_location: true,
    max_redirections: 5,
    timeout: Duration::from_secs(10),
    useragent: "Webpage - Rust crate - https://crates.io/crates/webpage".to_string(),
    headers: vec!["X-My-Header: 1234".to_string()],
}

// 使用
let mut options = WebpageOptions::default();
options.allow_insecure = true;
let info = Webpage::from_url(&url, options).expect("Halp, could not fetch");

1 回复

Rust网页解析与操作库webpage使用指南

webpage是一个Rust库,用于高效解析HTML、操作DOM和提取网页内容。它提供了类似浏览器环境的API来处理网页数据。

主要特性

  • HTML解析和DOM构建
  • CSS选择器支持
  • 网页内容提取
  • 表单处理
  • 轻量级且高效

基本使用方法

添加依赖

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

[dependencies]
webpage = "0.4"

解析HTML文档

use webpage::HTML;

fn main() {
    let html = r#"
        <html>
            <head><title>测试页面</title></head>
            <body>
                <div id="content">Hello, world!</div>
                <a href="/about">关于</a>
            </body>
        </html>
    "#;
    
    let document = HTML::from_string(html, None).unwrap();
    
    // 获取标题
    if let Some(title) = document.title() {
        println!("页面标题: {}", title);
    }
    
    // 通过CSS选择器查找元素
    if let Some(div) = document.select("#content").next() {
        println!("div内容: {}", div.text());
    }
    
    // 获取所有链接
    for link in document.select("a[href]") {
        println!("链接: {} -> {}", link.text(), link.attr("href").unwrap());
    }
}

提取网页内容

use webpage::{HTML, Webpage};

fn main() {
    // 从URL加载网页
    let webpage = Webpage::from_url("https://example.com", None).unwrap();
    
    // 获取HTML内容
    let html = webpage.html();
    
    // 解析HTML
    let document = HTML::from_string(&html, None).unwrap();
    
    // 提取所有段落文本
    let paragraphs: Vec<String> = document.select("p")
        .map(|p| p.text().trim().to_string())
        .collect();
    
    println!("找到 {} 个段落:", paragraphs.len());
    for (i, p) in paragraphs.iter().enumerate() {
        println!("段落 {}: {}", i+1, p);
    }
}

表单处理

use webpage::HTML;

fn main() {
    let html = r#"
        <form action="/submit" method="post">
            <input type="text name="username" value="rust_user">
            <input type="password" name="password">
            <input type="submit" value="登录">
        </form>
    "#;
    
    let document = HTML::from_string(html, None).unwrap();
    
    if let Some(form) = document.select("form").next() {
        println!("表单 action: {}", form.attr("action").unwrap());
        println!("表单 method: {}", form.attr("method").unwrap());
        
        // 获取表单数据
        let data = form.form_data();
        println!("表单数据: {:?}", data);
    }
}

高级用法

修改DOM

use webpage::HTML;

fn main() {
    let html = "<div id='container'></div>";
    let mut document = HTML::from_string(html, None).unwrap();
    
    if let Some(div) = document.select("#container").next() {
        // 设置HTML内容
        div.set_inner_html("<p>新内容</p>");
        
        // 添加类
        div.add_class("highlight");
        
        // 设置属性
        div.set_attr("data-version", "1.0");
    }
    
    println!("修改后的HTML:\n{}", document.to_string());
}

处理真实网页

use webpage::{Webpage, WebpageOptions};

fn main() {
    let options = WebpageOptions {
        enable_javascript: false, // 禁用JS执行
        ..Default::default()
    };
    
    match Webpage::from_url("https://news.ycombinator.com", Some(options)) {
        Ok(webpage) => {
            let html = webpage.html();
            let document = HTML::from_string(&html, None).unwrap();
            
            // 提取新闻标题
            let titles: Vec<String> = document.select(".titleline > a")
                .map(|a| a.text().trim().to_string())
                .collect();
                
            println!("热门新闻:");
            for (i, title) in titles.iter().take(10).enumerate() {
                println!("{}. {}", i+1, title);
            }
        }
        Err(e) => eprintln!("错误: {}", e),
    }
}

性能提示

  1. 对于大型HTML文档,优先使用迭代器方法(select().next())而不是收集所有结果
  2. 如果只需要部分内容,考虑使用WebpageOptions限制加载的资源
  3. 重复使用的选择器可以编译为Selector对象提高性能
use webpage::{HTML, Selector};

fn main() {
    let html = "<div class='item'>Item 1</div><div class='item'>Item 2</div>";
    let document = HTML::from_string(html, None).unwrap();
    
    // 预编译选择器
    let selector = Selector::parse(".item").unwrap();
    
    for item in document.select(&selector) {
        println!("找到项目: {}", item.text());
    }
}

完整示例demo

下面是一个完整的webpage库使用示例,展示如何从网页提取数据并修改DOM:

use webpage::{HTML, Webpage, WebpageOptions, Selector};

fn main() {
    // 示例1: 解析HTML字符串
    println!("=== 示例1: 解析HTML字符串 ===");
    let html = r#"
        <html>
            <head><title>示例页面</title></head>
            <body>
                <h1>欢迎</h1>
                <div class="articles">
                    <article><h2>文章1</h2><p>内容1</p></article>
                    <article><h2>文章2</h2><p>内容2</p></article>
                </div>
                <footer>页脚内容</footer>
            </body>
        </html>
    "#;
    
    let document = HTML::from_string(html, None).unwrap();
    
    // 提取文章标题
    let article_titles: Vec<_> = document.select("article h2")
        .map(|h2| h2.text().trim().to_string())
        .collect();
    println!("文章标题: {:?}", article_titles);
    
    // 示例2: 从URL加载并处理网页
    println!("\n=== 示例2: 从URL加载网页 ===");
    let options = WebpageOptions {
        enable_javascript: false,
        ..Default::default()
    };
    
    if let Ok(webpage) = Webpage::from_url("https://example.com", Some(options)) {
        let html = webpage.html();
        let document = HTML::from_string(&html, None).unwrap();
        
        // 使用预编译选择器
        let selector = Selector::parse("h1").unwrap();
        if let Some(h1) = document.select(&selector).next() {
            println!("主标题: {}", h1.text());
        }
    }
    
    // 示例3: 修改DOM
    println!("\n=== 示例3: 修改DOM ===");
    let html = r#"<div id="app"><p>原始内容</p></div>"#;
    let mut document = HTML::from_string(html, None).unwrap();
    
    if let Some(div) = document.select("#app").next() {
        // 修改内容
        div.set_inner_html("<p>修改后的内容</p>");
        // 添加属性
        div.set_attr("data-modified", "true");
        
        println!("修改后的HTML:\n{}", document.to_string());
    }
}

这个完整示例展示了:

  1. 从HTML字符串解析文档
  2. 从URL加载网页内容
  3. 使用CSS选择器提取特定元素
  4. 预编译选择器提高性能
  5. 修改DOM元素的内容和属性

webpage库为Rust开发者提供了强大的网页处理能力,适合爬虫、内容分析和自动化测试等场景。

回到顶部