Expand description
Web scraping and data ingestion.
Implement Endpoint for a type that describes your data source. The
endpoint makes the next request and parses each response into items. The
endpoint sees the previous response, thus cursor pagination is possible.
Give the endpoint to an Ingester with an HttpClient and a
RateLimit. Then read the items from the stream that the ingester
makes.
use ingester::{Endpoint, Error, Request, Response, ingest};
struct StoryIds {
page: u32,
max_pages: Option<u64>,
}
impl Endpoint for StoryIds {
type Item = u64;
fn next_request(&mut self, last: Option<&Response>) -> Option<Request> {
if let Some(resp) = last {
self.max_pages = resp.json::<serde_json::Value>().ok()?["nbPages"].as_u64();
}
if self.max_pages.is_some_and(|max| u64::from(self.page) >= max) {
return None;
}
let url = format!(
"https://hn.algolia.com/api/v1/search?tags=front_page&page={}",
self.page
);
self.page += 1;
Some(Request::get(url.parse().unwrap()))
}
fn parse(&self, response: &Response) -> Result<Vec<u64>, Error> {
let body: serde_json::Value = response.json()?;
Ok(body["hits"]
.as_array()
.into_iter()
.flatten()
.filter_map(|hit| hit["objectID"].as_str()?.parse().ok())
.collect())
}
}
let stories = ingest(StoryIds { page: 0, max_pages: None })
.collect()
.await?;Re-exports§
pub use client::HttpClient;pub use rate_limit::NoRateLimit;pub use rate_limit::RateLimit;
Modules§
Structs§
- Ingester
- Operates an
Endpoint: sends its requests through anHttpClient, obeys aRateLimit, and parses the responses into a stream of items. - Request
- One HTTP request that an
Endpointmakes. - Reqwest
- The
reqwestclient, re-exported for use as anHttpClientbackend:HttpClient<Reqwest>. - Response
- The response to one
Request.
Enums§
Traits§
- Endpoint
- Your description of the procedure that ingests one endpoint.
Functions§
- ingest
- Start ingestion of an endpoint with the default configuration: an
HttpClientthat usesReqwest, and no rate limit. UseIngester::with_clientandIngester::with_rate_limitto change the configuration.