Come costruire un aggregatore di offerte di lavoro: fonti, deduplica e freschezza

Un buon aggregatore di offerte di lavoro è composto da tre problemi ingegneristici: dove trovare gli annunci, come deduplicarli e come mantenerli aggiornati. Risolvi questi problemi e il scraping diventa la parte facile — ecco il progetto.

Un aggregatore di offerte di lavoro è un motore di ricerca per annunci di lavoro: raccoglie annunci da molte fonti e li mostra in un unico posto. Le parti difficili non sono le richieste HTTP — sono scegliere le fonti giuste, deduplicare lo stesso lavoro che appare su cinque siti e mantenere tutto fresco in modo da non mostrare ruoli già occupati il mese scorso. Se riesci in questi tre aspetti, hai un prodotto difendibile; se fallisci, hai un feed stantio e pieno di duplicati che erode la fiducia. Questa guida è il progetto ingegneristico: mix di fonti, deduplica canonica, freschezza, il verticale SERP delle offerte di lavoro e le linee legali da rispettare. È informativa, non un consiglio legale.

Fonte livello 1: endpoint pubblici degli ATS

I dati di lavoro più puliti su internet non si trovano sui portali di lavoro — si trovano sulle pagine delle carriere aziendali alimentate dai sistemi di tracciamento delle candidature, la maggior parte dei quali espone JSON strutturato. Greenhouse, Lever, Ashby, BambooHR, iCIMS, Paylocity e Workday offrono tutti annunci che puoi richiedere direttamente, senza parsing HTML. Un aggregatore open-source che estrae da tutti e sette li recupera in parallelo e regola il numero di lavoratori ai limiti di velocità di ciascuna piattaforma — circa 50 concorrenti per Workday, 30 per Greenhouse/Lever/iCIMS, 10 per BambooHR e 5 per i limitatori più stretti. Questo livello ti offre titoli canonici, località, fasce salariali e URL per candidarsi direttamente dal datore di lavoro, il che lo rende la spina dorsale di qualsiasi aggregatore serio.

import httpx

# Greenhouse exposes a public board JSON endpoint per company slug
def fetch_greenhouse(slug: str) -> list[dict]:
    url = f"https://boards-api.greenhouse.io/v1/boards/{slug}/jobs?content=true"
    r = httpx.get(url, timeout=20)
    r.raise_for_status()
    jobs = r.json().get("jobs", [])
    return [{
        "source": "greenhouse",
        "external_id": str(j["id"]),
        "title": j["title"],
        "company": slug,
        "location": (j.get("location") or {}).get("name"),
        "apply_url": j["absolute_url"],
        "updated_at": j.get("updated_at"),
    } for j in jobs]

rows = fetch_greenhouse("examplecompany")
print(len(rows), "roles")

Per trovare le aziende da sondare, raccogli gli slug degli ATS su larga scala da un indice web piuttosto che a mano — scansionando gli archivi URL di Common Crawl per modelli di dominio ATS possono emergere decine di migliaia di identificatori aziendali. Quella scansione di scoperta è essa stessa un lavoro di scraping; instradala attraverso un pool di proxy datacenter poiché l'indice è tollerante e la velocità conta più della reputazione IP lì.

Fonte livello 2: il verticale SERP delle offerte di lavoro

I feed degli ATS mancano di tutto ciò che viene pubblicato solo sui siti aggregatori, sui portali regionali o sull'esperienza di lavoro di Google. Il modo efficiente per coprire quella lunga coda senza fare scraping di una dozzina di portali individualmente è il verticale SERP delle offerte di lavoro — una query strutturata che restituisce le offerte che Google mostra per un ruolo e una località, già normalizzate. La nostra SERP API espone un verticale delle offerte di lavoro insieme a notizie, immagini e shopping, quindi puoi estrarre gli annunci di ruolo per parola chiave e geo come JSON e integrarli nello stesso pipeline:

import httpx

def fetch_jobs_serp(query: str, location: str) -> list[dict]:
    r = httpx.get(
        "https://api.quantumproxies.io/serp",
        params={"engine": "google_jobs", "q": query,
                "location": location, "api_key": "QP_API_KEY"},
        timeout=30,
    )
    jobs = r.json().get("jobs", [])
    return [{
        "source": "jobs_serp",
        "external_id": j.get("job_id"),
        "title": j.get("title"),
        "company": j.get("company_name"),
        "location": j.get("location"),
        "apply_url": (j.get("apply_options") or [{}])[0].get("link"),
    } for j in jobs]

serp_rows = fetch_jobs_serp("react developer", "Austin, TX")

Esegui la stessa query nelle città target secondo un programma e avrai una copertura geografica che nessun singolo feed ATS fornisce. Poiché i risultati SERP cambiano in base alla località, geo-targetizza ogni query — il nostro post su come funziona lo scraping SERP nel 2026 copre le meccaniche geo e di paginazione.

Estrai il verticale delle offerte di lavoro dalla SERP API

Diagramma del pipeline che mostra i dati di lavoro provenienti da API ATS, SERP delle offerte di lavoro e portali diretti, quindi normalizzati, deduplicati e aggiornati
Tre livelli di fonti alimentano uno schema; la deduplica e una finestra di freschezza trasformano gli annunci grezzi in un aggregatore affidabile.

Deduplica con chiave canonica

Lo stesso lavoro appare regolarmente sul sito aziendale, su due aggregatori e su una scheda di lavoro di Google. Mostrarlo quattro volte è il modo più veloce per sembrare difettoso. La soluzione standard è una chiave canonica: normalizza e hash la combinazione di titolo del lavoro, azienda, località e — quando disponibile — l'ID esterno del lavoro della fonte. Converti in minuscolo, rimuovi la punteggiatura e comprimi gli spazi bianchi prima di fare l'hash in modo che "Sr. Software Engineer" e "senior software engineer" si uniscano.

import re, hashlib

def canonical_key(job: dict) -> str:
    def norm(s):
        return re.sub(r"[^a-z0-9]+", " ", (s or "").lower()).strip()
    basis = "|".join([
        norm(job.get("title")),
        norm(job.get("company")),
        norm(job.get("location")),
        (job.get("external_id") or ""),
    ])
    return hashlib.sha1(basis.encode()).hexdigest()

def dedupe(rows: list[dict]) -> list[dict]:
    seen, out = set(), []
    for job in rows:
        k = canonical_key(job)
        if k not in seen:
            seen.add(k)
            out.append(job)
    return out

La freschezza è una caratteristica, non un ripensamento

La credibilità di un portale di lavoro è la sua freschezza. Due meccaniche la mantengono onesta: recupera ogni fonte secondo un programma (ogni ora per i feed ATS ad alto volume, ogni giorno per la lunga coda) e elimina tutto ciò che non hai rivisto in una finestra mobile — 30 giorni è un valore predefinito sensato, più breve per i mercati in rapido movimento. Traccia un timestamp dell'ultima visualizzazione per fonte e un conteggio giornaliero in modo da poter individuare quando una fonte si rompe silenziosamente; un aggregatore che monitora il proprio volume può aprire un avviso nel momento in cui i numeri di una piattaforma crollano. La nostra nota su eseguire scraper come software di produzione copre la pianificazione, il rilevamento delle derive e l'allerta.

Le linee legali da rispettare

I dati di lavoro sono dove la legge sullo scraping diventa reale, perché gli annunci possono contenere dati personali (nomi dei reclutatori, dettagli di contatto) e descrizioni dettagliate sono soggette a copyright. L'autorità francese per la protezione dei dati ha multato l'azienda KASPR di €240.000 per aver fatto scraping dei dati di contatto di LinkedIn senza il giusto consenso; le sanzioni GDPR possono raggiungere i €20 milioni o il 4% del fatturato globale, e i danni da copyright hanno raggiunto i $150.000 per opera nei casi statunitensi. La postura più sicura è strutturale: preferisci i feed ATS autorizzati e il SERP delle offerte di lavoro rispetto al brute-forcing dei portali protetti da login, memorizza campi fattuali (titolo, azienda, località) piuttosto che ripubblicare descrizioni complete soggette a copyright, e rimuovi i dati personali di cui non hai bisogno. La nostra panoramica sulla legalità dello scraping web nel 2026 e la saga di conformità di LinkedIn approfondiscono ulteriormente.

Pannello delle statistiche che mostra sette piattaforme ATS, una finestra di freschezza di 30 giorni e la multa di 240.000 euro per lo scraping di LinkedIn da parte di KASPR
I feed ATS strutturati più la deduplica canonica battono lo scraping HTML brute-force su costi, copertura ed esposizione legale.

Dove si inseriscono i proxy

Il livello ATS raramente necessita di IP residenziali, ma la scansione di scoperta, il verticale SERP e qualsiasi scraping diretto dei portali sì — Google e i portali più grandi limitano la velocità per IP e variano i risultati in base alla geo. Instrada quelli attraverso proxy residenziali rotanti con rotazione per richiesta e targeting geo in modo che ogni query della città provenga da una località corrispondente. Mantieni i livelli separati: datacenter economico per l'indice tollerante, residenziale per i target sensibili.

Domande frequenti

Cos'è un aggregatore di offerte di lavoro?

È un motore di ricerca per annunci di lavoro che raccoglie annunci da molte fonti — pagine delle carriere aziendali, piattaforme ATS, altri portali e il SERP delle offerte di lavoro — li normalizza in uno schema unico, rimuove i duplicati e li presenta ai cercatori di lavoro in un unico luogo ricercabile. Il lavoro ingegneristico riguarda le fonti, la deduplicazione e la freschezza, non solo lo scraping.

Come deduplicare gli annunci di lavoro da più fonti?

Costruisci una chiave canonica normalizzando e facendo l'hash del titolo del lavoro, dell'azienda, della località e dell'ID esterno del lavoro della fonte. Converti in minuscolo, rimuovi la punteggiatura e comprimi gli spazi bianchi prima in modo che le varianti ortografiche si uniscano. Mantieni la prima occorrenza e elimina i corrispondenti. Questo cattura lo stesso ruolo che appare sul sito del datore di lavoro, sugli aggregatori e su Google jobs.

È legale fare scraping dei portali di lavoro?

Dipende dalla fonte, dai dati e dalla tua giurisdizione. I campi di elenchi fattuali pubblici sono a rischio minore rispetto ai dati personali o alle descrizioni complete soggette a copyright, e bypassare i muri di login aumenta l'esposizione in stile CFAA. I feed ATS autorizzati e il SERP delle offerte di lavoro sono le rotte più pulite. Le aziende sono state pesantemente multate per lo scraping di dati personali — ottieni consulenza legale per l'uso commerciale.

Quanto dovrebbero essere freschi i dati aggregati delle offerte di lavoro?

Recupera le fonti ad alto volume come i feed ATS ogni ora e la lunga coda ogni giorno, quindi elimina qualsiasi annuncio che non hai rivisto entro una finestra mobile — 30 giorni è un valore predefinito comune. Traccia un timestamp dell'ultima visualizzazione per lavoro e monitora il volume per fonte in modo da catturare una fonte rotta prima che gli utenti vedano ruoli stantii.

Tratta l'aggregazione come tre problemi — fonti, deduplica, freschezza — e lo scraping diventa un dettaglio di supporto. Inizia con feed ATS puliti e il verticale SERP delle offerte di lavoro, deduplica su una chiave canonica, elimina in modo aggressivo e mantieni le scansioni sensibili su IP residenziali rotanti. Questa è la differenza tra un prodotto di cui le persone si fidano e un cimitero di link morti.

Alimenta il tuo aggregatore con la Scraper API