Ajout de classe utilitaires

This commit is contained in:
Rémi BERTHO 2020-05-11 21:21:57 +02:00
parent c13027724f
commit 2a6c0bf432
Signed by: dalan
GPG key ID: EE3B917931C07B64
8 changed files with 1541 additions and 8 deletions

1396
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -7,3 +7,6 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
reqwest = { version = "0.10", features = ["blocking"]}
scraper = "0.12.0"
arraygen = "0.1.11"

15
TODO.md
View file

@ -1,10 +1,11 @@
# TODO # TODO
* [ ] Récupération prix darty * [ ] Récupération prix darty avec [scraper](https://crates.io/crates/scraper) et [reqwest](https://crates.io/crates/reqwest)
* [ ] Récupération URL ligne de commande * [ ] Ajout du support de la Fnac, …
* [ ] Lecture des URLs depuis un fichier * [ ] Récupération URL ligne de commande avec [clap](https://crates.io/crates/clap)
* [ ] Parallélisation des requêtes * [ ] Lecture des URLs depuis un fichier avec [toml](https://crates.io/crates/toml)
* [ ] Parallélisation des requêtes avce [rayon](rayon)
* [ ] Écriture dans un fichier ODS avec [calamine](https://crates.io/crates/calamine) * [ ] Écriture dans un fichier ODS avec [calamine](https://crates.io/crates/calamine)
* [ ] Notification si baisse de prix * [ ] Notification si baisse de prix avec [notify-rust](https://crates.io/crates/notify-rust)
* [ ] Mail si baisse de prix * [ ] Mail si baisse de prix avec [lettre](https://crates.io/crates/lettre)
* [ ] Lancement de la rechrche régulière * [ ] Lancement de la recherche régulière avec [Clokwerk](https://crates.io/crates/clokwerk)

View file

@ -1,3 +1,16 @@
pub mod price_result;
pub mod parser;
pub mod price_checker;
extern crate reqwest;
extern crate scraper;
use price_result::PriceResult;
use price_checker::PriceChecker;
fn main() { fn main() {
println!("Hello, world!"); let price_checker = PriceChecker::new();
let price_result = price_checker.get_price("https://www.darty.com/nav/achat/gros_electromenager/refrigerateur-congelateur-refrigerateur-cong/refrigerateur-congelateur_bas/samsung_rb33n300nsa_ef.html");
println!("Product «{}: {}» price {}", price_result.product, price_result.name, price_result.price);
} }

28
src/parser.rs Normal file
View file

@ -0,0 +1,28 @@
pub mod darty;
extern crate arraygen;
use scraper::Html;
use crate::price_result::PriceResult;
use arraygen::Arraygen;
pub trait Parser{
fn new() -> Self where Self :Sized;
fn can_parse(&self, url : &str) -> bool;
fn parse(&self, html : &Html) -> PriceResult;
}
#[derive(Arraygen, Debug)]
#[gen_array(pub fn get: & dyn Parser)]
pub struct List {
#[in_array(get)]
darty: darty::Darty
}
impl List {
pub fn new() -> Self {
List {
darty: darty::Darty::new()
}
}
}

47
src/parser/darty.rs Normal file
View file

@ -0,0 +1,47 @@
use super::Parser;
use crate::PriceResult;
use scraper::{Selector, Html};
#[derive(Debug)]
pub struct Darty {
price_selector: Selector,
name_selector: Selector,
product_selector: Selector
}
impl Parser for Darty {
fn new() -> Self {
Darty {
price_selector: Selector::parse(r#".darty_prix"#).unwrap(),
name_selector: Selector::parse(r#".product_name"#).unwrap(),
product_selector: Selector::parse(r#".product_family"#).unwrap()
}
}
fn can_parse(&self, url : &str) -> bool {
url.contains("darty")
}
fn parse(&self, html : &Html) -> PriceResult {
// Get price
let price_element = html.select(&self.price_selector).next().unwrap();
let mut price_text_it = price_element.text();
let price_ent : u32 = price_text_it.next().unwrap_or("0").trim_end_matches(',').parse().unwrap();
let price_dec : u32 = price_text_it.next().unwrap_or("0").trim_end_matches('€').parse().unwrap();
let price = price_ent as f64 + (price_dec as f64) / 100.;
// Get name
let name_element = html.select(&self.name_selector).next().unwrap();
let name = name_element.text().next().unwrap().trim().replace('\n', "-");
// Get product
let family_element = html.select(&self.product_selector).next().unwrap();
let family = family_element.text().next().unwrap().trim().replace('\n', "-");
PriceResult {
name: name.to_owned(),
product: family.to_owned(),
price
}
}
}

39
src/price_checker.rs Normal file
View file

@ -0,0 +1,39 @@
extern crate reqwest;
use reqwest::blocking::Client;
use scraper::Html;
use crate::parser;
use crate::price_result::PriceResult;
const USER_AGENT: &str = "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0";
pub struct PriceChecker {
client: Client,
parser_list: parser::List
}
impl PriceChecker {
pub fn new() -> Self {
let client = reqwest::blocking::Client::builder().user_agent(USER_AGENT).build().unwrap();
PriceChecker {
client,
parser_list: parser::List::new()
}
}
pub fn get_price(&self, url : &str) -> PriceResult {
let response = self.client.get(url).send().unwrap();
let text = response.text().unwrap();
let document = Html::parse_document(&text);
for parser in self.parser_list.get().iter() {
if parser.can_parse(url) {
return parser.parse(&document);
}
}
PriceResult {
name: "name".to_owned(),
product: "family".to_owned(),
price: 0.
}
}
}

6
src/price_result.rs Normal file
View file

@ -0,0 +1,6 @@
#[derive(PartialEq, Clone, Debug)]
pub struct PriceResult {
pub name: String,
pub product: String,
pub price: f64
}