crawl and scrape web pages in rust
With voyager you can easily extract structured data from websites.
Write your own crawler/scraper with voyager following a state machine model.
The examples use tokio as its runtime, so your Cargo.toml could look like this:
[dependencies]
voyager = { version = "0.1" }
tokio = { version = "1", features = ["full"] }// Declare your scraper, with all the selectors etc.
struct HackernewsScraper {
post_selector: Selector,
author_selector: Selector,
title_selector: Selector,
comment_selector: Selector,
max_page: usize,
}
/// The state model
#[derive(Debug)]
enum HackernewsState {
Page(usize),
Post,
}
/// The ouput the scraper should eventually produce
#[derive(Debug)]
struct Entry {
author: String,
url: Url,
link: Option,
title: String,
}voyager::Scraper traitA Scraper consists of two associated types:
Output, the type the scraper eventually producesState, the type, the scraper can drag along several requests that eventually lead to an Outputand the scrape callback, which is invoked after each received response.
Based on the state attached to response you can supply the crawler with new urls to visit with, or without a state attached to it.
Scraping is done with causal-agent/scraper.
…Configure the crawler with via CrawlerConfig:
Robots.txt rulesFeed your config and an instance of your scraper to the Collector that drives the Crawler and forwards the responses to your Scraper.
…See examples for more.
Sometimes it might be helpful to execute some other calls first, get a token etc.,
You submit async closures to the crawler to manually get a response and inject a state or drive a state to completion
…If the crawler encountered an error, due to a failed or disallowed http request, the error is reported as CrawlError, which carries the last valid state. The error then can be down casted.
let mut collector = Collector::new(HackernewsScraper::default(), config);
while let Some(output) = collector.next().await {
match output {
Ok(post) => {/**/}
Err(err) => {
// recover the state by downcasting the error
if let Ok(err) = err.downcast::::State>>() {
let last_state = err.state();
}
}
}
}Licensed under either of these:
No open issues yet, or sync has not completed.