איך אני מגרד את Hacker News עם Python (2 שיטות, קוד מלא)

עודכן לאחרונה ב- April 16, 2026
איך אני מגרד את Hacker News עם Python (2 שיטות, קוד מלא)

לפני כמה חודשים רציתי לבנות תקציר יומי של הסיפורים המובילים ב-Hacker News בשביל הצוות שלנו ב-Thunderbit. האינסטינקט הראשון שלי היה פשוט לשמור את האתר במועדפים ולעבור עליו כל בוקר. זה החזיק בערך שלושה ימים, עד שקלטתי שאני מבזבז 20 דקות ביום רק על קריאת כותרות והעתקת קישורים לגיליון אלקטרוני.

Hacker News הוא אחד המקורות הכי עשירים והכי מרוכזים לתובנות טכנולוגיות באינטרנט — בערך 13 מיליון ביקורים חודשיים, כ-1,300 סיפורים חדשים שנשלחים כל יום, ועוד כ-13,000 תגובות שנוספות מדי יום. בין אם אתם עוקבים אחרי מגמות טכנולוגיה חדשות, עוקבים אחרי המותג שלכם, בונים צינור גיוס מתוך שרשורי "Who's Hiring", או פשוט מנסים להישאר בעניינים במה שמעסיק את קהילת המפתחים — לנסות לעקוב אחרי הכול ידנית זה קרב אבוד.

החדשות הטובות: גירוד Hacker News עם Python הוא פשוט באופן מפתיע, ובמדריך הזה אעבור אתכם על שתי שיטות מלאות — גירוד HTML עם BeautifulSoup וה-API הרשמי של HN Firebase — יחד עם עימוד, ייצוא נתונים, דפוסי עבודה מוכנים לפרודקשן, וגם קיצור דרך בלי קוד למקרים שבהם Python מרגיש כמו יותר מדי.

למה בכלל לגרד את Hacker News עם Python?

Hacker News הוא לא עוד אגרגטור קישורים. זהו פיד אוצר, מונע-קהילה, שבו סיפורי הטכנולוגיה הכי מעניינים עולים למעלה בזכות הצבעות ודיון פעיל. הקהל שלו נוטה מאוד לאנשי טכנולוגיה (בערך 76% גברים, קבוצת הגיל העיקרית 25-34), ושיעור התנועה הישירה של האתר, שעומד על 66%, מראה שזה קהל נאמן והרגלי — לא גולשים אקראיים.

הנה כמה סיבות נפוצות שבגללן אנשים מגרדים נתונים מ-HN:

מקרה שימושמה מקבלים
תקציר טכנולוגי יומיסיפורים מובילים, ציונים וקישורים שנשלחים לתיבת הדואר או ל-Slack
ניטור מותג/מתחריםהתראות כשמזכירים את החברה או המוצר שלכם
ניתוח מגמותמעקב אחר אילו טכנולוגיות, שפות או נושאים צוברים תאוצה לאורך זמן
גיוס עובדיםניתוח שרשורי "Who's Hiring" כדי לאתר משרות, סטאקים טכנולוגיים ורמזים לשכר
מחקר תוכןאיתור נושאים חזקים שכדאי לכתוב או לשתף עליהם
ניתוח סנטימנטהערכת דעת הקהילה על מוצרים, השקות או שינויים בתעשייה

חברות בשווי מצטבר של יותר מ-400 מיליארד דולר — Stripe, Dropbox, Airbnb — מייחסות ל-Hacker News פידבק מוקדם וקריטי וגם משתמשים ראשונים. דרו יוסטון העלה את הדמו של Dropbox ל-HN באפריל 2007, הוא הגיע למקום הראשון, ורשימת ההמתנה לגרסת הבטא קפצה מ-5,000 ל-75,000 משתמשים ביום אחד. נתוני HN הם לא רק מעניינים — הם גם בעלי ערך עסקי ממשי.

הנתונים זמינים לציבור, אבל המבנה של האתר הופך איסוף ידני למייגע. אוטומציה עם Python היא הפתרון המעשי.

שתי דרכים לגרד את Hacker News עם Python: מבט כללי

המדריך הזה מכסה שתי גישות מלאות שאפשר להריץ מיד:

  1. גירוד HTML עם requests + BeautifulSoup — שליפה של ה-HTML הגולמי של news.ycombinator.com ופירוק שלו כדי לחלץ נתוני סיפורים. מצוין ללמידת יסודות הגירוד ולהשגת בדיוק מה שמופיע בדף.
  2. ה-API הרשמי של Hacker News Firebase — גישה ישירה ל-endpoints שמחזירים JSON, בלי צורך בניתוח HTML. עדיף לצינורות נתונים אמינים, גישה לתגובות ולנתונים היסטוריים.

הנה השוואה צד-לצד שתעזור לכם לבחור מה מתאים:

קריטריוןגירוד HTML (requests + BS4)HN Firebase APIThunderbit (ללא קוד)
מורכבות ההקמהבינונית (פירוק selectors של HTML)נמוכה (endpoints של JSON)אין (תוסף Chrome בשני קליקים)
עדכניות הנתוניםזמן אמת מהעמוד הראשיזמן אמת (כל פריט לפי ID)זמן אמת
סיכון להגבלת קצבבינוני (robots.txt מציין השהיית crawl של 30 שניות)נמוך (רשמי ונדיב)מנוהל על ידי Thunderbit
גישה לתגובותקשה (HTML מקונן)קל (IDs רקורסיביים)תכונת גירוד תת-עמודים
נתונים היסטורייםמוגבלדרך Algolia Search APIלא רלוונטי
הכי מתאים ללימוד יסודות גירודצינורות נתונים אמיניםלא-מפתחים, ייצוא מהיר

שתי השיטות כוללות קוד Python מלא שאפשר להריץ. ואם כל מה שאתם צריכים זה את הנתונים בלי לכתוב אפילו שורת קוד אחת — גם לזה אגע בהמשך.

לפני שמתחילים

  • רמת קושי: מתחילים עד בינוני
  • זמן נדרש: כ-15–20 דקות לכל שיטה
  • מה תצטרכו:
    • Python 3.11+ מותקן
    • טרמינל או עורך קוד
    • דפדפן Chrome (אם תרצו לבדוק את ה-HTML של HN או לנסות את האפשרות ללא קוד)
    • Thunderbit Chrome Extension (אופציונלי, לשיטה ללא קוד)

scrape-hacker-news-methods.webp

הקמת סביבת Python

לפני שניגש לנתוני HN עצמם, נכין את הסביבה. אני ממליץ ליצור סביבה וירטואלית כדי שהרכיבים של הפרויקט יישארו נקיים ומופרדים.

# Create and activate a virtual environment
python3 -m venv hn-scraper
# macOS/Linux:
source hn-scraper/bin/activate
# Windows:
hn-scraper\Scripts\activate

# Install the packages we'll need for both methods
pip install requests==2.33.1 beautifulsoup4==4.14.3 pandas==3.0.2 openpyxl==3.1.5

לשלבים מתקדמים יותר של עבודה בפרודקשן (Caching, retries), תצטרכו גם:

pip install requests-cache==1.3.1 tenacity==9.1.4

אין צורך במפתחות API מיוחדים, ואין אסימוני אימות. הנתונים של HN פתוחים.

שיטה 1: גירוד Hacker News עם Python באמצעות BeautifulSoup

זו הגישה הקלאסית — שולפים את ה-HTML, מפרקים אותו, ומחלצים את הנתונים שרוצים. כך רוב האנשים לומדים web scraping, והפריסה הפשוטה של HN מבוססת הטבלאות הופכת אותו למגרש אימון מצוין.

שלב 1: שליפת העמוד הראשי של Hacker News

פתחו את העורך שלכם וצרו קובץ בשם scrape_hn_bs4.py. הנה קוד ההתחלה:

import requests
from bs4 import BeautifulSoup

url = "https://news.ycombinator.com/news"
headers = {"User-Agent": "Mozilla/5.0 (educational HN scraper)"}
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, "html.parser")

print(f"Status: {response.status_code}, Page length: {len(response.text)} chars")

הריצו אותו. אתם אמורים לראות Status: 200 ואורך עמוד של בערך 40,000–50,000 תווים. זה ה-HTML הגולמי של עמוד הבית של HN, שמחכה בזיכרון לפירוק.

שלב 2: להבין את מבנה ה-HTML

HN משתמשת בפריסה מבוססת טבלאות — בלי CSS grid או flex מודרניים. כל סיפור בדף מורכב משתי שורות <tr> מרכזיות:

  • שורת הסיפור (<tr class="athing submission">): כוללת את הדירוג, הכותרת והקישור
  • שורת המטא-דאטה (השורה הבאה <tr>): כוללת נקודות, מחבר, זמן ומספר תגובות

ה-selectors החשובים:

  • span.titleline > a — כותרת הסיפור וה-URL
  • span.score — מספר ההצבעות (למשל, "118 points")
  • a.hnuser — שם המשתמש של המחבר
  • span.age — מועד הפרסום
  • ה-<a> האחרון ב-.subtext עם המילה "comment" בטקסט — מספר התגובות

אם תלחצו לחיצה ימנית על כותרת סיפור ב-Chrome ותבחרו "Inspect", תראו משהו כזה:

<span class="titleline">
  <a href="https://darkbloom.dev">Darkbloom – Private inference on idle Macs</a>
</span>

ומתחתיו את שורת המטא-דאטה:

<span class="score" id="score_47788542">118 points</span>
by <a href="user?id=twapi" class="hnuser">twapi</a>
<span class="age" title="2026-04-16T04:06:39 1776312399">
  <a href="item?id=47788542">2 hours ago</a>
</span>
| <a href="item?id=47788542">65&nbsp;comments</a>

הבנת ה-selectors האלה היא קריטית — אם HN תשנה אי פעם את ה-markup שלה, תצטרכו לעדכן אותם. (ספוילר: שיטת ה-API עוקפת את הבעיה הזו לחלוטין.)

שלב 3: חילוץ כותרות, קישורים וציונים

עכשיו לעבודה האמיתית. נעבור על כל שורת סיפור, ניקח את הכותרת והקישור משורת הסיפור, ואז את הציון משורת המטא-דאטה שמיד מתחתיה.

import requests
from bs4 import BeautifulSoup
from pprint import pprint

url = "https://news.ycombinator.com/news"
headers = {"User-Agent": "Mozilla/5.0 (educational HN scraper)"}
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, "html.parser")

stories = []
story_rows = soup.select("tr.athing")

for row in story_rows:
    # Title and URL from the story row
    title_tag = row.select_one("span.titleline > a")
    if not title_tag:
        continue
    title = title_tag.get_text()
    link = title_tag.get("href", "")

    # Metadata from the next sibling row
    meta_row = row.find_next_sibling("tr")
    score = 0
    author = ""
    comments = 0

    if meta_row:
        if score_tag := meta_row.select_one("span.score"):
            score = int(score_tag.get_text().replace(" points", ""))
        if author_tag := meta_row.select_one("a.hnuser"):
            author = author_tag.get_text()
        # Comment count: last <a> with "comment" in text
        for a_tag in meta_row.select("a"):
            text = a_tag.get_text()
            if "comment" in text:
                comments = int(text.split("\xa0")[0])

    stories.append({
        "title": title,
        "url": link,
        "score": score,
        "author": author,
        "comments": comments,
    })

# Filter to stories with 50+ points, sorted by score
top_stories = sorted(
    [s for s in stories if s["score"] >= 50],
    key=lambda x: x["score"],
    reverse=True,
)

pprint(top_stories[:10])

כמה הערות על הקוד:

  • אופרטור ה-walrus (:=) עובד ב-Python 3.8+. הוא מאפשר לנו גם להקצות וגם לבדוק בערך אחד — שימושי במיוחד לאלמנטים אופציונליים כמו span.score שלא קיימים בכל שורה (למשל, בפוסטים של משרות אין ציון).
  • HN משתמשת ב-\xa0 (רווח לא שביר) בין המספר לבין "comments", לכן אנחנו מפצלים על התו הזה.
  • סיפורים שמקשרים לעמודים אחרים ב-HN (כמו פוסטים מסוג "Ask HN") יופיעו עם URLs יחסיים שמתחילים ב-item?id=. אולי תרצו להוסיף להם https://news.ycombinator.com/.

שלב 4: הרצה וראיית התוצאות

שמרו והריצו:

python scrape_hn_bs4.py

תראו פלט בסגנון:

[{'author': 'twapi',
  'comments': 65,
  'score': 118,
  'title': 'Darkbloom – Private inference on idle Macs',
  'url': 'https://darkbloom.dev'},
 {'author': 'sebg',
  'comments': 203,
  'score': 247,
  'title': 'Show HN: I built an open-source Perplexity alternative',
  'url': 'https://github.com/...'},
 ...]

אלה 30 סיפורים מהעמוד הראשון. אבל ל-HN יש מאות סיפורים פעילים בכל רגע נתון. נעסוק בעימוד בסעיף מאוחר יותר.

שיטה 2: גירוד Hacker News עם Python באמצעות ה-API הרשמי

ה-API של HN Firebase הוא הדרך הרשמית לגשת לנתוני Hacker News. בלי אימות, בלי מפתחות API, בלי ניתוח HTML. מקבלים תגובות JSON נקיות. אני משתמש בשיטה הזו לכל דבר שצריך לרוץ בצורה אמינה בפרודקשן.

נקודות קצה עיקריות שכדאי להכיר

כתובת הבסיס היא https://hacker-news.firebaseio.com/v0/. הנה ה-endpoints החשובים:

Endpointמחזירדוגמה
/v0/topstories.jsonמערך של עד 500 IDs של סיפורים מובילים[47788542, 47787901, ...]
/v0/newstories.jsonעד 500 IDs של הסיפורים החדשים ביותראותו פורמט
/v0/beststories.jsonעד 500 IDs של הסיפורים הטובים ביותראותו פורמט
/v0/askstories.jsonעד 200 IDs של סיפורי "Ask HN"אותו פורמט
/v0/showstories.jsonעד 200 IDs של סיפורי "Show HN"אותו פורמט
/v0/jobstories.jsonעד 200 IDs של משרותאותו פורמט
/v0/item/{id}.jsonפירוט מלא לכל פריט (סיפור, תגובה, סקר)אובייקט JSON
/v0/user/{username}.jsonפרופיל משתמשאובייקט JSON
/v0/maxitem.jsonה-ID המקסימלי הנוכחימספר שלם (למשל, 47789427)

פריט סיפור נראה כך:

{
  "by": "twapi",
  "descendants": 65,
  "id": 47788542,
  "kids": [47789171, 47788769, 47788762],
  "score": 118,
  "time": 1776312399,
  "title": "Darkbloom – Private inference on idle Macs",
  "type": "story",
  "url": "https://darkbloom.dev"
}

השדה kids מכיל את ה-IDs של תגובות הבת הישירות. כל תגובה היא בעצמה פריט שעשוי להכיל kids משלה — כך בנוי עץ התגובות.

שלב 1: שליפת IDs של הסיפורים המובילים

צרו קובץ בשם scrape_hn_api.py:

import requests
import time
from pprint import pprint

API_BASE = "https://hacker-news.firebaseio.com/v0"

# Fetch top story IDs
response = requests.get(f"{API_BASE}/topstories.json")
story_ids = response.json()

print(f"Got {len(story_ids)} top story IDs")
# Output: Got 500 top story IDs

500 IDs של סיפורים בבקשה אחת — בלי פירוק, בלי selectors, רק מערך JSON.

שלב 2: שליפת פרטי הסיפורים לפי ID

עכשיו צריך את נתוני הסיפור עצמם. כאן מופיעה בעיית ההתפצלות: 500 סיפורים משמעם 500 קריאות API נפרדות. בבדיקות שביצעתי, כל בקשת item לוקחת בערך 1.2 שניות ברצף. עבור 500 סיפורים, מדובר בכ-10 דקות.

לרוב הצרכים לא צריך את כל 500. הנה קוד לשליפת 30 המובילים:

def fetch_story(story_id):
    """Fetch a single story's details from the HN API."""
    resp = requests.get(f"{API_BASE}/item/{story_id}.json")
    return resp.json()

# Fetch details for the top 30 stories
stories = []
for sid in story_ids[:30]:
    story = fetch_story(sid)
    if story and story.get("type") == "story":
        stories.append({
            "title": story.get("title", ""),
            "url": story.get("url", ""),
            "score": story.get("score", 0),
            "author": story.get("by", ""),
            "comments": story.get("descendants", 0),
            "time": story.get("time", 0),
            "id": story.get("id"),
        })
    time.sleep(0.1)  # Be polite — small delay between requests

# Sort by score, show top 10
top = sorted(stories, key=lambda x: x["score"], reverse=True)[:10]
pprint(top)

ה-time.sleep(0.1) מוסיף השהיה קטנה מתוך נימוס. ל-Firebase API אין מגבלת קצב מוצהרת, אבל אין זה ראוי להציף כל API בלי הפסקות.

שלב 3: גירוד תגובות (מעבר רקורסיבי על העץ)

כאן ה-API באמת בולט לעומת גירוד HTML. תגובות ב-HN מקוננות לעומק — תשובות לתשובות לתשובות. ב-HTML זה אומר פירוק של מבני טבלאות מקוננים ומסובכים. עם ה-API, כל שדה kids של תגובה נותן את IDs הילדים שלה, ופשוט עוברים על העץ רקורסיבית.

def fetch_comments(item_id, depth=0, max_depth=3):
    """Recursively fetch comments up to max_depth."""
    item = requests.get(f"{API_BASE}/item/{item_id}.json").json()
    if not item or item.get("type") != "comment":
        return []

    comments = [{
        "author": item.get("by", "[deleted]"),
        "text": item.get("text", ""),
        "depth": depth,
        "id": item.get("id"),
    }]

    if depth < max_depth and item.get("kids"):
        for kid_id in item["kids"]:
            comments.extend(fetch_comments(kid_id, depth + 1, max_depth))
            time.sleep(0.05)

    return comments

# Example: fetch comments for the top story
if stories:
    top_story = stories[0]
    top_story_full = requests.get(f"{API_BASE}/item/{top_story['id']}.json").json()
    if top_story_full.get("kids"):
        print(f"\nComments for: {top_story['title']}")
        all_comments = []
        for kid_id in top_story_full["kids"][:5]:  # First 5 top-level comments
            all_comments.extend(fetch_comments(kid_id, depth=0, max_depth=2))
            time.sleep(0.1)

        for c in all_comments[:15]:
            indent = "  " * c["depth"]
            preview = c["text"][:80].replace("\n", " ") if c["text"] else "[no text]"
            print(f"{indent}[{c['author']}] {preview}...")

הגישה הרקורסיבית הזו הרבה יותר פשוטה מאשר לנסות לפרק שרשראות תגובות מקוננות ב-HTML. אם אתם צריכים עץ תגובות מלא — ה-API הוא הדרך הנכונה.

שלב 4: הרצה וצפייה בתוצאות

python scrape_hn_api.py

תראו נתוני סיפור מובנים ואחריהם תצוגה מקוננת של תגובות. הנתונים נקיים יותר, הגישה לתגובות פשוטה, ואין חשש שה-scraper שלכם יישבר כי HN שינתה שם של מחלקת CSS.

מעבר לעמוד 1: עימוד ונתונים היסטוריים

רוב מדריכי הגירוד של HN נעצרים בעמוד 1 — 30 סיפורים. זה בסדר להדגמה מהירה, אבל מקרי שימוש אמיתיים לרוב דורשים עומק רב יותר.

גירוד כמה עמודים עם BeautifulSoup

העימוד של HN משתמש בתבנית URL פשוטה: ?p=2, ?p=3 וכן הלאה. כל עמוד מחזיר 30 סיפורים, והאתר מגיש עד בערך עמוד 20 (כלומר, בערך 600 סיפורים בסך הכול). מעבר לזה מתקבלים עמודים ריקים.

import time

def scrape_hn_pages(num_pages=5):
    """Scrape multiple pages of HN front page stories."""
    all_stories = []

    for page in range(1, num_pages + 1):
        url = f"https://news.ycombinator.com/news?p={page}"
        response = requests.get(url, headers=headers)
        soup = BeautifulSoup(response.text, "html.parser")

        story_rows = soup.select("tr.athing")
        if not story_rows:
            print(f"Page {page}: no stories found, stopping.")
            break

        for row in story_rows:
            title_tag = row.select_one("span.titleline > a")
            if not title_tag:
                continue
            meta_row = row.find_next_sibling("tr")
            score = 0
            if meta_row and (score_tag := meta_row.select_one("span.score")):
                score = int(score_tag.get_text().replace(" points", ""))

            all_stories.append({
                "title": title_tag.get_text(),
                "url": title_tag.get("href", ""),
                "score": score,
            })

        print(f"Page {page}: scraped {len(story_rows)} stories")

        # Respect the robots.txt crawl-delay of 30 seconds
        if page < num_pages:
            time.sleep(30)

    return all_stories

stories = scrape_hn_pages(5)
print(f"\nTotal stories scraped: {len(stories)}")

ה-time.sleep(30) חשוב. הקובץ robots.txt של HN מבקש במפורש השהיית crawl של 30 שניות. התעלמות מזה עלולה להוביל להגבלת קצב (HTTP 429) או לחסימה זמנית. חמשת העמודים במרווחים של 30 שניות ייקחו בערך 2.5 דקות — לא מיידי, אבל מכבד.

למי שלא רוצה לנהל קוד עימוד, Thunderbit מטפל בעימוד מבוסס קליקים וגם בגלילה אינסופית באופן אוטומטי. הוא לוחץ על כפתור "More" בתחתית דפי HN בלי צורך בהגדרות.

גרדו את דפי Hacker News בעזרת AI

גישה לנתוני Hacker News היסטוריים עם Algolia API

ה-API של Firebase נותן נתונים נוכחיים. לניתוח היסטורי — "מה היו סיפורי Python המובילים ב-2023?" או "איך השתנתה הסיקור על AI בחמש השנים האחרונות?" — צריך את HN Algolia Search API.

import requests

ALGOLIA_BASE = "https://hn.algolia.com/api/v1"

def search_hn(query, tags="story", page=0, hits_per_page=20):
    """Search HN via Algolia API."""
    params = {
        "query": query,
        "tags": tags,
        "page": page,
        "hitsPerPage": hits_per_page,
    }
    resp = requests.get(f"{ALGOLIA_BASE}/search", params=params)
    return resp.json()

# Example: find Python scraping stories with 10+ points since Jan 2024
results = search_hn(
    query="python scraping",
    tags="story",
)
print(f"Found {results['nbHits']} total results")

for hit in results["hits"][:5]:
    print(f"  [{hit.get('points', 0)} pts] {hit['title']}")

לשאילתות מסוננות לפי תאריך, השתמשו ב-numericFilters:

import calendar, datetime

# Stories since January 1, 2024
start_date = datetime.datetime(2024, 1, 1)
start_ts = int(calendar.timegm(start_date.timetuple()))

params = {
    "query": "python web scraping",
    "tags": "story",
    "numericFilters": f"created_at_i>{start_ts},points>10",
    "hitsPerPage": 50,
}
resp = requests.get(f"{ALGOLIA_BASE}/search_by_date", params=params)
data = resp.json()
print(f"Found {data['nbHits']} stories about Python web scraping since 2024 with >10 points")

ה-Algolia API מהיר (5–9 מילישניות זמן עיבוד שרת), לא דורש מפתח API, ותומך בעימוד עד 500 עמודים. לניתוח היסטורי בכמויות גדולות, זהו הפתרון הכי טוב.

ייצוא נתוני Hacker News שנגרדו ל-CSV, Excel ו-Google Sheets

כל מדריך גירוד של HN שראיתי מסתיים בפלט pprint() בטרמינל. זה מצוין לניפוי שגיאות, אבל אם אתם בונים תקציר יומי או עושים ניתוח מגמות, צריך את הנתונים בקובץ. הנה איך עושים את זה.

ייצוא ל-CSV עם Python

import csv

def export_to_csv(stories, filename="hn_stories.csv"):
    """Save scraped stories to a CSV file."""
    fieldnames = ["title", "url", "score", "author", "comments"]
    with open(filename, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(stories)
    print(f"Saved {len(stories)} stories to {filename}")

export_to_csv(stories)

ייצוא ל-Excel עם Python

import pandas as pd

def export_to_excel(stories, filename="hn_stories.xlsx"):
    """Save scraped stories to an Excel file."""
    df = pd.DataFrame(stories)
    df.to_excel(filename, index=False, engine="openpyxl")
    print(f"Saved {len(stories)} stories to {filename}")

export_to_excel(stories)

ודאו ש-openpyxl מותקן — pandas משתמשת בו כמנוע ל-Excel. אם הוא חסר, תקבלו ImportError.

שליחה ל-Google Sheets (אופציונלי)

לזרימות עבודה אוטומטיות, ייתכן שתרצו להעביר את הנתונים ישירות ל-Google Sheets באמצעות הספרייה gspread. זה דורש הגדרה חד-פעמית של Google Cloud service account:

import gspread

gc = gspread.service_account(filename="service_account.json")
sh = gc.open("HN Daily Digest")
worksheet = sh.sheet1

# Convert stories to rows
header = list(stories[0].keys())
rows = [list(s.values()) for s in stories]

worksheet.clear()
worksheet.update([header] + rows)
print("Pushed to Google Sheets")

חלופה לייצוא בלי קוד

אם הקמה של service account וכתיבת קוד ייצוא נשמעת כמו יותר עבודה מהגירוד עצמו — אני לגמרי מבין. ב-Thunderbit בנינו ייצוא נתונים חינמי שמאפשר לשלוח נתונים שנגרדו ישירות ל-Excel, ל-Google Sheets, ל-Airtable או ל-Notion — בלי קוד, בלי אישורים, בלי צינור תחזוקה. עבור שליפה חד-פעמית של נתונים, זה באמת מהיר יותר. עוד על כך בהמשך.

איך להפוך את הסקרייפר למוכן לפרודקשן: טיפול בשגיאות, Cache ותזמון

אם אתם מריצים סקרייפר פעם אחת בשביל הכיף, הקוד למעלה מספיק. אם אתם מריצים אותו כל יום כחלק מזרימת עבודה, תצטרכו עוד כמה רכיבים.

טיפול בשגיאות ולוגיקת Retry

רשתות נופלות. שרתים מגבילים. בקשה אחת רעה לא צריכה להפיל את כל תהליך הגירוד. הנה פונקציית retry עם backoff מעריכי:

from tenacity import retry, stop_after_attempt, wait_exponential_jitter
import requests

@retry(stop=stop_after_attempt(5), wait=wait_exponential_jitter(initial=1, max=60))
def fetch_with_retry(url):
    """Fetch a URL with automatic retries and exponential backoff."""
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    return response

# Usage:
try:
    resp = fetch_with_retry("https://hacker-news.firebaseio.com/v0/topstories.json")
    story_ids = resp.json()
except Exception as e:
    print(f"Failed after retries: {e}")

הספרייה tenacity מטפלת בלוגיקת retry בצורה נקייה. היא תנסה עד 5 פעמים עם backoff מעריכי ו-jitter — מתחילה בשנייה אחת ומגיעה עד 60 שניות. זה מטפל יפה ב-HTTP 429 (הגבלת קצב), ב-503 (שירות לא זמין), ובשגיאות רשת זמניות.

Cache לתגובות כדי לא לסרוק מחדש

במהלך פיתוח, תריצו את הסקרייפר שוב ושוב בזמן שאתם מכוונים את לוגיקת הפירוק. בלי Cache, כל הרצה פוגעת שוב בשרתי HN עבור אותם נתונים. הספרייה requests-cache פותרת את זה בשתי שורות:

import requests_cache

requests_cache.install_cache("hn_cache", expire_after=3600)  # Cache for 1 hour

אחרי שתוסיפו את השורות האלה לראש הסקריפט, כל הקריאות requests.get() יישמרו אוטומטית ב-SQLite מקומי. תפעילו את הסקריפט 10 פעמים בתוך שעה, ורק ההרצה הראשונה באמת תצא לרשת. זהו כלי שמשתמשי פורומים ממליצים עליו לעיתים קרובות, ובצדק.

הפרדה בין איסוף לפירוק

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

import os, json

def crawl_and_save(story_ids, output_dir="raw_data"):
    """Fetch story data and save raw JSON to disk."""
    os.makedirs(output_dir, exist_ok=True)
    for sid in story_ids:
        filepath = os.path.join(output_dir, f"{sid}.json")
        if os.path.exists(filepath):
            continue  # Skip already-fetched items
        resp = fetch_with_retry(f"{API_BASE}/item/{sid}.json")
        with open(filepath, "w") as f:
            json.dump(resp.json(), f)

def parse_saved_data(input_dir="raw_data"):
    """Parse saved JSON files into structured story list."""
    stories = []
    for filename in os.listdir(input_dir):
        with open(os.path.join(input_dir, filename)) as f:
            item = json.load(f)
        if item and item.get("type") == "story":
            stories.append({
                "title": item.get("title", ""),
                "url": item.get("url", ""),
                "score": item.get("score", 0),
                "author": item.get("by", ""),
                "comments": item.get("descendants", 0),
            })
    return stories

הגישה הזו בשני שלבים מועילה במיוחד כשמגרדים מאות פריטים ורוצים לשפר במהירות את אופן העיבוד של הנתונים.

אוטומציה של הסקרייפר לפי לוח זמנים

לתקציר יומי של HN, צריך שהסקרייפר ירוץ אוטומטית. שתי אפשרויות נפוצות:

אפשרות 1: cron (Linux/Mac)

# Run every day at 8:30 AM UTC
30 8 * * * /usr/bin/python3 /home/user/scrape_hn.py >> /home/user/scrape.log 2>&1

אפשרות 2: GitHub Actions (חינם, בלי שרת)

name: Scrape Hacker News

on:
  schedule:
    - cron: '30 8 * * *'  # Daily at 8:30 AM UTC
  workflow_dispatch:        # Manual trigger button

jobs:
  scrape:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v6
        with:
          python-version: '3.12'
      - run: pip install requests beautifulsoup4 pandas openpyxl
      - run: python scrape_hn.py
      - run: |
          git config user.name "GitHub Actions Bot"
          git config user.email "actions@github.com"
          git add -A
          git diff --staged --quiet || git commit -m "Update HN data $(date -u +%Y-%m-%dT%H:%M:%SZ)"
          git push

כמה מלכודות בתזמון של GitHub Actions: כל זמני cron הם ב-UTC, עיכובים של 15–60 דקות הם דבר נפוץ (כדאי להשתמש בשעות לא עגולות כמו :30 במקום :00), ו-GitHub עשוי להשבית workflows מתוזמנים במאגרים בלי פעילות במשך 60 יום. תמיד כדאי לכלול workflow_dispatch כדי שתוכלו להפעיל ידנית לצורך בדיקה.

לפתרון פשוט יותר, תכונת Scheduled Scraper של Thunderbit מאפשרת לכם לתאר את לוח הזמנים באנגלית פשוטה — משהו כמו "לגרד כל בוקר ב-8" — בלי שרת ובלי הקמת cron.

מתי Python הוא יותר מדי: הדרך ללא קוד לגרד את Hacker News

אהיה כן כאן, גם אם אני אוהב Python והצוות שלי בונה כלי פיתוח. אם כל מה שאתם צריכים זה את 100 הסיפורים המובילים של HN של היום בגיליון אלקטרוני — עכשיו, חד-פעמי — כתיבה, דיבוג והרצה של סקריפט Python הם הוצאה מיותרת. רק ההכנה עצמה (סביבה וירטואלית, התקנת חבילות, הבנת selectors) לוקחת יותר זמן מאיסוף הנתונים עצמו.

כאן Thunderbit נכנס לתמונה. כך נראה התהליך:

  1. פותחים את news.ycombinator.com ב-Chrome
  2. לוחצים על סמל התוסף של Thunderbit ואז על "AI Suggest Fields"
  3. ה-AI קורא את הדף ומציע עמודות: Title, URL, Score, Author, Comment Count, Time Posted
  4. משנים את השדות אם רוצים (שינוי שם, הסרה או הוספת שדות מותאמים — אפשר אפילו להוסיף פרומפט AI כמו "Categorize as AI/DevTools/Web/Other")
  5. לוחצים "Scrape" — הנתונים מופיעים בטבלה מובנית
  6. מייצאים ל-Excel, Google Sheets, Airtable או Notion

שני קליקים לנתונים מובנים. בלי selectors, בלי קוד, בלי תחזוקה.

יתרון ממשי כאן: ה-AI של Thunderbit מסתגל אוטומטית לשינויי פריסה. סקרייפרים קלאסיים שמבוססים על CSS selectors נשברים כשהאתר משנה את ה-markup שלו — ולמרות שה-HTML של HN יציב יחסית, הוא כן השתנה (המחלקה class="athing submission" עודכנה, ו-span.titleline החליף את a.storylink הישן). סקרייפר מבוסס AI קורא את הדף מחדש בכל פעם, כך שהוא לא תלוי בשמות מחלקות.

python-vs-thunderbit-comparison.webp

Thunderbit גם מטפל בעימוד (לחיצה אוטומטית על כפתור "More" של HN) וגם בגירוד תת-עמודים (ביקור בעמוד התגובות של כל סיפור כדי למשוך נתוני דיון). עבור שימוש של העשרת תגובות, זה מקביל לקוד הרקורסיבי שבשיטה 2 — רק בלי לכתוב שורה אחת.

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

Python מול API מול ללא קוד: איזו שיטה כדאי לבחור?

הנה מסגרת ההחלטה המלאה:

קריטריוןBeautifulSoup (HTML)Firebase APIAlgolia APIThunderbit (ללא קוד)
רמת ידע טכנית נדרשתPython בינוניPython למתחיליםPython למתחיליםאין
זמן הקמה10–15 דק'5–10 דק'5–10 דק'2 דק'
עומס תחזוקהבינוני (selectors נשברים)נמוך (JSON יציב)נמוך (JSON יציב)אין
עומק נתוניםעמוד ראשי בלבדכל פריט, משתמשיםחיפוש + היסטוריהעמוד ראשי + תת-עמודים
תגובותקשהקל (רקורסיבי)קל (עץ מקונן)גירוד תת-עמודים
נתונים היסטורייםלאלאכן (ארכיון מלא)לא
אפשרויות ייצואלכתוב לבדלכתוב לבדלכתוב לבדמובנה (Excel, Sheets וכו')
תזמוןcron / GitHub Actionscron / GitHub Actionscron / GitHub Actionsמתזמן מובנה
הכי מתאים ללימוד גירודצינורות אמיניםמחקר וניתוחשליפות מהירות

אם אתם לומדים Python או בונים משהו מותאם אישית, לכו על שיטה 1 או 2. אם אתם צריכים ניתוח היסטורי, הוסיפו את Algolia API. אם אתם רק רוצים את הנתונים בלי הקוד, נסו את Thunderbit.

נסו את Thunderbit לגרידת Hacker News

סיכום ותובנות עיקריות

הנה מה שיש לכם עכשיו בארגז הכלים:

  • שתי שיטות Python מלאות לגרידת Hacker News — BeautifulSoup לניתוח HTML ו-Firebase API לנתוני JSON נקיים
  • טכניקות עימוד לגרידה מעבר לעמוד 1, כולל Algolia API לנתונים היסטוריים מאז 2007
  • קוד ייצוא ל-CSV, Excel ו-Google Sheets — כי נתונים בטרמינל לא מועילים לאף אחד אחר בצוות
  • דפוסי עבודה לפרודקשן — לוגיקת retry, Cache, הפרדה בין איסוף לפירוק, ואוטומציה מתוזמנת דרך cron או GitHub Actions
  • חלופה ללא קוד למקרים שבהם Python הוא יותר כלי ממה שאתם צריכים

ההמלצה שלי: התחילו עם Firebase API (שיטה 2) ברוב מקרי השימוש. הוא נקי יותר, אמין יותר, ומאפשר גישה לתגובות בלי כאב הראש של ניתוח HTML מקונן. הוסיפו את Algolia API כשאתם צריכים נתונים היסטוריים. ושמרו את Thunderbit במועדפים לרגעים שבהם אתם צריכים רק גיליון אלקטרוני מהיר ולא רוצים להרים פרויקט Python שלם.

אם תרצו להעמיק, נסו לגרד תגובות HN לצורך ניתוח סנטימנט, בנו צינור תקציר יומי עם GitHub Actions, או חקרו את Algolia API כדי לעקוב אחרי האופן שבו מגמות טכנולוגיות השתנו בעשור האחרון.

נסו את Thunderbit לגרידת Hacker News מהירה Get Started Free

שאלות נפוצות

האם חוקי לגרד את Hacker News?

הנתונים של HN זמינים לציבור, ו-Y Combinator מספקת API רשמי במיוחד לגישה תכנותית. קובץ ה-robots.txt של האתר מאפשר גירוד של תוכן לקריאה בלבד (העמוד הראשי, עמודי פריטים, עמודי משתמשים), אבל מבקש השהיית crawl של 30 שניות. אם תכבדו את ההשהיה, לא תגרדו נקודות אינטראקטיביות (הצבעה, התחברות), ואתם במצב טוב. למידע נוסף על אתיקה של גירוד, ראו את המדריך שלנו על השלכות משפטיות של web scraping.

האם יש ל-Hacker News API רשמי?

כן. HN Firebase API ב-hacker-news.firebaseio.com/v0/ חינמי, לא דורש אימות, ומספק גישה לסיפורים, תגובות, פרופילי משתמשים, וכל סוגי הפידים (top, new, best, ask, show, jobs). הוא מחזיר JSON נקי ואין מגבלת קצב מוצהרת, אם כי תמיד מומלץ להיות מנומסים עם תדירות הבקשות.

איך מגרדים תגובות Hacker News עם Python?

באמצעות Firebase API, שולפים פריט סיפור כדי לקבל את שדה kids שלו (מערך של IDs לתגובות ברמת העליונה). כל תגובה היא גם פריט עם שדה kids משלה לתשובות. עוברים על העץ רקורסיבית עם פונקציה ששולפת כל תגובה וילדיה. ראו את הסעיף "גירוד תגובות (מעבר רקורסיבי על העץ)" למעלה לקוד מלא. לחלופין, ה-endpoint של Algolia API ב-/items/<id> מחזיר את עץ התגובות המקונן המלא בבקשה אחת — הרבה יותר מהיר לפוסטים עתירי תגובות.

אפשר לגרד את Hacker News בלי לכתוב קוד?

כן. ה-AI web scraper של Thunderbit עובד כתוסף Chrome — פותחים את HN, לוחצים "AI Suggest Fields", והוא מזהה אוטומטית עמודות כמו כותרת, URL, ציון ומחבר. לוחצים "Scrape" ומייצאים ישירות ל-Excel, Google Sheets, Airtable או Notion. הוא מטפל בעימוד ואפילו יכול לבקר בתת-עמודים כדי למשוך נתוני תגובות. בלי Python, בלי selectors, בלי תחזוקה.

איך משיגים נתוני Hacker News היסטוריים?

HN Algolia Search API הוא הכלי הכי טוב לכך. השתמשו ב-endpoint של search_by_date עם numericFilters=created_at_i>TIMESTAMP כדי לסנן לפי טווח תאריכים. אפשר לחפש לפי מילות מפתח, לסנן לפי סוג סיפור, ולעבור עד 500 עמודי תוצאות. לניתוח היסטורי בכמויות גדולות, יש גם מאגרי נתונים ציבוריים ב-Google BigQuery (ארכיון מלא), ClickHouse (28 מיליון רשומות), ו-Hugging Face (4 מיליון סיפורים).

למידע נוסף

Shuai Guan
Shuai Guan
מנכ"ל Thunderbit | מומחה לאוטומציה של נתונים באמצעות AI Shuai Guan הוא המנכ"ל של Thunderbit ובוגר הפקולטה להנדסה של אוניברסיטת מישיגן. עם כמעט עשור של ניסיון בטכנולוגיה ובארכיטקטורת SaaS, הוא מתמחה בהפיכת מודלים מורכבים של AI לכלי חילוץ נתונים פרקטיים, ללא קוד. בבלוג הזה הוא משתף תובנות ישירות מהשטח, שנבחנו בפועל, על Web Scraping ואסטרטגיות אוטומציה שיעזרו לכם לבנות תהליכי עבודה חכמים יותר, מבוססי נתונים. כשאינו משפר תהליכי נתונים, הוא מביא את אותה תשומת לב לפרטים גם לתשוקה שלו לצילום.

נסו את Thunderbit

חלצו לידים ונתונים נוספים ב-2 קליקים בלבד. מופעל בידי AI.

קבל/י את Thunderbit זה בחינם
חלץ/י נתונים בעזרת AI
העבר/י בקלות נתונים ל-Google Sheets, Airtable או Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week