/* html.duckduckgo.com google.com */ use std::fs; use reqwest::{header::HeaderMap, Client, Method}; use rocket::{form::Form, fs::FileServer, post, routes, FromForm}; use rocket_dyn_templates::{context, Template}; use scraper::{Html, Selector}; use serde::Serialize; #[derive(Serialize)] struct SearchResult { a: String, desc: String, title: String, } #[derive(FromForm)] struct Query { query: String } async fn google(client: &Client, search: &str) -> Vec<SearchResult> { todo!() } async fn ddg(client: &Client, search: &str) -> Vec<SearchResult> { // TODO url encode search let mut headers = HeaderMap::new(); headers.insert("content-type", "application/x-www-form-urlencoded".parse().unwrap()); headers.insert("Accept-Language", "en-US,en;q=0.9".parse().unwrap()); headers.insert("Referer", "https://html.duckduckgo.com/".parse().unwrap()); let request = client .request(Method::POST, "https://html.duckduckgo.com/html/") .headers(headers) .body(format!("q={}", search)) .build() .unwrap(); let ddg_dom = client.execute(request) .await .unwrap() .text() .await .unwrap(); fs::write("ddg-debug.html", &ddg_dom).unwrap(); let html = Html::parse_document(&ddg_dom); let search_result = Selector::parse("#links > .results_links > .result__body ").unwrap(); let bot_detected = Selector::parse("anomaly-modal__mask").unwrap(); let count = html.select(&bot_detected).into_iter().count(); if count > 0 { // we've been found out! println!("bot"); } let mut buf = Vec::new(); for result in html.select(&search_result) { let title_selector = Selector::parse("h2 > a").unwrap(); let title = result.select(&title_selector).next().expect("Failed to get title"); let preview_selector = Selector::parse(".result__snippet").unwrap(); let preview = result.select(&preview_selector).next().expect("Failed to get title"); let a = title.attr("href").unwrap().to_string(); let title = title.inner_html(); let desc = preview.inner_html(); let sr = SearchResult { a, title, desc }; buf.push(sr); } buf } #[post("/", data="<input>")] async fn search(input: Form<Query>) -> Template { let clean = &input.query.trim(); let client = reqwest::ClientBuilder::new() .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36") .gzip(true) .build() .unwrap(); Template::render("results", context! { ddg: ddg(&client, clean).await, last_search: clean }) } #[rocket::main] async fn main() { rocket::build() .mount("/", routes![search]) .mount("/", FileServer::from("www")) .attach(Template::fairing()) .launch() .await .unwrap(); }