Come fare uno scraper asincrono in Python

A me piace leggere, specialmente quando viaggio. Da sempre dalle medie in poi ho letto un mucchio di libri dalla biblioteca fino a quelli settimanali da edicola gialli o fantascienza che siano.
Senza parlare dei fumetti che ne ho una collezione bella grande…

Comunque per scegliere i libri è necessario avere anche qualche recensione perché gli incipit spesso non sono s sufficienti oppure perché si vuole approfondire qualcosa dopo la lettura.

Dopo aver trovato in Fantascienza.com un materiale di tutto rispetto a livello di archivio il secondo passaggio è capire come cavolo trovare le recensioni. Il sito, come gli altri del network, non ha una organizzazione tramite url o archivi per le recensioni ma sono mischiati agli altri come se fossero degli articoli.
Cercare poi per autore o per libro è difficile perché la ricerca non è precisa e trova anche negli articoli e cerca per ogni singolo termine e non nell’insieme.

Qualche esempio:

  • Beam Piper: è un autore ma siccome ha Piper nel nome si trovano gli episodi di Doctor Who con una attrice dallo stesso cognome
  • Ciclo fondazione: è una serie di libri di Asimov ma trova tutti gli articoli con il termine ciclo, 322 per essere precisi e 51 approfondimenti

Per un programmatore come me questo fatto mi urta moltissimo e allora era necessario approfittare per fare pratica di scraping con Python con il supporto asincrono per fare prima (altrimenti ci mette delle ore invece ora dei minuti).

Vediamo di smontare un po lo script che ho fatto che alla fine del processo genera un JSON che con un altro script diventa una pagina HTML.

Lo scraper si trova qui, e fa impressione perché c’è molto codice. Tale quantità di codice è dovuta per normalizzare il contenuto visto che le schede non seguono la stessa organizzazione e l’ordine dei dati che ci servono può essere diverso da una pagina all’altra. Quindi si può trovare l’editore dove in realtà c’è il traduttore e cose di questo tipo.
Inoltre siccome ci sono recensioni anche di album, film e video giochi bisogna escluderli.

Il programma funziona in questo modo:

  1. Verifico se il file books.json esiste o no, in caso mi preparo
    1. Se esiste parti dall’ultimo libro indicizzato
    2. Altrimenti se non esiste parti dalla prima recensione
    3. Prendi l’ultimo articolo pubblicato per sapere l’ID dell’ultimo articolo (le recensioni e il resto sono mischiati)
  2. Facciamo un bel loop
    1. Verifica se la URL è una recensione
    2. Verifica che recensione è in base a tutta una serie di filtri
    3. Normalizza o pulisci i dati che ci servono per il libro
    4. Aggiungi il libro
  3. Salva tutto
    1. Però prima metti in ordine alfabetico in modo naturale

Cosa ho imparato nello sviluppo di questo scraper?

La prima versione non usava i thread e non era asincrono, ci metteva delle ore ma funzionava.
La seconda versione usava i thread ma li eseguiva uno dopo l’altro quindi non c’erano veri margini di guadagno in termini di prestazione.
La terza versione utilizza asyncio e alcuni suoi moduli per fare le varie richieste e salvataggi in modo asincrono.

In poche parole nella fase due nel loop in cui metto in coda tutte le pagine da cercare lui farà un massimo di thread e li eseguirà piano piano che finiscono.
In questo modo diventa molto più rapido e ci mette meno di 30 minuti a elaborare circa 27000 pagine.

Inoltre nel fare i filtri è stato un modo per capire meglio come elaborare una pagina HTML con beautifulsoap tramite selettori CSS.

Inizialmente salvavo il json ad ogni libro, poi con un codice ogni 10 libri ma con la modalità asincrona oltre a complicare le cose rallenta il processo.

Ho aggiunto poi una GitHub Action che viene eseguita una volta a settimana per aggiornare il database JSON. Tale db contiene anche altre info tipo ISBN che non mostro perché manca in parecchi libri.

Conclusione

Con python è rapido fare queste cose il problema sono i dati che vanno normalizzati e che prende molto tempo come lavoro per pensare a degli escamotage per ottenere i dati o pulirli senza romperne altri.

Liked it? Take a second to support Mte90 on Patreon!
Become a patron at Patreon!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *