יום 9 - הדפסת הכותרות מ Hacker News ב Rust

04/01/2023

בזמן שאתם קוראים את זה אני כבר על המטוס חזרה לארץ. מקווה שנהניתם מהסידרה ובשביל לסיים אותה אני רוצה לראות איך לקרוא קובץ HTML ב Rust ולהדפיס את הכותרות המובילות מאתר Hacker News.

1. מה אנחנו בונים

את אתר Hacker News אני בטוח שאתם מכירים. היום נראה איך להשתמש ב Rust כדי למשוך את ה HTML הראשי של האתר, לחפש בו את הכתבות ולהדפיס את הכותרות שלהן. דוגמה לקטע HTML מתוך האתר היא:


                <tr class='athing' id='34110987'>
      <td align="right" valign="top" class="title"><font color="#be2828"><span class="rank">5.</span></font></td>      <td valign="top" class="votelinks"><center><a id='up_34110987'href='vote?id=34110987&amp;how=up&amp;goto=news'><div class='votearrow' title='upvote'></div></a></center></td><td class="title"><span class="titleline"><a href="https://pioneerworks.org/broadcast/picture-this-periodic-table">Picture This: The Periodic Table</a><span class="sitebit comhead"> (<a href="from?site=pioneerworks.org"><span class="sitestr">pioneerworks.org</span></a>)</span></span></td></tr><tr><td colspan="2"></td><td class="subtext"><span class="subline">
          <span class="score" id="score_34110987">47 points</span> by <a href="user?id=laurex" class="hnuser">laurex</a> <span class="age" title="2022-12-23T21:50:07"><a href="item?id=34110987">4 hours ago</a></span> <span id="unv_34110987"></span> | <a href="hide?id=34110987&amp;goto=news">hide</a> | <a href="item?id=34110987">12&nbsp;comments</a>        </span>
              </td></tr>

ואנחנו רוצים להוציא מהקטע רק את הכותרת: Picture This: The Periodic Table.

2. קוד הפיתרון

עם הספריות הנכונות עבודה ברשת ב Rust היא ממש פשוטה. אני מתקין את tokio, scraper ו reqwest על ידי הוספת השורות הבאות לקובץ Cargo.toml:

[dependencies]
tokio = { version = "1.23.0", features = ["full"] }
scraper = "0.14.0"
reqwest = "0.11.13"

וממשיך לקוד:

  1. הפונקציה reqwest::get מקבלת URL ומחזירה דף אינטרנט.

  2. הפונקציה Html::parse_document מקבלת טקסט של דף אינטרנט ומחזירה "מסמך"

  3. הפונקציה document.select מקבלת Selector (שיצרנו מטקסט של CSS Selector), ומחזירה את כל האלמנטים שמתאימים לו.

כשמחברים את כל השלושה יחד מקבלים:

use scraper::{Html, Selector};
use std::process;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let resp = match reqwest::get("https://news.ycombinator.com/").await {
        Ok(x) => x,
        Err(_) => {
            println!("{}", "error on /random request");
            process::exit(0x0100);  //exit if error expected during request to /random
        }
    };
    let text = resp.text().await?;

    let document = Html::parse_document(&text);
    let selector = Selector::parse(r#".title .titleline > a"#).unwrap();
    for (index, elem) in document.select(&selector).into_iter().enumerate() {
        for text_node in elem.text() {
            println!("{}. {}", index + 1, text_node);
        }

    }
    Ok(())
}

3. עוד שתי הערות על הקוד

לפני שאני יכול להוציא את כל הלינקים מדף אני צריך לסיים להוריד אותו. ספריית טוקיו היא האחראית על מימוש המודל האסינכרוני ובטוקיו הסטראקט Future מייצג תוצאה של חישוב עתידי. הפונקציה await של Future גורמת לטוקיו לחכות עד שהחישוב יסתיים, ובזמן הזה אפשר להריץ פעולות אסינכרוניות אחרות. אם היו לנו כמה קבצים להוריד היינו יכולים להשתמש בפונקציה join_all של טוקיו כדי לחכות לכולם במקביל.

הערה שניה היא הפונקציה enumerate של איטרטורים שלי מאוד הזכירה את פייתון. היא מוסיפה אינדקס לאיטרציה כדי שאוכל להדפיס אותו ליד הטקסט של הכותרת.

4. תרגילים להרחבה

יום אחרון של ראסט אז אפשר להעלות הילוך - שימו לב שכל כותרת מוגדרת בתוך אלמנט a, והיא בעצם לינק לדף של מאמר מסוים (מאתר אחר). הרחיבו את התוכנית כדי שתכנס לכל אחד מהלינקים עצמם ותבדוק מה ה Title של דף ה HTML באותו קישור. כשאתם מדפיסים הוסיפו את ה Title לשורת ההדפסה של הכותרת.

בשביל המשחק נסו קודם להוריד את הדפים אחד אחד ואחר כך עדכנו את התוכנית כדי שתוריד את כולם במקביל, כדי לראות את פערי הזמנים בין שתי הגישות.

פה אנחנו נפרדים מ Rust. מחר חוזרים למתכונת הבלוג היומי הרגיל. מקווה שנהניתם מהסידרה ושהיא נתנה לכם בסיס להתחיל להשתמש ב Rust מדי פעם גם ביום יום.