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

עודכן לאחרונה ב-April 16, 2026

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

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

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

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

Hacker News הוא לא עוד אגרגטור קישורים. זהו פיד אוצר, מונע-קהילה, שבו סיפורי הטכנולוגיה הכי מעניינים עולים למעלה בזכות הצבעות ודיון פעיל. הקהל שלו נוטה מאוד לאנשי טכנולוגיה (בערך ), ושיעור התנועה הישירה של האתר, שעומד על 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 או לנסות את האפשרות ללא קוד)
    • (אופציונלי, לשיטה ללא קוד)

scrape-hacker-news-methods.webp

הקמת סביבת Python

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

1# Create and activate a virtual environment
2python3 -m venv hn-scraper
3# macOS/Linux:
4source hn-scraper/bin/activate
5# Windows:
6hn-scraper\Scripts\activate
7# Install the packages we'll need for both methods
8pip install requests==2.33.1 beautifulsoup4==4.14.3 pandas==3.0.2 openpyxl==3.1.5

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

1pip 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. הנה קוד ההתחלה:

1import requests
2from bs4 import BeautifulSoup
3> This paragraph contains content that cannot be parsed and has been skipped.
4print(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", תראו משהו כזה:

1<span class="titleline">
2  <a href="https://darkbloom.dev">DarkbloomPrivate inference on idle Macs</a>
3</span>

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

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

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

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

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

1import requests
2from bs4 import BeautifulSoup
3from pprint import pprint
4> This paragraph contains content that cannot be parsed and has been skipped.
5stories = []
6story_rows = soup.select("tr.athing")
7for row in story_rows:
8    # Title and URL from the story row
9    title_tag = row.select_one("span.titleline > a")
10    if not title_tag:
11        continue
12    title = title_tag.get_text()
13    link = title_tag.get("href", "")
14    # Metadata from the next sibling row
15    meta_row = row.find_next_sibling("tr")
16    score = 0
17    author = ""
18    comments = 0
19> This paragraph contains content that cannot be parsed and has been skipped.
20> This paragraph contains content that cannot be parsed and has been skipped.
21# Filter to stories with 50+ points, sorted by score
22top_stories = sorted(
23    [s for s in stories if s["score"] >= 50],
24    key=lambda x: x["score"],
25    reverse=True,
26)
27pprint(top_stories[:10])

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

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

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

שמרו והריצו:

1python scrape_hn_bs4.py

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

1[{'author': 'twapi',
2  'comments': 65,
3  'score': 118,
4  'title': 'Darkbloom – Private inference on idle Macs',
5  'url': 'https://darkbloom.dev'},
6 {'author': 'sebg',
7  'comments': 203,
8  'score': 247,
9  'title': 'Show HN: I built an open-source Perplexity alternative',
10  'url': 'https://github.com/...'},
11 ...]

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

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

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

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

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

This paragraph contains content that cannot be parsed and has been skipped.

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

1{
2  "by": "twapi",
3  "descendants": 65,
4  "id": 47788542,
5  "kids": [47789171, 47788769, 47788762],
6  "score": 118,
7  "time": 1776312399,
8  "title": "Darkbloom – Private inference on idle Macs",
9  "type": "story",
10  "url": "https://darkbloom.dev"
11}

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

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

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

1import requests
2import time
3from pprint import pprint
4API_BASE = "https://hacker-news.firebaseio.com/v0"
5# Fetch top story IDs
6response = requests.get(f"{API_BASE}/topstories.json")
7story_ids = response.json()
8print(f"Got {len(story_ids)} top story IDs")
9# Output: Got 500 top story IDs

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

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

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

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

1def fetch_story(story_id):
2    """Fetch a single story's details from the HN API."""
3    resp = requests.get(f"{API_BASE}/item/{story_id}.json")
4    return resp.json()
5> This paragraph contains content that cannot be parsed and has been skipped.
6# Sort by score, show top 10
7top = sorted(stories, key=lambda x: x["score"], reverse=True)[:10]
8pprint(top)

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

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

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

1def fetch_comments(item_id, depth=0, max_depth=3):
2    """Recursively fetch comments up to max_depth."""
3    item = requests.get(f"{API_BASE}/item/{item_id}.json").json()
4    if not item or item.get("type") != "comment":
5        return []
6> This paragraph contains content that cannot be parsed and has been skipped.
7    if depth < max_depth and item.get("kids"):
8        for kid_id in item["kids"]:
9            comments.extend(fetch_comments(kid_id, depth + 1, max_depth))
10            time.sleep(0.05)
11    return comments
12# Example: fetch comments for the top story
13if stories:
14    top_story = stories[0]
15    top_story_full = requests.get(f"{API_BASE}/item/{top_story['id']}.json").json()
16    if top_story_full.get("kids"):
17        print(f"\nComments for: {top_story['title']}")
18        all_comments = []
19        for kid_id in top_story_full["kids"][:5]:  # First 5 top-level comments
20            all_comments.extend(fetch_comments(kid_id, depth=0, max_depth=2))
21            time.sleep(0.1)
22        for c in all_comments[:15]:
23            indent = "  " * c["depth"]
24            preview = c["text"][:80].replace("\n", " ") if c["text"] else "[no text]"
25            print(f"{indent}[{c['author']}] {preview}...")

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

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

1python scrape_hn_api.py

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

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

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

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

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

1import time
2def scrape_hn_pages(num_pages=5):
3    """Scrape multiple pages of HN front page stories."""
4    all_stories = []
5    for page in range(1, num_pages + 1):
6        url = f"https://news.ycombinator.com/news?p={page}"
7        response = requests.get(url, headers=headers)
8        soup = BeautifulSoup(response.text, "html.parser")
9        story_rows = soup.select("tr.athing")
10        if not story_rows:
11            print(f"Page {page}: no stories found, stopping.")
12            break
13        for row in story_rows:
14            title_tag = row.select_one("span.titleline > a")
15            if not title_tag:
16                continue
17            meta_row = row.find_next_sibling("tr")
18            score = 0
19            if meta_row and (score_tag := meta_row.select_one("span.score")):
20                score = int(score_tag.get_text().replace(" points", ""))
21> This paragraph contains content that cannot be parsed and has been skipped.
22        print(f"Page {page}: scraped {len(story_rows)} stories")
23        # Respect the robots.txt crawl-delay of 30 seconds
24        if page < num_pages:
25            time.sleep(30)
26    return all_stories
27stories = scrape_hn_pages(5)
28print(f"\nTotal stories scraped: {len(stories)}")

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

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

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

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

1import requests
2ALGOLIA_BASE = "https://hn.algolia.com/api/v1"
3> This paragraph contains content that cannot be parsed and has been skipped.
4# Example: find Python scraping stories with 10+ points since Jan 2024
5results = search_hn(
6    query="python scraping",
7    tags="story",
8)
9print(f"Found {results['nbHits']} total results")
10for hit in results["hits"][:5]:
11    print(f"  [{hit.get('points', 0)} pts] {hit['title']}")

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

1import calendar, datetime
2# Stories since January 1, 2024
3start_date = datetime.datetime(2024, 1, 1)
4start_ts = int(calendar.timegm(start_date.timetuple()))
5> This paragraph contains content that cannot be parsed and has been skipped.
6ה-Algolia API מהיר (59 מילישניות זמן עיבוד שרת), לא דורש מפתח API, ותומך בעימוד עד 500 עמודים. לניתוח היסטורי בכמויות גדולות, זהו הפתרון הכי טוב.
7## ייצוא נתוני Hacker News שנגרדו ל-CSV, Excel ו-Google Sheets
8כל מדריך גירוד של HN שראיתי מסתיים בפלט `pprint()` בטרמינל. זה מצוין לניפוי שגיאות, אבל אם אתם בונים תקציר יומי או עושים ניתוח מגמות, צריך את הנתונים בקובץ. הנה איך עושים את זה.
9### ייצוא ל-CSV עם Python
10```python
11import csv
12def export_to_csv(stories, filename="hn_stories.csv"):
13    """Save scraped stories to a CSV file."""
14    fieldnames = ["title", "url", "score", "author", "comments"]
15    with open(filename, "w", newline="", encoding="utf-8") as f:
16        writer = csv.DictWriter(f, fieldnames=fieldnames)
17        writer.writeheader()
18        writer.writerows(stories)
19    print(f"Saved {len(stories)} stories to {filename}")
20export_to_csv(stories)

ייצוא ל-Excel עם Python

1import pandas as pd
2def export_to_excel(stories, filename="hn_stories.xlsx"):
3    """Save scraped stories to an Excel file."""
4    df = pd.DataFrame(stories)
5    df.to_excel(filename, index=False, engine="openpyxl")
6    print(f"Saved {len(stories)} stories to {filename}")
7export_to_excel(stories)

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

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

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

1import gspread
2gc = gspread.service_account(filename="service_account.json")
3sh = gc.open("HN Daily Digest")
4worksheet = sh.sheet1
5# Convert stories to rows
6header = list(stories[0].keys())
7rows = [list(s.values()) for s in stories]
8worksheet.clear()
9worksheet.update([header] + rows)
10print("Pushed to Google Sheets")

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

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

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

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

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

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

1from tenacity import retry, stop_after_attempt, wait_exponential_jitter
2import requests
3@retry(stop=stop_after_attempt(5), wait=wait_exponential_jitter(initial=1, max=60))
4def fetch_with_retry(url):
5    """Fetch a URL with automatic retries and exponential backoff."""
6    response = requests.get(url, timeout=10)
7    response.raise_for_status()
8    return response
9# Usage:
10try:
11    resp = fetch_with_retry("https://hacker-news.firebaseio.com/v0/topstories.json")
12    story_ids = resp.json()
13except Exception as e:
14    print(f"Failed after retries: {e}")

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

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

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

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

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

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

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

1import os, json
2def crawl_and_save(story_ids, output_dir="raw_data"):
3    """Fetch story data and save raw JSON to disk."""
4    os.makedirs(output_dir, exist_ok=True)
5    for sid in story_ids:
6        filepath = os.path.join(output_dir, f"{sid}.json")
7        if os.path.exists(filepath):
8            continue  # Skip already-fetched items
9        resp = fetch_with_retry(f"{API_BASE}/item/{sid}.json")
10        with open(filepath, "w") as f:
11            json.dump(resp.json(), f)
12> This paragraph contains content that cannot be parsed and has been skipped.
13הגישה הזו בשני שלבים מועילה במיוחד כשמגרדים מאות פריטים ורוצים לשפר במהירות את אופן העיבוד של הנתונים.
14### אוטומציה של הסקרייפר לפי לוח זמנים
15לתקציר יומי של HN, צריך שהסקרייפר ירוץ אוטומטית. שתי אפשרויות נפוצות:
16**אפשרות 1: cron (Linux/Mac)**
17```bash
18# Run every day at 8:30 AM UTC
1930 8 * * * /usr/bin/python3 /home/user/scrape_hn.py >> /home/user/scrape.log 2>&1

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

1name: Scrape Hacker News
2on:
3  schedule:
4    - cron: '30 8 * * *'  # Daily at 8:30 AM UTC
5  workflow_dispatch:        # Manual trigger button
6jobs:
7  scrape:
8    runs-on: ubuntu-latest
9    steps:
10      - uses: actions/checkout@v4
11      - uses: actions/setup-python@v6
12        with:
13          python-version: '3.12'
14      - run: pip install requests beautifulsoup4 pandas openpyxl
15      - run: python scrape_hn.py
16      - run: |
17          git config user.name "GitHub Actions Bot"
18          git config user.email "actions@github.com"
19          git add -A
20          git diff --staged --quiet || git commit -m "Update HN data $(date -u +%Y-%m-%dT%H:%M:%SZ)"
21          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. אם אתם רק רוצים את הנתונים בלי הקוד, .

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

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

  • שתי שיטות 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 כשאתם צריכים נתונים היסטוריים. ושמרו את במועדפים לרגעים שבהם אתם צריכים רק גיליון אלקטרוני מהיר ולא רוצים להרים פרויקט Python שלם.

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

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

שאלות נפוצות

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

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

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

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

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

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

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

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

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

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

למידע נוסף

תוכן עניינים

נסה את Thunderbit

חלץ לידים ונתונים אחרים ב-2 קליקים בלבד. מופעל על ידי AI.

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