1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 6778字,需瀏覽 14分鐘

         ·

        2024-03-30 15:30

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

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

        本文的依賴如下

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

        快速入門

        爬蟲的任務(wù)總結(jié)起來并不復(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)頁內(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!("倉庫鏈接: {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(())
        }

        輸出如下:

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

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

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

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

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

              
              export http_proxy=http://127.0.0.1:18080

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

        內(nèi)容解析

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

        定位HTML頁面的各個(gè)元素一般有兩種語法,XPath和CSS選擇器, 我比較喜歡后者,本文涉及的第三方庫scraper也支持這個(gè)語法,CSS選擇器的語法可參考這個(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)迭代器的方法訪問其中的元素或者使用for循環(huán)依次遍歷。

        獲取元素屬性

        常見的屬性有classhref, 總的來說,除了元素名, 包裹的文本或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ù)的加密措施而非解析頁面然后獲取數(shù)據(jù),由于本人對(duì)反反爬的技術(shù)不是太精通,所以這里只是簡單的介紹了一下相關(guān)第三方庫, 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)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产最爽的乱婬视频国语对白 | 中国女人18一20岁毛片 | 午夜性| 国产suv精品一区二区883 | 性生活片免费看 | av大鸡吧 | 妹操屄| 影音先锋美女自拍 | 俺去俺来也www色官网cms | 天天躁日日躁狠狠躁伊人 |