使用 Rust 库 reqwest 和 select 协同工作

3
我正在尝试按照这个例子:https://rust-lang-nursery.github.io/rust-cookbook/web/scraping.html,使用Reqwest和Select获取HTML响应并解析数据。
我使用的是Reqwest 0.10.4版本和Select 0.4.3版本,这也是该示例中展示的版本。但是,我遇到了一个错误:
error[E0277]: the trait bound `reqwest::Response: std::io::Read` is not satisfied
  --> src/main.rs:19:25
   |
19 |     Document::from_read(res)?
   |                         ^^^ the trait `std::io::Read` is not implemented for `reqwest::Response`
   | 
  ::: /root/.cargo/registry/src/github.com-1ecc6299db9ec823/select-0.4.3/src/document.rs:31:25
   |
31 |     pub fn from_read<R: io::Read>(mut readable: R) -> io::Result<Document> {
   |                         -------- required by this bound in `select::document::Document::from_read`

看起来 from_read 方法需要一个 Read 类型,但 reqwest::get 方法返回的是不同类型的内容。在将响应传递给 from_read 方法之前,是否需要进行某种转换?

这是示例:

#[macro_use]
extern crate error_chain;
extern crate reqwest;
extern crate select;

use select::document::Document;
use select::predicate::Name;

error_chain! {
   foreign_links {
       ReqError(reqwest::Error);
       IoError(std::io::Error);
   }
}

fn main() -> Result<()> {
    let res = reqwest::get("https://www.rust-lang.org/en-US/").await?;

    Document::from_read(res)?
        .find(Name("a"))
        .filter_map(|n| n.attr("href"))
        .for_each(|x| println!("{}", x));

    Ok(())
}
1个回答

1

reqwest::get 返回一个 Result<Response>,然后通过使用 ? 解包 Result,意味着您现在拥有一个作为文档 here 记录的 Response 对象。由于 Web 调用可能成功发生但仍然失败(请参见 HTTP 非 200 状态码),因此您应该检查响应代码,但由于这是为了学习,我们将忽略它。您需要的是一种实现了 std::io::Read 特征的结构体,阅读该特征可以了解到 String 实现了该特征。回到 reqwest::Response,可以使用方法 text() 获取返回的字符串。所以您的代码现在变成了:

let res = reqwest::get("https://www.rust-lang.org/en-US/")
              .await?
              .text()
              .await?;
评论更新:现在问题是Document::from_read只接受std::io::Read作为参数,而std::string::String没有实现该trait。我们可以使用Document::from,因为document实现了From<&'a str> trait,而不是使用像stringreader这样的crate来解决这个问题。
几乎所有事情都有多种解决方法。你可以选择以下两种方式之一:
  1. 直接使用Document::from(res)从字符串中创建文档,或者

  2. 将字符串转换为字节切片(实现了Read),然后使用Document::from_read(res.as_bytes())创建文档。


谢谢您的回复。我在做一些测试时也尝试了这种方法。我遇到了一个类似的错误:“未为'std :: string :: String'实现特征'std :: io :: Read'”。我查看了文档中有关Read的一些内容:https://doc.rust-lang.org/std/io/trait.Read.html#implementors。底部的实现者部分没有显示String作为实现者--我不确定这些是否是可以与Read一起使用的唯一类型,因为我还很新学Rust。 - winsticknova
1
啊,我现在明白了,我盲目地假设String实现了Read。浏览实现者时,我看到了&'a [u8],所以你可以在String上调用as_bytes - MindSwipe
或者,您可以使用Document::from(res)而不是Document::from_read(),因为Document结构实现了From<&'a str>特质,如此文档所述这里 - MindSwipe
尝试使用Document::from,但是它说“std::convert::From<&std::string::String>”特征未为“select::document::Document”实现。在查看select文档(https://docs.rs/select/0.4.3/select/document/struct.Document.html)后,发现select::document::Document的唯一方法是“find”,“from_read”和“nth”。另一个可能的解决方案是将响应数据转换为字符串,然后创建一个文件并将其传递给Document::from_read吗?虽然这似乎非常不理想。 - winsticknova
非常奇怪,它说文档实现了 impl<'a> From<&'a str> for Document。也许你需要显式地转换它,比如 Document::from(&res[..])(这将返回 res 的完整切片)或者像 Document::from(&*res) 那样解引用字符串。是的,创建一个文件并读取它是次优的。 - MindSwipe
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接