Advertisement
Guest User

Untitled

a guest
Mar 3rd, 2020
378
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 5.80 KB | None | 0 0
  1. #[macro_use]
  2. extern crate lazy_static;
  3. extern crate clap;
  4. extern crate queues;
  5. extern crate regex;
  6. extern crate reqwest;
  7. extern crate select;
  8.  
  9. use clap::{App, Arg};
  10. use queues::*;
  11. use regex::Regex;
  12. use reqwest::Url;
  13. use select::document::Document;
  14. use select::predicate::Name;
  15. use std::collections::HashSet;
  16. use std::result::Result;
  17. use std::sync::{Arc, Mutex};
  18. use std::thread;
  19. use tokio::prelude::*;
  20.  
  21. #[derive(Clone)]
  22. struct Crawler {
  23.     client: reqwest::Client,
  24.     target: String,
  25.     visited: Arc<Mutex<HashSet<String>>>,
  26.     queue: Arc<Mutex<Vec<String>>>,
  27.     base_url: String,
  28.     fetch_any_domain: bool,
  29.     workers: u8,
  30. }
  31.  
  32. impl Crawler {
  33.     fn new(target: &String, any_domain: bool, workers: u8) -> Crawler {
  34.         let client = reqwest::Client::new();
  35.         let visited = Arc::new(Mutex::new(HashSet::new()));
  36.         let queue = Arc::new(Mutex::new(Vec::new()));
  37.         let burl: String;
  38.  
  39.         match Url::parse(target.as_str()) {
  40.             Ok(url) => {
  41.                 if let Some(base_url) = url.host_str() {
  42.                     burl = String::from(base_url);
  43.                 } else {
  44.                     panic!("Unable to extract base url from target link {}", target);
  45.                 }
  46.             }
  47.             Err(e) => panic!("{}", e),
  48.         }
  49.  
  50.         Crawler {
  51.             client: client,
  52.             target: target.to_string(),
  53.             visited: visited,
  54.             queue: queue,
  55.             base_url: burl,
  56.             fetch_any_domain: any_domain,
  57.             workers: workers,
  58.         }
  59.     }
  60.  
  61.     #[tokio::main]
  62.     async fn fetch(&self, target: &str) -> Result<String, reqwest::Error> {
  63.         let body = self.client.get(target).send().await?.text().await?;
  64.         Ok(body)
  65.     }
  66.  
  67.     fn should_fetch(&self, link: &str) -> bool {
  68.         if self.fetch_any_domain {
  69.             return true;
  70.         }
  71.         self.is_same_domain(link)
  72.     }
  73.  
  74.     fn is_same_domain(&self, link: &str) -> bool {
  75.         match Url::parse(link) {
  76.             Ok(url) => {
  77.                 if let Some(host) = url.host_str() {
  78.                     println!("checking {} base url {} with {}", link, host, self.base_url);
  79.                     return host == self.base_url;
  80.                 } else {
  81.                     false
  82.                 }
  83.             }
  84.             Err(e) => {
  85.                 println!("{}", e);
  86.                 return false;
  87.             }
  88.         }
  89.     }
  90.  
  91.     fn run(&self) {
  92.         {
  93.             self.queue
  94.                 .lock()
  95.                 .unwrap()
  96.                 .push(self.convert_link_to_abs(self.target.as_str()))
  97.         }
  98.  
  99.         while self.queue.lock().unwrap().len() > 0 {
  100.             let x = self.queue.lock().unwrap().pop();
  101.             match x {
  102.                 Some(link) => match self.fetch(&link) {
  103.                     Ok(content) => match self.get_links(content) {
  104.                         Ok(()) => println!("added new links"),
  105.                         Err(e) => println!("{}", e),
  106.                     },
  107.                     Err(e) => println!("{}", e),
  108.                 },
  109.                 _ => {}
  110.             }
  111.         }
  112.     }
  113.  
  114.     fn convert_link_to_abs(&self, link: &str) -> String {
  115.         lazy_static! {
  116.             static ref RE: Regex = Regex::new(r"^http[s]*://.*").unwrap();
  117.         }
  118.         if !RE.is_match(link) {
  119.             let mut s = self.base_url.to_owned();
  120.             s.push_str(link);
  121.             return s;
  122.         } else {
  123.             String::from(link)
  124.         }
  125.     }
  126.  
  127.     fn get_links(&self, content: String) -> Result<(), reqwest::Error> {
  128.         Document::from(content.as_str())
  129.             .find(Name("a"))
  130.             .filter_map(|n| n.attr("href"))
  131.             .for_each(|l| {
  132.                 let link = &String::from(l);
  133.                 let full_link = self.convert_link_to_abs(link);
  134.                 if !self.visited.lock().unwrap().contains(&full_link) {
  135.                     if self.should_fetch(&full_link) {
  136.                         println!("should fetch");
  137.                         self.queue.lock().unwrap().push(full_link.to_string());
  138.                         self.visited.lock().unwrap().insert(full_link.to_string());
  139.                     }
  140.                 }
  141.             });
  142.         Ok(())
  143.     }
  144. }
  145.  
  146. fn main() {
  147.     let opts = App::new("Crawler")
  148.         .about("Crawler crawls a website")
  149.         .arg(
  150.             Arg::with_name("target")
  151.                 .help("the target to crawl")
  152.                 .required(true)
  153.                 .short("t")
  154.                 .takes_value(true),
  155.         )
  156.         .arg(Arg::with_name("any").help("fetch any domain").short("a"))
  157.         .arg(
  158.             Arg::with_name("workers")
  159.                 .help("number of workers to spin up")
  160.                 .short("w")
  161.                 .takes_value(true)
  162.                 .default_value("1"),
  163.         );
  164.     let matches = opts.get_matches();
  165.  
  166.     let mut any_domain = false;
  167.     if let Some(_a) = matches.value_of("any") {
  168.         any_domain = true;
  169.     }
  170.  
  171.     let mut workers = 1;
  172.     if let Some(w) = matches.value_of("workers") {
  173.         match w.parse::<u8>() {
  174.             Ok(v) => workers = v,
  175.             Err(_) => println!("unable to parse workers, defaulting to {}", workers),
  176.         }
  177.     }
  178.  
  179.     if let Some(t) = matches.value_of("target") {
  180.         let crawler: Crawler = Crawler::new(&t.to_string(), any_domain, workers);
  181.  
  182.         let mut threads = vec![];
  183.         let c = Arc::new(Mutex::new(crawler));
  184.         for _i in 0..workers {
  185.             let cc = c.clone();
  186.             threads.push(thread::spawn(move || {
  187.                 let guard = cc.lock().unwrap();
  188.                 guard.run();
  189.             }));
  190.         }
  191.  
  192.         for t in threads {
  193.             let _ = t.join();
  194.         }
  195.     } else {
  196.         println!("unable to parse target");
  197.     }
  198. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement