Muutama kuukausi sitten halusin rakentaa tiimillemme Thunderbitillä päivittäisen koonnin Hacker Newsin kiinnostavimmista jutuista. Ensimmäinen ajatukseni oli vain tallentaa sivu kirjanmerkkeihin ja käydä läpi jutut joka aamu. Se toimi noin kolme päivää, kunnes tajusin käyttäväni 20 minuuttia päivässä pelkkien otsikoiden lukemiseen ja linkkien kopioimiseen taulukkoon.
Hacker News on yksi internetin rikkaimmista ja tiheimmin pakatuista teknologiasignaalien lähteistä — noin , suunnilleen 1 300 uutta juttua päivässä ja noin 13 000 kommenttia vuorokaudessa. Seuraatpa nousevia teknologiatrendejä, valvot brändiäsi, rakennat rekrytointiputkea "Who's Hiring" -ketjuista tai haluat vain pysyä kärryillä siitä, mistä kehittäjämaailma puhuu, kaiken tämän seuraaminen käsin on häviävä peli.
Hyvä uutinen: Hacker Newsin poimiminen Pythonilla on yllättävän suoraviivaista. Tässä oppaassa käyn läpi kaksi valmista tapaa — HTML-poiminta BeautifulSoupilla sekä virallinen HN Firebase API — ja lisäksi kerron sivutuksesta, datan viennistä, tuotantokelpoisista käytännöistä sekä koodittomasta oikotiestä tilanteisiin, joissa Python tuntuu ylimitoitetulta.
Miksi poimia Hacker Newsia Pythonilla?
Hacker News ei ole vain yksi linkkikokoelma muiden joukossa. Se on kuratoitu, yhteisölähtöinen virta, jossa kiinnostavimmat teknologiatarinat nousevat kärkeen äänien ja aktiivisen keskustelun ansiosta. Yleisö painottuu vahvasti teknologia-alan ammattilaisiin (noin ), ja sivuston 66 % suoran liikenteen osuus kertoo uskollisesta, tottuneesta lukijakunnasta — ei satunnaisista kävijöistä.
Tässä syitä, miksi HN-dataa poimitaan:
| Käyttötapaus | Mitä saat |
|---|---|
| Päivittäinen teknologiadigest | Kärkiuutiset, pisteet ja linkit sähköpostiin tai Slackiin |
| Brändin/ kilpailijan seuranta | Hälytykset, kun yrityksesi tai tuotteesi mainitaan |
| Trendianalyysi | Seuraa, mitkä teknologiat, kielet tai aiheet keräävät vetoa ajan myötä |
| Rekrytointi | Jäsennä "Who's Hiring" -ketjut työpaikkailmoituksiksi, teknologiapinoiksi ja palkkasignaaleiksi |
| Sisältötutkimus | Löydä aiheita, joista kannattaa kirjoittaa tai jakaa sisältöä |
| Sentimenttianalyysi | Arvioi yhteisön mielipidettä tuotteista, julkaisuista tai alan muutoksista |
Yli 400 miljardin dollarin arvoiset yhtiöt yhteensä — Stripe, Dropbox, Airbnb — ovat saaneet Hacker Newsista ratkaisevaa varhaista palautetta ja käyttäjiä. Drew Houston julkaisi Dropboxin demon HN:ssä huhtikuussa 2007, se nousi ykköseksi, ja beta-jono paisui 5 000 käyttäjästä 75 000 käyttäjään yhdessä päivässä. HN-data ei ole vain kiinnostavaa — se on kaupallisesti arvokasta.
Data on julkisesti saatavilla, mutta sivuston rakenne tekee manuaalisesta keräämisestä työlästä. Python-automaatio on käytännöllinen ratkaisu.
Kaksi tapaa poimia Hacker News Pythonilla: yleiskatsaus
Tämä opas kattaa kaksi täysin toimivaa lähestymistapaa:
- HTML-poiminta
requests+ BeautifulSoupilla — hae news.ycombinator.com-sivun raakaa HTML:ää ja pura siitä juttudata. Hyvä tapa oppia poiminnan perusteet ja napata juuri ne tiedot, jotka sivulla näkyvät. - Virallinen Hacker News Firebase API — hae JSON-päätteistä suoraan, ilman HTML:n purkua. Parempi luotettaviin datavirtoihin, kommentteihin ja historiatietoihin.
Tässä rinnakkainen vertailu, joka auttaa valitsemaan sopivimman tavan:
| Kriteeri | HTML-poiminta (requests + BS4) | HN Firebase API | Thunderbit (kooditon) |
|---|---|---|---|
| Käyttöönoton vaikeus | Keskitaso (HTML-valitsimet) | Matala (JSON-päätteet) | Ei mitään (2 klikkausta Chrome-laajennuksella) |
| Datan ajantasaisuus | Etusivun reaaliaikainen data | Reaaliaikainen (mikä tahansa kohde ID:n perusteella) | Reaaliaikainen |
| Rajoitusriski | Keskitaso (robots.txt ilmoittaa 30 s crawl-delayn) | Matala (virallinen, salliva) | Thunderbitin hallinnoima |
| Kommenttien haku | Vaikea (sisäkkäinen HTML) | Helppo (rekursiiviset item ID:t) | Alisivujen poimintatoiminto |
| Historiadata | Rajoitettu | Algolia Search API:n kautta | Ei sovellu |
| Paras käyttötapa | Poiminnan perusteiden opettelu | Luotettavat datavirrat | Ei-kehittäjät, nopeat viennit |
Molemmat menetelmät sisältävät täydellisen, ajettavan Python-koodin. Ja jos haluat datan ilman että kirjoitat koodia lainkaan, käsittelen senkin.
Ennen kuin aloitat
- Vaikeustaso: aloittelijasta keskitason käyttäjään
- Aikaa kuluu: noin 15–20 minuuttia per menetelmä
- Tarvitset:
- Python 3.11+ asennettuna
- Pääte tai koodieditori
- Chrome-selain (jos haluat tarkistaa HN:n HTML:n tai kokeilla kooditonta vaihtoehtoa)
- (valinnainen, kooditonta menetelmää varten)

Python-ympäristön valmistelu
Ennen kuin koskemme HN-dataan, valmistellaan ympäristö. Suosittelen virtuaaliympäristön luomista, jotta projektin riippuvuudet pysyvät siisteinä.
1# Luo ja aktivoi virtuaaliympäristö
2python3 -m venv hn-scraper
3# macOS/Linux:
4source hn-scraper/bin/activate
5# Windows:
6hn-scraper\Scripts\activate
7# Asenna molemmissa menetelmissä tarvittavat paketit
8pip install requests==2.33.1 beautifulsoup4==4.14.3 pandas==3.0.2 openpyxl==3.1.5
Tuotantokelpoisia käytäntöjä varten (välimuisti, uudelleenyritykset) kannattaa asentaa myös:
1pip install requests-cache==1.3.1 tenacity==9.1.4
Ei erityisiä API-avaimia, ei tunnuksia. HN:n data on avointa.
Menetelmä 1: Hacker Newsin poiminta Pythonilla BeautifulSoupin avulla
Tämä on klassinen tapa — hae HTML, pura se ja poimi tarvitsemasi tiedot. Näin useimmat oppivat web-poiminnan, ja HN:n yksinkertainen taulukkopohjainen rakenne tekee siitä mainion harjoituskentän.
Vaihe 1: Hae Hacker Newsin etusivu
Avaa editori ja luo tiedosto nimeltä scrape_hn_bs4.py. Tässä aloituskoodi:
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}, Sivun pituus: {len(response.text)} merkkiä")
Aja se. Pitäisi näkyä Status: 200 ja sivun pituudeksi noin 40 000–50 000 merkkiä. Se on HN:n etusivun raaka HTML muistissa valmiina purettavaksi.
Vaihe 2: Ymmärrä HTML-rakenne
HN käyttää taulukkopohjaista rakennetta — ei modernia CSS gridia tai flexiä. Jokainen sivun juttu koostuu kahdesta keskeisestä <tr>-rivistä:
- Jutturivi (
<tr class="athing submission">): sisältää sijoituksen, otsikon ja linkin - Metatietorivi (seuraava
<tr>): sisältää pisteet, kirjoittajan, ajan ja kommenttien määrän
Tärkeät valitsimet:
span.titleline > a— jutun otsikko ja URLspan.score— äänimäärä (esim. "118 points")a.hnuser— kirjoittajan käyttäjänimispan.age— julkaisuaika.subtext-elementin viimeinen<a>, jossa tekstissä on "comment" — kommenttien määrä
Jos klikkaat Chrome-selaimessa minkä tahansa jutun otsikkoa oikealla ja valitset "Inspect", näet jotain tällaista:
1<span class="titleline">
2 <a href="https://darkbloom.dev">Darkbloom – Private inference on idle Macs</a>
3</span>
Ja sen alla metatietorivin:
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 comments</a>
Näiden valitsimien ymmärtäminen on tärkeää — jos HN joskus muuttaa merkintätapaansa, valitsimet pitää päivittää. (Spoileri: API-menetelmä kiertää tämän ongelman kokonaan.)
Vaihe 3: Poimi otsikot, linkit ja pisteet
Nyt varsinaiseen työhön. Käymme läpi jokaisen jutturivin, poimimme otsikon ja linkin jutturiviltä ja sitten metatietoriviltä sen alta pisteet.
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 # Otsikko ja URL jutturiviltä
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 # Metatiedot seuraavalta riviltä
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# Suodata vähintään 50 pistettä saaneet jutut ja järjestä pisteiden mukaan
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])
Muutama huomio koodista:
- Walrus-operaattori (
:=) toimii Python 3.8+:ssa. Sen avulla voit sekä asettaa että tarkistaa arvon yhdellä rivillä — hyödyllinen esimerkiksi valinnaisille elementeille kutenspan.score, joita ei ole jokaisella rivillä (esim. työpaikkajulkaisuissa ei ole pisteitä). - HN käyttää
\xa0:aa eli sitovaa välilyöntiä numeron ja "comments"-sanan välissä, joten jaamme merkkijonon sen kohdalta. - HN:n sisäisiin sivuihin osoittavissa jutuissa (kuten "Ask HN") URL voi olla suhteellinen, esimerkiksi
item?id=. Saatat haluta lisätä eteenhttps://news.ycombinator.com/.
Vaihe 4: Aja koodi ja katso tulokset
Tallenna ja aja:
1python scrape_hn_bs4.py
Näet suunnilleen tällaista tulostetta:
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 ...]
Se on 30 juttua sivulta 1. HN:ssä on kuitenkin satoja aktiivisia juttuja millä tahansa hetkellä. Käsittelemme sivutuksen myöhemmin.
Menetelmä 2: Hacker Newsin poiminta Pythonilla virallisen API:n avulla
HN Firebase API on virallisesti hyväksytty tapa käyttää Hacker Newsin dataa. Ei tunnistautumista, ei API-avaimia, ei HTML:n purkua. Saat siistit JSON-vastaukset. Käytän tätä tapaa kaikkeen, minkä pitää toimia luotettavasti tuotannossa.
Tärkeimmät API-päätteet, jotka kannattaa tuntea
Perus-URL on https://hacker-news.firebaseio.com/v0/. Tässä olennaiset päätteet:
This paragraph contains content that cannot be parsed and has been skipped.
Juttuobjekti näyttää tältä:
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}
Kenttä kids sisältää suorien alakommenttien ID:t. Jokainen kommentti on oma iteminsä, jolla voi olla omia kids-alaisiaan — näin kommenttipuu rakentuu.
Vaihe 1: Hae kärkiuutisten ID:t
Luo tiedosto nimeltä scrape_hn_api.py:
1import requests
2import time
3from pprint import pprint
4API_BASE = "https://hacker-news.firebaseio.com/v0"
5# Hae kärkiuutisten ID:t
6response = requests.get(f"{API_BASE}/topstories.json")
7story_ids = response.json()
8print(f"Sain {len(story_ids)} kärkiuutis-ID:tä")
9# Tulos: Sain 500 kärkiuutis-ID:tä
500 jutun ID:tä yhdellä pyynnöllä — ei parsintaa, ei valitsimia, vain JSON-taulukko.
Vaihe 2: Hae jutun tiedot ID:n perusteella
Nyt tarvitsemme varsinaisen jutun datan. Tässä tulee fan-out-ongelma: 500 juttua tarkoittaa 500 erillistä API-kutsua. Omissa testeissäni yksi item-pyyntö kestää noin 1,2 sekuntia peräkkäin tehtynä. 500 jutulla se on noin 10 minuuttia.
Useimmissa käyttötapauksissa et tarvitse kaikkia 500:tä. Tässä koodi, joka hakee 30 kärkipäivää:
1def fetch_story(story_id):
2 """Hae yhden jutun tiedot HN API:sta."""
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# Järjestä pisteiden mukaan, näytä 10 parasta
7top = sorted(stories, key=lambda x: x["score"], reverse=True)[:10]
8pprint(top)
time.sleep(0.1) lisää pienen kohteliaisuusviiveen. Firebase API:lla ei ole ilmoitettua rajoitusta, mutta minkä tahansa API:n hakkaaminen ilman taukoja on huono tapa.
Vaihe 3: Poimi kommentit (rekursiivinen puukävely)
Tässä API todella loistaa verrattuna HTML-poimintaan. HN:n kommentit voivat olla syvästi sisäkkäisiä — vastauksia vastauksiin vastauksiin. HTML:ssä tämä tarkoittaa monimutkaisten sisäkkäisten taulukoiden purkamista. API:ssa taas jokaisen kommentin kids kertoo sen alaelementit, ja puuta voi kulkea rekursiivisesti.
1def fetch_comments(item_id, depth=0, max_depth=3):
2 """Hae kommentit rekursiivisesti enintään max_depth-tasoon asti."""
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# Esimerkki: hae kommentit kärkipostaukselle
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"\nKommentit jutulle: {top_story['title']}")
18 all_comments = []
19 for kid_id in top_story_full["kids"][:5]: # Ensimmäiset 5 ylintä kommenttia
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 "[ei tekstiä]"
25 print(f"{indent}[{c['author']}] {preview}...")
Tämä rekursiivinen lähestymistapa on huomattavasti helpompi kuin yrittää purkaa sisäkkäisiä HTML-kommenttiketjuja. Jos tarvitset täydet kommenttipuut, API on oikea tie.
Vaihe 4: Aja ja tarkastele tuloksia
1python scrape_hn_api.py
Näet jäsenneltyä juttudataa ja sen jälkeen sisennetyn kommenttien esikatselun. Data on siistimpää, kommenttien haku on suoraviivaista eikä ole riskiä, että poimija hajoaa siksi, että HN vaihtaa CSS-luokan nimeä.
Etusivua pidemmälle: sivutus ja historiadata
Useimmat HN-poimintaoppaat jäävät sivulle 1 — 30 juttuun. Se riittää pikademoon, mutta oikeissa käyttötapauksissa tarvitaan usein enemmän syvyyttä.
Useamman sivun poiminta BeautifulSoupilla
HN:n sivutus käyttää yksinkertaista URL-mallia: ?p=2, ?p=3 jne. Jokaisella sivulla on 30 juttua, ja sivusto näyttää noin sivulle 20 asti (yhteensä noin 600 juttua). Sen jälkeen sivut tyhjenevät.
1import time
2def scrape_hn_pages(num_pages=5):
3 """Poimi useita HN:n etusivun sivuja."""
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"Sivu {page}: ei juttuja löytynyt, lopetetaan.")
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"Sivu {page}: poimittiin {len(story_rows)} juttua")
23 # Kunnioita robots.txt:n 30 sekunnin crawl-delayta
24 if page < num_pages:
25 time.sleep(30)
26 return all_stories
27stories = scrape_hn_pages(5)
28print(f"\nPoimittuja juttuja yhteensä: {len(stories)}")
time.sleep(30) on tärkeä. HN:n pyytää nimenomaisesti 30 sekunnin crawl-delayta. Jos jätät sen huomiotta, sinut voidaan rajoittaa (HTTP 429) tai estää tilapäisesti. Viisi sivua 30 sekunnin välein vie noin 2,5 minuuttia — ei välitön, mutta kohtelias.
Niille, jotka eivät halua hallita sivutuskoodia, hoitaa klikkauspohjaisen ja äärettömän vierityksen sivutuksen automaattisesti. Se klikkaa HN-sivujen alalaidan "More"-painiketta ilman mitään asetuksia.
Historiallisen Hacker News -datan hakeminen Algolia API:lla
Firebase API antaa nykyisen datan. Historiallista analyysiä varten — "Mitkä olivat vuoden 2023 parhaat Python-jutut?" tai "Miten AI-keskustelu on muuttunut viimeisen 5 vuoden aikana?" — tarvitset .
1import requests
2ALGOLIA_BASE = "https://hn.algolia.com/api/v1"
3> This paragraph contains content that cannot be parsed and has been skipped.
4# Esimerkki: löydä Python-poimintaan liittyviä juttuja, joilla on vähintään 10 pistettä, tammikuusta 2024 alkaen
5results = search_hn(
6 query="python scraping",
7 tags="story",
8)
9print(f"Löytyi yhteensä {results['nbHits']} tulosta")
10for hit in results["hits"][:5]:
11 print(f" [{hit.get('points', 0)} pts] {hit['title']}")
Päivämäärärajoituksilla käytä numericFilters-parametria:
1import calendar, datetime
2# Jutut 1. tammikuuta 2024 alkaen
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.
6Algolia API on nopea (5–9 ms palvelinprosessointi), ei vaadi API-avainta ja tukee sivutusta jopa 500 sivuun asti. Suurten historiatietoanalyysien kannalta se on paras saatavilla oleva vaihtoehto.
7## Poimitun Hacker News -datan vienti CSV:ksi, Exceliin ja Google Sheetsiin
8Jokainen näkemäni HN-poimintaopas päättyy `pprint()`-tulosteeseen terminaalissa. Se on hyvä debuggaamiseen, mutta jos rakennat päivittäistä koontia tai teet trendianalyysiä, data pitää saada tiedostoon. Näin se onnistuu.
9### Vie CSV:ksi Pythonilla
10```python
11import csv
12def export_to_csv(stories, filename="hn_stories.csv"):
13 """Tallenna poimitut jutut CSV-tiedostoon."""
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"Tallennettu {len(stories)} juttua tiedostoon {filename}")
20export_to_csv(stories)
Vie Exceliin Pythonilla
1import pandas as pd
2def export_to_excel(stories, filename="hn_stories.xlsx"):
3 """Tallenna poimitut jutut Excel-tiedostoon."""
4 df = pd.DataFrame(stories)
5 df.to_excel(filename, index=False, engine="openpyxl")
6 print(f"Tallennettu {len(stories)} juttua tiedostoon {filename}")
7export_to_excel(stories)
Varmista, että openpyxl on asennettuna — pandas käyttää sitä Excel-moottorina. Jos se puuttuu, saat ImportError-virheen.
Vie Google Sheetsiin (valinnainen)
Automaattisissa työnkuluissa voit haluta puskea datan suoraan Google Sheetsiin gspread-kirjaston avulla. Tämä vaatii Google Cloud -palvelutilin asetuksen (kertaluontoinen prosessi):
1import gspread
2gc = gspread.service_account(filename="service_account.json")
3sh = gc.open("HN Daily Digest")
4worksheet = sh.sheet1
5# Muunna jutut riveiksi
6header = list(stories[0].keys())
7rows = [list(s.values()) for s in stories]
8worksheet.clear()
9worksheet.update([header] + rows)
10print("Puskettiin Google Sheetsiin")
Kooditon vientivaihtoehto
Jos palvelutilien asetukset ja vientikoodin kirjoittaminen tuntuvat työläämmiltä kuin varsinainen poiminta, ymmärrän täysin. Thunderbitillä olemme rakentaneet ilmaisen dataviennin, jolla voit lähettää poimitun datan suoraan Exceliin, Google Sheetsiin, Airtableen tai Notioniin — ilman koodia, ilman tunnuksia, ilman ylläpidettävää putkea. Yksittäisessä tietojen haussa se on aidosti nopeampi tapa. Lisää siitä alempana.
Tee poimijastasi tuotantokelpoinen: virheenkäsittely, välimuisti ja ajastus
Jos ajat poimijaa kerran huvin vuoksi, yllä oleva koodi riittää. Jos ajat sitä päivittäin osana työnkulkua, tarvitset muutaman lisäpalikan.
Virheenkäsittely ja uudelleenyritykset
Verkot pettävät. Palvelimet hidastavat. Yksi huono pyyntö ei saisi kaataa koko poimintaa. Tässä uudelleenyritysfunktio eksponentiaalisella odotusajalla:
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 """Hae URL automaattisilla uudelleenyrityksillä ja eksponentiaalisella backoffilla."""
6 response = requests.get(url, timeout=10)
7 response.raise_for_status()
8 return response
9# Käyttö:
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"Epäonnistui uudelleenyritysten jälkeen: {e}")
tenacity-kirjasto hoitaa uudelleenyritykset siististi. Se yrittää enintään 5 kertaa jitteröidyllä eksponentiaalisella odotuksella — alkaen 1 sekunnista ja nousten enintään 60 sekuntiin. Tämä käsittelee sulavasti HTTP 429 -tilanteet (rajoitettu), 503-virheet (palvelu ei käytettävissä) ja hetkelliset verkkovirheet.
Vastausten välimuistiin tallentaminen uudelleenpoiminnan välttämiseksi
Kehitysvaiheessa ajat poimijaa monta kertaa samalla kun säädät purkulogiiikkaa. Ilman välimuistia jokainen ajo osuu uudelleen HN:n palvelimiin samoilla tiedoilla. requests-cache korjaa tämän kahdella rivillä:
1import requests_cache
2requests_cache.install_cache("hn_cache", expire_after=3600) # Välimuisti 1 tunniksi
Kun lisäät nämä rivit skriptin alkuun, kaikki requests.get()-kutsut tallennetaan automaattisesti paikalliseen SQLite-tietokantaan. Aja skripti uudelleen 10 kertaa tunnin aikana, ja vain ensimmäinen ajo käy oikeasti verkossa. Tämä on työkalu, jota , syystäkin.
Poiminnan ja parsinnan erottaminen toisistaan
Kokeneet poimijat vannovat yhden tavan nimeen: lataa raaka data ensin, pura se vasta sitten. Näin jos parsintalogiiikassa on virhe, voit korjata sen ja parsia uudelleen ilman uutta hakua.
1import os, json
2def crawl_and_save(story_ids, output_dir="raw_data"):
3 """Hae jutun data ja tallenna raaka JSON levylle."""
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 # Ohita jo haetut kohteet
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.
13Tämä kaksivaiheinen malli on erityisen hyödyllinen, kun poimit satoja kohteita ja haluat kehittää käsittelyä nopeasti.
14### Poimijan automaatio aikataululla
15Päivittäistä HN-koontia varten poimijan pitää ajaa automaattisesti. Kaksi yleistä vaihtoehtoa:
16**Vaihtoehto 1: cron (Linux/Mac)**
17```bash
18# Aja joka päivä klo 8.30 UTC
1930 8 * * * /usr/bin/python3 /home/user/scrape_hn.py >> /home/user/scrape.log 2>&1
Vaihtoehto 2: GitHub Actions (ilmainen, ei omaa palvelinta)
1name: Poimi Hacker News
2on:
3 schedule:
4 - cron: '30 8 * * *' # Päivittäin klo 8.30 UTC
5 workflow_dispatch: # Manuaalinen käynnistysnappi
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 "Päivitä HN-data $(date -u +%Y-%m-%dT%H:%M:%SZ)"
21 git push
Muutama huomio GitHub Actions -ajastuksista: kaikki cron-ajat ovat UTC-aikaa, 15–60 minuutin viiveet ovat tavallisia (käytä :30-tyyppisiä minuutteja mieluummin kuin :00), ja GitHub voi poistaa käytöstä ajoitetut workflow't repoilta, jotka ovat olleet 60 päivää ilman toimintaa. Lisää aina workflow_dispatch, jotta voit käynnistää työnkulun käsin testaukseen.
Yksinkertaisempana vaihtoehtona Thunderbitin Scheduled Scraper -toiminnolla voit kuvata aikataulun luonnollisella kielellä — esimerkiksi "poimi joka aamu klo 8" — ilman palvelinta tai cron-asetuksia.
Kun Python on liikaa: kooditon tapa poimia Hacker News
Sanon tämän suoraan, vaikka olen Pythonin ystävä ja tiimini rakentaa kehittäjätyökaluja. Jos tarvitset vain tämän päivän 100 kärki-HN-juttua taulukkoon — heti, kertaluonteisesti — Python-skriptin kirjoittaminen, debuggaus ja ajaminen on turhaa ylipainoa. Pelkkä käyttöönotto (virtuaaliympäristö, paketit, valitsimien selvittely) vie enemmän aikaa kuin itse datan keruu.
Tähän sopii. Työnkulku on tämä:
- Avaa
news.ycombinator.comChromessa - Klikkaa Thunderbit-laajennuksen kuvaketta ja valitse sitten "AI Suggest Fields"
- AI lukee sivun ja ehdottaa sarakkeet: Title, URL, Score, Author, Comment Count, Time Posted
- Säädä kenttiä halutessasi (nimeä uudelleen, poista tai lisää omia — voit jopa lisätä AI-kehotteen kuten "Luokittele AI/DevTools/Web/Muu")
- Klikkaa "Scrape" — data ilmestyy jäsenneltyyn taulukkoon
- Vie Exceliin, Google Sheetsiin, Airtableen tai Notioniin
Kaksi klikkausta jäsenneltyyn dataan. Ei valitsimia, ei koodia, ei ylläpitoa.
Yksi iso etu tässä: Thunderbitin AI mukautuu asettelumuutoksiin automaattisesti. Perinteiset CSS-valitsimiin nojaavat poimijat hajoavat, kun sivun merkintä muuttuu — ja vaikka HN:n HTML on melko vakaa, sitä on muutettu (esimerkiksi class="athing submission" päivitettiin ja span.titleline korvasi vanhemman a.storylink-rakenteen). AI-pohjainen poimija lukee sivun aina tuoreena, joten luokkien nimet eivät haittaa.

Thunderbit hoitaa myös sivutuksen (klikkaa HN:n "More"-painiketta automaattisesti) sekä alisivujen poiminnan (käy jokaisen jutun kommenttisivulla hakemassa keskusteludatan). käyttötapauksessa tämä vastaa menetelmän 2 rekursiivista API-koodia — mutta ilman yhden yhtäkään kirjoitettua riviä.
Valinta on lopulta selkeä: Python on oikea ratkaisu, kun tarvitset omaa logiikkaa, monimutkaisia muunnoksia, ajastettuja automaatioputkia tai olet oppimassa koodaamista. Thunderbit on oikea ratkaisu, kun tarvitset dataa nopeasti, et halua ylläpitää koodia tai et ole kehittäjä. Valitse tilanteeseesi sopiva työkalu.
Python vs. API vs. kooditon: mikä menetelmä kannattaa valita?
Tässä koko päätösmalli:
| Kriteeri | BeautifulSoup (HTML) | Firebase API | Algolia API | Thunderbit (kooditon) |
|---|---|---|---|---|
| Tarvittava tekninen taito | Pythonin keskitaso | Pythonin alkeistaso | Pythonin alkeistaso | Ei mitään |
| Käyttöönottoaika | 10–15 min | 5–10 min | 5–10 min | 2 min |
| Ylläpitotaakka | Keskitaso (valitsimet voivat hajota) | Matala (vakaa JSON) | Matala (vakaa JSON) | Ei mitään |
| Datan syvyys | Vain etusivu | Mikä tahansa kohde, käyttäjät | Haku + historia | Etusivu + alisivut |
| Kommentit | Vaikea | Helppo (rekursiivinen) | Helppo (sisäkkäinen puu) | Alisivujen poiminta |
| Historiadata | Ei | Ei | Kyllä (täysi arkisto) | Ei |
| Vientivaihtoehdot | Koodaa itse | Koodaa itse | Koodaa itse | Sisäänrakennettu (Excel, Sheets jne.) |
| Ajastus | cron / GitHub Actions | cron / GitHub Actions | cron / GitHub Actions | Sisäänrakennettu ajastin |
| Paras käytössä | Poiminnan opetteluun | Luotettaviin putkiin | Tutkimukseen ja analyysiin | Nopeat datanhaut |
Jos opettelet Pythonia tai rakennat jotain räätälöityä, valitse menetelmä 1 tai 2. Jos tarvitset historiatietoa, lisää Algolia API. Jos haluat vain datan ilman koodia, .
Yhteenveto ja tärkeimmät opit
Nyt työkalupakissasi on:
- Kaksi täydellistä Python-menetelmää Hacker Newsin poimintaan — BeautifulSoup HTML:n purkuun ja Firebase API siistiin JSON-dataan
- Sivutustekniikat etusivua pidemmälle, mukaan lukien Algolia API historiatietoon aina vuoteen 2007 asti
- Vientikoodi CSV:lle, Excelille ja Google Sheetsille — koska data terminaalissa ei auta muuta tiimiä
- Tuotantokelpoiset käytännöt — uudelleenyritykset, välimuisti, poiminnan ja parsinnan erottaminen sekä ajastettu automaatio cronilla tai GitHub Actionsilla
- Kooditon vaihtoehto tilanteisiin, joissa Python on enemmän työkalu kuin tarpeen
Suositukseni: aloita useimmissa tapauksissa Firebase API:lla (menetelmä 2). Se on siistimpi, luotettavampi ja antaa kommenttien haun ilman sisäkkäisen HTML:n aiheuttamaa vaivaa. Lisää Algolia API, kun tarvitset historiadataa. Ja pidä kirjanmerkkeissä niitä hetkiä varten, kun tarvitset vain nopean taulukon etkä halua käynnistää kokonaista Python-projektia.
Jos haluat mennä syvemmälle, kokeile poimia HN-kommentteja varten, rakenna päivittäinen koontiputki GitHub Actionsilla tai tutki Algolia API:a ja seuraa, miten teknologiatrendit ovat muuttuneet viimeisen vuosikymmenen aikana.
Usein kysytyt kysymykset
Onko Hacker Newsin poimiminen laillista?
HN:n data on julkisesti saatavilla, ja Y Combinator tarjoaa virallisen API:n nimenomaan ohjelmallista käyttöä varten. Sivuston sallii vain lukuoikeudellisen sisällön poiminnan (etusivu, item-sivut, käyttäjäsivut), mutta pyytää 30 sekunnin crawl-delayta. Kun kunnioitat viivettä etkä poimi vuorovaikutteisia päätepisteitä (äänestys, kirjautuminen), olet vahvoilla. Lisää web-poiminnan etiikasta oppaassamme .
Onko Hacker Newsilla virallinen API?
Kyllä. osoitteessa hacker-news.firebaseio.com/v0/ on ilmainen, ei vaadi tunnistautumista ja tarjoaa pääsyn juttuihin, kommentteihin, käyttäjäprofiileihin ja kaikkiin syöttyyppeihin (top, new, best, ask, show, jobs). Se palauttaa siistiä JSONia eikä ilmoita varsinaista rate limit -rajaa, vaikka kohtelias pyyntövauhti on aina suositeltavaa.
Miten poimin Hacker News -kommentit Pythonilla?
Firebase API:lla haet jutun itemin ja saat sen kids-kentän (taulukko ylimmän tason kommentti-ID:itä). Jokainen kommentti on myös item, jolla on oma kids-kenttänsä vastauksia varten. Kävele puu läpi rekursiivisella funktiolla, joka hakee jokaisen kommentin ja sen alikommentit. Katso täydellinen koodi yllä kohdasta "Poimi kommentit (rekursiivinen puukävely)". Vaihtoehtoisesti palauttaa koko sisäkkäisen kommenttipuun yhdellä pyynnöllä — paljon nopeampi kommenttipainotteisille jutuille.
Voinko poimia Hacker Newsin ilman koodia?
Kyllä. toimii Chrome-laajennuksena — avaa HN, klikkaa "AI Suggest Fields", ja se tunnistaa automaattisesti sarakkeet kuten otsikko, URL, pisteet ja kirjoittaja. Klikkaa "Scrape" ja vie data suoraan Exceliin, Google Sheetsiin, Airtableen tai Notioniin. Se hoitaa sivutuksen ja voi jopa käydä alisivuille hakemassa kommenttidataa. Ei Pythonia, ei valitsimia, ei ylläpitoa.
Miten saan historiallista Hacker News -dataa?
on tähän paras työkalu. Käytä search_by_date-päätettä ja numericFilters=created_at_i>TIMESTAMP-suodatinta päivämäärävälin rajaamiseen. Voit hakea avainsanalla, suodattaa juttutyyppien mukaan ja selata jopa 500 tulossivua. Suurten historiatietojen analysointiin julkisia aineistoja on saatavilla myös (täysi arkisto), (28 miljoonaa riviä) ja (4 miljoonaa juttua).
Lue lisää