Rust 中的异步网页抓取

问题描述 投票:0回答:1

我正在尝试使用 tokioreqwestscraper 板条箱在 Rust 中执行异步和并行 HTML 抓取。我有一个循环遍历 HTML 元素并使用 tokio::spawn 同时处理每个元素。但是,我遇到了 ElementRef 类型未实现 Send 特征的问题,导致无法在异步上下文中使用它。

示例:

<div class="aside">....</div>
<div class="aside">....</div>
<div class="aside">....</div>

代码:

#[tokio::main]
async fn main() -> Res<()> {
    ...
    let response = reqwest::get(url).await?;
    ...
    let document: ElementRef<'_> = document.root_element();

    for node in document.select(&ASIDE_SELECTOR) {
        tokio::spawn(async move {
            // Parse, insert to Db, etc.
            // Issue: ElementRef is not Send, preventing async usage
            println!("{}", node.inner_html());
        });
    }
    Ok(())
}

如何解决这个问题并在 Rust 中执行异步并行 HTML 抓取?在这种情况下是否可以使用替代类型或方法?

asynchronous web-scraping rust rust-tokio
1个回答
0
投票

这会失败,因为

ElementRef
包含对文档结构的引用,但独立的 Tokio 任务没有生命周期限制,因此传递给
tokio::spawn
的 future 必须是
'static
。这个矛盾导致了错误。

另请注意,当主任务(在本例中为

main()
)终止时,任何生成的后台任务都将终止,因此此代码将启动一堆后台任务,然后很可能在它们可以执行任何工作之前终止它们。

您实际上不太可能需要为每个元素生成一个任务。考虑将

select
迭代器转换为 stream。完成此操作后,您可以将每个元素映射到未来,并使用
buffer_unordered
组合器
同时等待它们。这将解决这两个问题。

类似这样的:

futures::stream::iter(document.select(&ASIDE_SELECTOR))
    .map(|node| async move {
        // Parse, insert to db, etc.
    })
    // Run up to 16 concurrently.
    .buffer_unordered(16)
    .await;

如果您不需要从任务期货中收集任何结果,您可以使用

for_each_concurrent
组合器代替
map
buffer_unordered

© www.soinside.com 2019 - 2024. All rights reserved.