Rust的robots.txt解析库robotstxt的使用:高效解析与生成网站爬虫规则文件

robotstxt

Crates.io Docs.rs Apache 2.0

Google的robots.txt解析器和匹配器C++库的原生Rust移植版本。

  • 原生Rust移植,无第三方crate依赖
  • 零不安全代码
  • 保留原始库的所有行为
  • 与原始库一致的API
  • 100%通过Google原始测试

安装

[dependencies]
robotstxt = "0.3.0"

快速开始

use robotstxt::DefaultMatcher;

let mut matcher = DefaultMatcher::default(); let robots_body = “user-agent: FooBot\n
disallow: /\n”
; assert_eq!(false, matcher.one_agent_allowed_by_robots(robots_body, “FooBot”, https://foo.com/));

关于

引用自Google的robots.txt解析器和匹配器仓库的README:

机器人排除协议(REP)是一种标准,使网站所有者能够通过具有特定语法的简单文本文件控制哪些URL可以被自动化客户端(即爬虫)访问。它是我们所知的互联网的基本构建块之一,也是搜索引擎能够运行的原因。

由于REP在过去25年中只是一个事实上的标准,不同的实现者对robots.txt的解析略有不同,导致混淆。这个项目旨在通过发布Google使用的解析器来解决这个问题。

该库是Googlebot(Google的爬虫)使用的生产代码的略微修改版本(即一些内部头和等效符号),用于根据网站管理员在robots.txt文件中提供的规则确定可以访问哪些URL。该库以开源形式发布,以帮助开发人员构建更好地反映Google的robots.txt解析和匹配的工具。

Crate robotstxt 旨在成为Google的robots.txt解析器和匹配器从C++到Rust的忠实转换。

测试

$ git clone https://github.com/Folyd/robotstxt
Cloning into 'robotstxt'...
$ cd robotstxt/tests 
...
$ mkdir c-build && cd c-build
...
$ cmake ..
...
$ make
...
$ make test
Running tests...
Test project ~/robotstxt/tests/c-build
    Start 1: robots-test
1/1 Test #1: robots-test ......................   Passed    0.33 sec

许可证

robotstxt解析器和匹配器Rust库根据Apache许可证的条款进行许可。有关更多信息,请参阅LICENSE。

完整示例代码

// 引入robotstxt库中的DefaultMatcher
use robotstxt::DefaultMatcher;

fn main() { // 创建默认的匹配器实例 let mut matcher = DefaultMatcher::default();

<span class="hljs-comment">// 定义robots.txt内容,禁止FooBot访问所有路径</span>
<span class="hljs-keyword">let</span> <span class="hljs-variable">robots_body</span> = <span class="hljs-string">"user-agent: FooBot\n\
                   disallow: /\n"</span>;

<span class="hljs-comment">// 检查FooBot是否被允许访问指定URL</span>
<span class="hljs-keyword">let</span> <span class="hljs-variable">is_allowed</span> = matcher.<span class="hljs-title function_ invoke__">one_agent_allowed_by_robots</span>(
    robots_body, 
    <span class="hljs-string">"FooBot"</span>, 
    <span class="hljs-string">"https://foo.com/"</span>
);

<span class="hljs-comment">// 断言结果应为false(不允许访问)</span>
<span class="hljs-built_in">assert_eq!</span>(<span class="hljs-literal">false</span>, is_allowed);
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"FooBot访问被拒绝: {}"</span>, !is_allowed);

<span class="hljs-comment">// 另一个示例:允许其他爬虫访问</span>
<span class="hljs-keyword">let</span> <span class="hljs-variable">is_other_allowed</span> = matcher.<span class="hljs-title function_ invoke__">one_agent_allowed_by_robots</span>(
    robots_body, 
    <span class="hljs-string">"OtherBot"</span>, 
    <span class="hljs-string">"https://foo.com/"</span>
);

<span class="hljs-built_in">println!</span>(<span class="hljs-string">"OtherBot访问允许: {}"</span>, is_other_allowed);

}


1 回复

Rust的robots.txt解析库robotstxt使用指南

概述

robotstxt是一个用于解析和生成robots.txt文件的Rust库,帮助开发者处理网站爬虫规则。该库支持解析现有的robots.txt文件,也能生成符合标准的规则文件。

安装方法

在Cargo.toml中添加依赖:

[dependencies]
robotstxt = "0.10"

基本用法

解析robots.txt文件

use robotstxt::DefaultMatcher;

fn main() {
    let contents = r#"
        User-agent: *
        Disallow: /private/
        Allow: /public/
        Crawl-delay: 5
    "#;

    let matcher = DefaultMatcher::default();
    let result = matcher.one_agent_allowed_by_robots(contents, "MyBot", "/public/about.html");
    
    println!("允许访问: {}", result); // 输出: true
}

检查特定路径的访问权限

use robotstxt::DefaultMatcher;

fn check_access(robots_txt: &str, user_agent: &str, path: &str) -> bool {
    let matcher = DefaultMatcher::default();
    matcher.one_agent_allowed_by_robots(robots_txt, user_agent, path)
}

// 使用示例
let robots_content = "
User-agent: Googlebot
Disallow: /private/

User-agent: *
Disallow: /admin/
";

println!("Googlebot访问/admin/: {}", 
    check_access(robots_content, "Googlebot", "/admin/")); // false
println!("OtherBot访问/admin/: {}", 
    check_access(robots_content, "OtherBot", "/admin/")); // false

生成robots.txt文件

use robotstxt::RobotsBuilder;

fn generate_robots_txt() -> String {
    RobotsBuilder::new()
        .add_user_agent("*")
        .disallow("/private/")
        .allow("/public/")
        .crawl_delay(10)
        .add_user_agent("Googlebot")
        .disallow("/temp/")
        .finish()
        .to_string()
}

println!("{}", generate_robots_txt());

高级功能

处理多个User-agent

use robotstxt::DefaultMatcher;

fn check_multiple_agents() {
    let robots_txt = "
        User-agent: Bot1
        Disallow: /dir1/
        
        User-agent: Bot2
        Allow: /dir1/
        Disallow: /dir2/
    ";

    let matcher = DefaultMatcher::default();
    
    // 检查Bot1对/dir1/page的访问
    let result1 = matcher.one_agent_allowed_by_robots(robots_txt, "Bot1", "/dir1/page.html");
    println!("Bot1访问/dir1/page.html: {}", result1); // false
    
    // 检查Bot2对/dir1/page的访问
    let result2 = matcher.one_agent_allowed_by_robots(robots_txt, "Bot2", "/dir1/page.html");
    println!("Bot2访问/dir1/page.html: {}", result2); // true
}

处理爬虫延迟设置

use robotstxt::{DefaultMatcher, RobotsParseHandler};

fn get_crawl_delay(robots_txt: &str, user_agent: &str) -> Option<u64> {
    let mut handler = RobotsParseHandler::new();
    handler.parse(robots_txt.as_bytes());
    
    for group in handler.get_groups() {
        if group.applies_to(user_agent) {
            return group.get_crawl_delay();
        }
    }
    None
}

let delay = get_crawl_delay(
    "User-agent: *\nCrawl-delay: 5",
    "MyBot"
);
println!("爬虫延迟: {:?}秒", delay); // Some(5)

错误处理

use robotstxt::DefaultMatcher;

fn safe_check(robots_txt: &str, user_agent: &str, path: &str) -> Result<bool, &'static str> {
    if robots_txt.is_empty() {
        return Err("空的robots.txt内容");
    }
    
    let matcher = DefaultMatcher::default();
    Ok(matcher.one_agent_allowed_by_robots(robots_txt, user_agent, path))
}

实际应用示例

use robotstxt::DefaultMatcher;
use reqwest::blocking::get;

fn check_website_permission(url: &str, user_agent: &str, path: &str) -> bool {
    let robots_url = format!("{}/robots.txt", url.trim_end_matches('/'));
    
    match get(&robots_url) {
        Ok(response) => {
            if let Ok(content) = response.text() {
                let matcher = DefaultMatcher::default();
                matcher.one_agent_allowed_by_robots(&content, user_agent, path)
            } else {
                true // 如果无法读取内容,默认允许访问
            }
        }
        Err(_) => true // 如果无法获取robots.txt,默认允许访问
    }
}

完整示例demo

// 完整示例:构建一个robots.txt检查工具
use robotstxt::{DefaultMatcher, RobotsBuilder};
use std::io::{self, Write};

fn main() {
    // 示例1:生成robots.txt文件
    println!("=== 生成robots.txt示例 ===");
    let robots_content = generate_robots_example();
    println!("生成的robots.txt内容:\n{}", robots_content);
    
    // 示例2:检查访问权限
    println!("\n=== 访问权限检查示例 ===");
    check_access_permissions(&robots_content);
    
    // 示例3:交互式检查工具
    println!("\n=== 交互式检查工具 ===");
    interactive_check_tool(&robots_content);
}

// 生成示例robots.txt
fn generate_robots_example() -> String {
    RobotsBuilder::new()
        .add_user_agent("*")
        .disallow("/admin/")
        .disallow("/private/")
        .allow("/public/")
        .crawl_delay(5)
        .add_user_agent("Googlebot")
        .allow("/news/")
        .disallow("/temp/")
        .add_user_agent("Bingbot")
        .disallow("/search/")
        .finish()
        .to_string()
}

// 检查不同用户代理的访问权限
fn check_access_permissions(robots_txt: &str) {
    let matcher = DefaultMatcher::default();
    
    let test_cases = vec![
        ("Googlebot", "/news/article1", "Googlebot访问新闻文章"),
        ("Googlebot", "/admin/dashboard", "Googlebot访问管理面板"),
        ("Bingbot", "/search/results", "Bingbot访问搜索结果"),
        ("MyCrawler", "/public/docs", "普通爬虫访问公开文档"),
        ("MyCrawler", "/private/data", "普通爬虫访问私有数据"),
    ];
    
    for (ua, path, description) in test_cases {
        let allowed = matcher.one_agent_allowed_by_robots(robots_txt, ua, path);
        println!("{}: {}", description, if allowed { "允许" } else { "拒绝" });
    }
}

// 交互式检查工具
fn interactive_check_tool(robots_txt: &str) {
    let matcher = DefaultMatcher::default();
    
    loop {
        print!("请输入用户代理名称 (输入 'quit' 退出): ");
        io::stdout().flush().unwrap();
        
        let mut user_agent = String::new();
        io::stdin().read_line(&mut user_agent).unwrap();
        let user_agent = user_agent.trim();
        
        if user_agent.eq_ignore_ascii_case("quit") {
            break;
        }
        
        print!("请输入要检查的路径: ");
        io::stdout().flush().unwrap();
        
        let mut path = String::new();
        io::stdin().read_line(&mut path).unwrap();
        let path = path.trim();
        
        let allowed = matcher.one_agent_allowed_by_robots(robots_txt, user_agent, path);
        println!("用户代理 '{}' 访问路径 '{}': {}",
            user_agent, path, 
            if allowed { "允许" } else { "拒绝" }
        );
        println!();
    }
}

这个库提供了完整的robots.txt处理功能,适合在爬虫项目或web应用中集成机器人协议检查功能。

回到顶部