<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Rust小項(xiàng)目: 寫一個(gè)簡(jiǎn)單的網(wǎng)頁(yè)爬蟲

          共 6778字,需瀏覽 14分鐘

           ·

          2024-03-30 15:30

          本文主要是對(duì)之前reqwest庫(kù)的一個(gè)簡(jiǎn)單的擴(kuò)展,通過(guò)寫一個(gè)簡(jiǎn)單的爬蟲項(xiàng)目來(lái)練習(xí)練習(xí)Rust, 爬蟲有用也有趣,但是不要給目標(biāo)網(wǎng)站造成過(guò)大的壓力,否則可能會(huì)觸犯法律,切記切記。

          本文的爬蟲任務(wù)是獲取GitHub的Trending相關(guān)數(shù)據(jù), 獲取當(dāng)日列表的倉(cāng)庫(kù)名,倉(cāng)庫(kù)Star數(shù),今日Star數(shù)。

          本文的依賴如下

                
                [dependencies]
          reqwest = "0.11.23"
          scraper = "0.18.1"
          tokio = { version = "1.35.1", features = ["full"] }

          快速入門

          爬蟲的任務(wù)總結(jié)起來(lái)并不復(fù)雜,獲取數(shù)據(jù),解析數(shù)據(jù),示例代碼如下:

          有反爬機(jī)制的網(wǎng)站獲取數(shù)據(jù)會(huì)很難的,需要耗費(fèi)很多精力破解相關(guān)機(jī)制的。。。

                
                use reqwest::Result;
          use scraper::{Html, Selector};


          #[tokio::main]
          async fn main() -> Result<()>{
              let target_url = "https://github.com/trending/rust";
           // 獲取數(shù)據(jù)
              let body = reqwest::get(target_url)
              .await
              .expect("請(qǐng)求地址失敗")
              .text()
              .await
              .expect("解析網(wǎng)頁(yè)內(nèi)容失敗");
              
           // 解析數(shù)據(jù)
              let document = Html::parse_document(body.as_str());
              // 我為啥知道是Box-row, h2 a之類的路徑? 因?yàn)槭謩?dòng)獲取的呀^_^
              let rows_selector = Selector::parse(".Box-row").unwrap();
              let repo_link_selector = Selector::parse("h2 a").unwrap();
              let repo_today_star_selector: Selector = Selector::parse("span.d-inline-block.float-sm-right").unwrap();
              let repo_total_star_selector = Selector::parse("a.Link.Link--muted.d-inline-block.mr-3").unwrap();

             // 以每行作為后續(xù)的解析入口
              for row in document.select(&rows_selector) {
                  if let Some(repo_link) = row.select(&repo_link_selector).nth(0) {
                      if  let Some(href) = repo_link.value().attr("href") {
                          print!("倉(cāng)庫(kù)鏈接: {href} ")
                      }
                  }

                  if let Some(today_star) = row.select(&repo_today_star_selector).nth(0) {
                      let texts: Vec<_> = today_star.text().collect();
                      let text = texts.join("").split_whitespace().nth(0).expect("獲取今日star數(shù)失敗").to_string();
                      let text = text.replace(",""");
                      print!("今日star數(shù): {text} ")
                  }

                  if let Some(total_star) = row.select(&repo_total_star_selector).nth(0) {
                      let texts: Vec<_> = total_star.text().collect();
                      let text = texts.join("").split_whitespace().nth(0).expect("獲取總star數(shù)失敗").to_string();
                      let text = text.replace(",""");
                      print!("總star數(shù): {text}")
                  }
                  println!("")
              }

              Ok(())
          }

          輸出如下:

                
                倉(cāng)庫(kù)鏈接: /llenotre/maestro 今日star數(shù): 232 總star數(shù): 2187
          倉(cāng)庫(kù)鏈接: /rustls/rustls 今日star數(shù): 20 總star數(shù): 5077
          倉(cāng)庫(kù)鏈接: /aptos-labs/aptos-core 今日star數(shù): 1 總star數(shù): 5599
          倉(cāng)庫(kù)鏈接: /microsoft/windows-rs 今日star數(shù): 15 總star數(shù): 9291
          ....省略其他....

          如果上面的代碼沒(méi)有輸出了,可能是Github換前端樣式了,爬蟲運(yùn)行一段時(shí)間不生效是很正常的事情。

          獲取數(shù)據(jù)

          如果只是獲取沒(méi)有太復(fù)雜的反爬機(jī)制的網(wǎng)頁(yè)還是很簡(jiǎn)單的,通過(guò)http客戶端構(gòu)造一個(gè)請(qǐng)求就能獲得網(wǎng)頁(yè)內(nèi)容了,反爬機(jī)制有很多,反反爬機(jī)制也有很多,這里簡(jiǎn)單說(shuō)一個(gè),代理。

          最簡(jiǎn)單的辦法就是在命令行設(shè)置http_proxy或者h(yuǎn)ttps_proxy變量,這個(gè)變量對(duì)于大多數(shù)http庫(kù)有效,包括reqwest。

                
                export http_proxy=http://127.0.0.1:18080

          至于代理從何而來(lái),可以去爬提供代理的網(wǎng)頁(yè)呀^_^這里就不展開(kāi)了,爬代理池主要注意的是定時(shí)檢查代理是否還有效。

          內(nèi)容解析

          由程序生成和閱讀的數(shù)據(jù)總是是格式化的數(shù)據(jù),所以一定有固定的格式,網(wǎng)頁(yè)也不例外,網(wǎng)頁(yè)一般是HTML(也有直接返回txt格式的網(wǎng)頁(yè))。

          定位HTML頁(yè)面的各個(gè)元素一般有兩種語(yǔ)法,XPath和CSS選擇器, 我比較喜歡后者,本文涉及的第三方庫(kù)scraper也支持這個(gè)語(yǔ)法,CSS選擇器的語(yǔ)法可參考這個(gè)鏈接: https://www.runoob.com/cssref/css-selectors.html

          下面是官方的幾個(gè)示例。

          獲取列表元素

                
                use scraper::{Html, Selector};

          let html = r#"
              <ul>
                  <li>Foo</li>
                  <li>Bar</li>
                  <li>Baz</li>
              </ul>
          "#
          ;

          let fragment = Html::parse_fragment(html);
          let selector = Selector::parse("li").unwrap();

          for element in fragment.select(&selector) {
              assert_eq!("li", element.value().name());
          }

          select總是返回一個(gè)迭代器,所以需要調(diào)用相關(guān)迭代器的方法訪問(wèn)其中的元素或者使用for循環(huán)依次遍歷。

          獲取元素屬性

          常見(jiàn)的屬性有classhref, 總的來(lái)說(shuō),除了元素名, 包裹的文本或html內(nèi)容之外哪些鍵值對(duì)都是熟悉, 比如這里的name="foo"value="bar"。

                
                use scraper::{Html, Selector};

          let fragment = Html::parse_fragment(r#"<input name="foo" value="bar">"#);
          let selector = Selector::parse(r#"input[name="foo"]"#).unwrap();

          let input = fragment.select(&selector).next().unwrap();
          assert_eq!(Some("bar"), input.value().attr("value"));

          獲取元素文本

          scraper會(huì)遞歸的獲取指定元素的文本以及包含子節(jié)點(diǎn)的所有文本,所以返回一個(gè)列表這并不奇怪。

                
                use scraper::{Html, Selector};

          let fragment = Html::parse_fragment("<h1>Hello, <i>world!</i></h1>");
          let selector = Selector::parse("h1").unwrap();

          let h1 = fragment.select(&selector).next().unwrap();
          let text = h1.text().collect::<Vec<_>>();

          assert_eq!(vec!["Hello, ""world!"], text);

          總結(jié)

          隨著反爬反反爬技術(shù)的不斷碰撞,大多數(shù)爬蟲項(xiàng)目的難點(diǎn)在于破解數(shù)據(jù)的加密措施而非解析頁(yè)面然后獲取數(shù)據(jù),由于本人對(duì)反反爬的技術(shù)不是太精通,所以這里只是簡(jiǎn)單的介紹了一下相關(guān)第三方庫(kù), reqwestscraper

          參考鏈接

          • https://github.com/trending/rust

          • https://docs.rs/scraper/latest/scraper/

          • https://www.runoob.com/cssref/css-selectors.html

          瀏覽 60
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费av网站 | 啪啪AV网站 | 日本一区三级片 | 无码无遮挡 | 久久av在线 |