Web Scraping with Rust

Rust is increasingly popular for web scraping because of its speed, memory safety, and concurrency capabilities. In this guide, we’ll build a scraper to fetch IMDb’s top ten movies using both blocking and asynchronous IO. Finally, we’ll show how FoxScrape can simplify fetching dynamic or protected content, letting you focus on parsing and data processing.
⚙️ 1. Why Rust for Web Scraping?
Rust offers:
| Feature | Benefit |
|---|---|
| Memory Safety | Avoids crashes or memory leaks during scraping |
| Speed | Efficient HTTP requests and HTML parsing |
| Concurrency | Async tasks with Tokio for multiple requests |
| Type Safety | Compile-time checks prevent common runtime errors |
Popular Rust crates for scraping:
reqwest – HTTP client (blocking & async)scraper – CSS-selector-based HTML parsingtokio – async runtimeserde_json – optional for JSON serialization🛠️ 2. Setting Up Your Rust Project
Create a new project:
1cargo new imdb_scraper2cd imdb_scraper
Add dependencies in Cargo.toml:
1[dependencies]2reqwest = { version = "0.12.12", features = ["blocking"] }3scraper = "0.22.0"4tokio = { version = "1", features = ["full"] } # for async5serde_json = "1.0" # optional for saving JSON
🏗️ 3. Blocking IO Scraper
For simple single-page scraping, blocking IO works well.
1use reqwest::blocking::get;2use scraper::{Html, Selector};34fn main() -> Result<(), Box<dyn std::error::Error>> {5let url = "https://www.imdb.com/chart/top/";6let response = get(url)?.text()?;78let document = Html::parse_document(&response);9let selector = Selector::parse("td.titleColumn a")?;1011let titles: Vec<String> = document12.select(&selector)13.take(10)14.map(|x| x.text().collect::<String>())15.collect();1617println!("Top 10 IMDb Movies:");18for (i, title) in titles.iter().enumerate() {19println!("{}. {}", i + 1, title);20}2122Ok(())23}
✅ What’s happening:
reqwest::blocking::get fetches HTML synchronously.scraper::Html::parse_document parses HTML into a queryable DOM.Selector::parse with CSS selectors targets movie titles.Limitation: Works only for static pages; dynamic content or anti-bot measures require extra handling.
⚡ 4. Async Scraping with Tokio
Blocking IO is simple but slow for multiple pages. Using Tokio, you can fetch many pages concurrently:
1use reqwest::Client;2use scraper::{Html, Selector};3use futures::future::try_join_all;45#[tokio::main]6async fn main() -> Result<(), Box<dyn std::error::Error>> {7let client = Client::new();8let urls = vec![9"https://www.imdb.com/chart/top/",10"https://www.imdb.com/chart/moviemeter/"11];1213let futures = urls.into_iter().map(|url| async {14let resp = client.get(url).send().await?.text().await?;15Ok::<String, reqwest::Error>(resp)16});1718let results = try_join_all(futures).await?;1920for html in results {21let document = Html::parse_document(&html);22let selector = Selector::parse("td.titleColumn a")?;23for movie in document.select(&selector).take(5) {24println!("{}", movie.text().collect::<String>());25}26}2728Ok(())29}
Benefits of async:
🦊 5. Handling Protected or JS-heavy Pages with FoxScrape
Some pages (like modern e-commerce or IMDb user-specific pages) use JavaScript or anti-scraping measures. Managing proxies, headless browsers, and retries in Rust can be complex. FoxScrape simplifies this:
🔧 Example: Using FoxScrape with Rust
1use reqwest::blocking::get;2use scraper::{Html, Selector};3use std::error::Error;45fn main() -> Result<(), Box<dyn Error>> {6let api_key = "YOUR_API_KEY";7let target_url = "https://www.imdb.com/chart/top/";8let fox_url = format!(9"https://www.foxscrape.com/api/v1?api_key={}&url={}",10api_key, target_url11);1213let resp = get(&fox_url)?.text()?;14let document = Html::parse_document(&resp);15let selector = Selector::parse("td.titleColumn a")?;1617let titles: Vec<String> = document18.select(&selector)19.take(10)20.map(|x| x.text().collect::<String>())21.collect();2223println!("Top 10 IMDb Movies (via FoxScrape):");24for (i, title) in titles.iter().enumerate() {25println!("{}. {}", i + 1, title);26}2728Ok(())29}
Optional JS rendering:
1let fox_url = format!(2"https://www.foxscrape.com/api/v1?api_key={}&url={}&render_js=true",3api_key, target_url4);
✅ Why FoxScrape helps:
💾 6. Exporting Results
Rust makes it simple to save scraped data to JSON or CSV:
1use std::fs::File;2use serde_json::to_writer_pretty;34let file = File::create("top_movies.json")?;5to_writer_pretty(file, &titles)?;
Now your data is ready for analysis or further processing.
⚖️ 7. Comparing Methods in Rust
| Method | JS Support | Anti-Bot Handling | Concurrency | Complexity |
|---|---|---|---|---|
| Blocking IO + scraper | ❌ | ❌ | ⚡ Low | 🟢 Simple |
| Async + reqwest + scraper | ⚠️ Partial | ⚠️ Partial | ⚡⚡ High | 🟡 Medium |
| FoxScrape API | ✅ | ✅ Automatic | ⚡⚡ High | 🟢 Simple |
🧠 8. Best Practices
robots.txt and rate limits🎯 9. Conclusion
In this tutorial, you learned:
reqwest::blocking for small tasksscraperRust provides high performance, safety, and concurrency. Adding FoxScrape removes the complexity of dealing with dynamic pages, anti-bot blocks, and proxies — letting you focus on your parsing logic and data extraction.
🦊 Try FoxScrape with Rust:
1let api_key = "YOUR_API_KEY";2let url = "https://www.imdb.com/chart/top/";3let fox_url = format!("https://www.foxscrape.com/api/v1?api_key={}&url={}", api_key, url);
Fetch any page, static or dynamic, and parse it with your existing Rust code — simple, fast, and reliable.
Further Reading

Web Scraping with Ruby
Web scraping is one of those quiet superpowers every developer eventually picks up. Whether you’re building a price tracker, collecting research da...

Web Scraping with Scala
Web scraping sits at the intersection of curiosity and automation. It’s what happens when developers stop copying data manually and start thinking:...

Web Scraping with C#
If you’ve ever tried scraping a modern website, you’ve probably experienced a full emotional arc: excitement, frustration, triumph, and then despai...