Ajout de classe utilitaires
This commit is contained in:
parent
c13027724f
commit
2a6c0bf432
8 changed files with 1541 additions and 8 deletions
1396
Cargo.lock
generated
1396
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -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
15
TODO.md
|
@ -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)
|
15
src/main.rs
15
src/main.rs
|
@ -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
28
src/parser.rs
Normal 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
47
src/parser/darty.rs
Normal 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
39
src/price_checker.rs
Normal 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
6
src/price_result.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
|
pub struct PriceResult {
|
||||||
|
pub name: String,
|
||||||
|
pub product: String,
|
||||||
|
pub price: f64
|
||||||
|
}
|
Loading…
Reference in a new issue