Here's a quick 'n' dirty way to dump your new-fangled post analytics to a CSV using Rust. You have to save the page source to src/page.html . Y'know, for graphs and stuff. Who doesn't like graphs?

This ain't polished - It was my "one-hour-before-my-day-job-starts" project today. Snag the regex for your own real version, or improve this one and show me!



extern crate chrono ; extern crate csv ; #[macro_use] extern crate lazy_static ; extern crate regex ; extern crate select ; extern crate serde ; #[macro_use] extern crate serde_derive ; use chrono :: prelude :: * ; use regex :: Regex ; use select ::{ document :: Document , predicate ::{ Class , Name }, }; use std ::{ error :: Error , fs ::{ File , OpenOptions }, }; lazy_static! { static ref NOW : DateTime < Local > = Local :: now (); static ref STAT_RE : Regex = Regex :: new ( ".+?([0-9]+).+//.?([0-9]+).+//.?([0-9]+).+" ) .unwrap (); } #[derive(Debug, Serialize)] struct Record { time : String , title : String , views : i32 , reactions : i32 , comments : i32 , } impl Record { fn new ( time : String , title : String , views : i32 , reactions : i32 , comments : i32 ) -> Self { Self { time , title , views , reactions , comments , } } } fn write_entries ( rs : Vec < Record > , f : File ) -> Result < (), Box < Error >> { let mut wtr = csv :: Writer :: from_writer ( f ); for r in rs { wtr .serialize ( r ) ? ; } wtr .flush () ? ; Ok (()) } fn scrape_page ( doc : & Document ) -> Result < Vec < Record > , Box < Error >> { let mut ret = Vec :: new (); for node in doc .find ( Class ( "dashboard-pageviews-indicator" )) { let text = node .text (); if STAT_RE .is_match ( & text ) { let title = node .parent () .unwrap () .parent () .unwrap () .find ( Name ( "a" )) .next () .unwrap () .find ( Name ( "h2" )) .next () .unwrap () .text (); for cap in STAT_RE .captures_iter ( & text ) { let r = Record :: new ( NOW .to_rfc2822 (), title .clone (), cap [ 1 ] .parse :: < i32 > () ? , cap [ 2 ] .parse :: < i32 > () ? , cap [ 3 ] .parse :: < i32 > () ? , ); ret .push ( r ); } } } Ok ( ret ) } fn run () -> Result < (), Box < Error >> { let doc = Document :: from ( include_str! ( "page.html" )); let file = OpenOptions :: new () .write ( true ) .create ( true ) .append ( true ) .open ( "stats.csv" ) ? ; let entries = scrape_page ( & doc ) ? ; write_entries ( entries , file ) ? ; Ok (()) } fn main () { if let Err ( e ) = run () { eprintln! ( "Error: {}" , e ); :: std :: process :: exit ( 1 ); } }

edit finished off the error handling