use reqwest::blocking::Client; use scraper::{Html, Selector}; use serde::Serialize; use std::{collections::HashSet, fs}; const BASE: &str = "https://letterboxd.com"; const LIST: &str = "https://letterboxd.com/mimuuu/list/2026/by/reverse/"; #[derive(Serialize)] struct Film { title: String, year: String, director: String, url: String, } fn main() -> Result<(), Box> { let client = Client::builder().user_agent("Mozilla/5.0").build()?; // Get latest 5 film URLs let html = client.get(LIST).send()?.text()?; let doc = Html::parse_document(&html); let poster_sel = Selector::parse("li.posteritem div.react-component")?; let mut urls = Vec::new(); let mut seen = HashSet::new(); for node in doc.select(&poster_sel) { if let Some(path) = node.value().attr("data-item-link") { let url = format!("{BASE}{path}"); if seen.insert(url.clone()) { urls.push(url); } } if urls.len() == 5 { break; } } let mut films = Vec::new(); let original_title_sel = Selector::parse(".productioninfo .quoted-creative-work-title")?; let title_sel = Selector::parse(".details .primaryname .name")?; let year_sel = Selector::parse(".productioninfo .releasedate a")?; let directors_sel = Selector::parse(".credits .contributorlist")?; let a_sel = Selector::parse("a")?; for url in urls { let html = client.get(&url).send()?.text()?; let doc = Html::parse_document(&html); let mut title = doc .select(&original_title_sel) .next() .map(|node| node.text().collect::().trim().to_string()) .unwrap_or_default(); if title.is_empty() { title = doc .select(&title_sel) .next() .map(|node| node.text().collect::().trim().to_string()) .unwrap_or_default(); } let year = doc .select(&year_sel) .next() .map(|node| node.text().collect::().trim().to_string()) .unwrap_or_default(); let director = doc .select(&directors_sel) .flat_map(|node| node.select(&a_sel)) .map(|a| a.text().collect::().trim().to_string()) .collect::>() .join(", "); films.push(Film { title, year, director, url, }); } fs::create_dir_all("data")?; fs::write("data/films.json", serde_json::to_string_pretty(&films)?)?; println!("Saved {} films", films.len()); Ok(()) }