# Requests2 Requests 它可以快速编写http请求,并能使用闭包风格解析页面 - 闭包风格的解析器parser,可以扩展自己的解析的方法 - 并发请求支持 - 一个缓存器,它将解析后的结果存储到HashMap中 ```rust let data = Cache::new(); let client = Requests::new(&data); let mut rq = client.connect("https://www.qq.com/", Headers::Default); rq.parser(|p| { p.find_all("a", |x| { x.attr("href").map_or(false, |v| v.starts_with("http")) }, "href") }, "href"); // data.print() ``` 使用cache可以初始化一个存储器,将用它来初始化一个requests,当准备好就可以发送一个url请求 ,自定义一个headres,它可以使用是Default默认的带一个useragent的字段,也可以使用json来自定义 ```rust let headers = r#"{"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36", "host": "www.qq.com"}"#; let store = Cache::new(); let client = Requests::new(&store); let mut p = client.connect("https://www.qq.com", Headers::JSON(headers)); ``` 需要注意的是,headers目前只可以初始化两个字段useragent host,如果需要更多的,可以在 `headers.rs `中进行添加 ## Parser 当使用connect获取页面结果后,可以使用parser来进行解析工作,parser接受的第一个参数是一个闭包, 它将有一系列method来解析dom元素,比如 **find** **find_all** **select** ```rust rq.parser(|p| { p.find_all("a", |x| { x.attr("href").map_or(false, |v| v.starts_with("http")) }, "href") }, "href") ``` 找到页面所有的超链接并存储到href的键值中 - output ```rust Key -- "href" Value -- LIST(["https://qzone.qq.com", "https://qzone.qq.com", "https://mail.qq.com", "https://mail.qq.com/cgi-bin/loginpage", "http://news.qq.com/", "https://v.qq.com/?isoldly=1", "http://gongyi.qq.com/",...] ``` 它的值是一个自定义的枚举类型Value 如果需要自定义返回的Value,这通常在需要处理复杂的解析工作上用。可以使用select方法, 例子如下 ```rust let data = Cache::new(); let client = Requests::new(&data); let mut parser = client.connect("https://www.qq.com", Headers::Default); parser.parser(|p| { let mut result = HashMap::new(); let navs = p.select("li.nav-item", |nodes| { let navs = nodes.into_iter().map(|n| { let mut item = HashMap::new(); n.find(Name("a")).next().map_or(HashMap::from([("".to_string(), Value::NULL)]), |a| { let nav_name = a.text(); let nav_href = a.attr("href").map_or(String::from(""), |x| x.to_string()); item.insert("nav_name".to_string(), Value::STR(nav_name)); item.insert("nav_href".to_string(), Value::STR(nav_href)); item }) }).collect::>>(); Value::VECMAP(navs) }); let news = p.select("ul.yw-list", |nodes| { let mut news = Vec::new(); for node in nodes { for n in node.find(Class("news-top")) { for a in n.find(Name("a")) { let title = a.text(); news.push(title); } } } Value::LIST(news) }); result.insert("titles".to_owned(), news); result.insert("nav".to_owned(), navs); Value::MAP(result) }, "index"); data.print(); ``` ## 并发支持 使用 **rayon** 并发请求 ```rust let data = Cache::new(); let client = Requests::new(&data); let urls = ["https://www.baidu.com", "https://www.qq.com", "https://www.163.com"]; let _ = urls.par_iter().map(|url| { let mut p = client.connect(url, Headers::Default); p.parser(|p| { p.find_all("a", |f| f.attr("href").map_or(false, |v| v.starts_with("http://")), "href") }, format!("{}_link", url).as_str()); p.parser(|p| { p.find("title", |f| f.text() != "", "text") }, format!("{}_title", url).as_str()); }) .map(|_| String::from("")).collect::(); match data.get("https://www.qq.com_title") { Value::STR(i) => assert_eq!(i, "腾讯首页"), _ => panic!("") }; if let Value::STR(i) = data.get("https://www.163.com_title") { assert_eq!(i, "网易"); } if let Value::STR(i) = data.get("https://www.baidu.com_title") { assert_eq!(i, "百度一下,你就知道"); } ```