Vai al contenuto principale

Metodi hook

Questi metodi funzionano secondo il principio degli hook. L'implementazione di questi metodi consente di controllare il funzionamento dello scraper in diverse fasi, dall'inizializzazione alla distruzione dell'oggetto

L'implementazione di tutti i metodi eccetto parse è opzionale

async parse(set, results)

Il metodo parse implementa la logica principale di elaborazione della query e di ottenimento del risultato dello scraping; come argomenti vengono passati:

  • set - oggetto con informazioni sulla query:
    • set.query - stringa di testo della query
    • set.lvl - livello della query, predefinito a 0
  • results - oggetto con i risultati, che deve essere compilato e restituito dal metodo parse()
    • lo scraper deve verificare la presenza di ogni chiave nell'oggetto results e compilarla solo se presente, ottimizzando così la velocità e scrapando solo i dati utilizzati nella formazione del risultato
    • results contiene le chiavi delle variabili flat necessarie con valore none, che per impostazione predefinita significa che il risultato non è stato ottenuto, così come le chiavi delle variabili-array (arrays) con valore sotto forma di array vuoto, pronto per essere compilato
    • results.success deve essere impostato su 1 in caso di elaborazione riuscita della query, il valore predefinito è 0, il che significa che la query è stata elaborata con un errore

Vediamo un esempio:

class JS_HTML_Tags extends BaseParser {
static defaultConf = {
results: {
flat: [
['title', 'Title'],
],
arrays: {
h2: ['H2 Headers List', [
['header', 'Header'],
]],
}
},
...
};

async parse(set, results) {
// Otteniamo il contenuto della pagina HTML, il cui indirizzo è stato passato nella query
const {success, data, headers} = await this.request('GET', set.query);

// Verifichiamo il successo e il tipo di data; con una corretta elaborazione delle pagine HTML dovremmo ricevere il tipo 'string', altrimenti A-Parser restituisce un oggetto di tipo Buffer
if (success && typeof data == 'string') {
let matches;

// Verifichiamo la necessità di raccogliere il title e salviamo il valore
if (results.title && matches = data.match(/<title[^>]*>(.*?)<\/title>/))
results.title = matches[1];

// Verifichiamo la necessità di raccogliere h2
if (results.h2) {
let count = 0;
const re = /<h2[^>]*>(.*?)<\/h2>/g;
while(matches = re.exec(data)) {
// Salviamo in un ciclo tutti i tag h2 trovati
results.h2.push(matches[1]);
}
}

// Notifichiamo il successo dello scraping
results.success = 1;
}

// Restituiamo i risultati elaborati
return results;
}
};

Si noti che è possibile creare le proprie funzioni e metodi per una migliore organizzazione del codice:

function Answer() {
return 42;
}

class JS_HTML_Tags extends BaseParser {
...

async parse(set, results) {
results = await this.doWork(set, results);
return results;
}

async doWork(set, results) {
results.answer = Answer();
return results;
}
};

async processConf?(conf)

Questo metodo viene utilizzato per trasformare la configurazione secondo determinate regole, ad esempio quando si utilizza un captcha è sempre necessario utilizzare le sessioni:

async processConf(conf) {
if (conf.useCaptcha)
conf.useSessions = 1
}

async parse(set, results) {
if (conf.useSessions)
await this.login();
}

L'esistenza di questo metodo è dovuta al fatto che A-Parser supporta campi di configurazione dinamici e, nell'ambito di un singolo task, la configurazione può avere valori diversi; tale scenario è possibile in due casi:

  • Utilizzo di modelli nei campi di configurazione, ad esempio [% tools.ua.random() %] per il campo User-Agent
  • Utilizzo di overrides quando si chiama uno scraper da un altro per this.parser.request

Il metodo processConf viene chiamato una sola volta prima di init(). Per i casi sopra descritti, processConf viene chiamato ulteriormente prima dell'elaborazione di ogni query

Regole principali per l'applicazione di processConf:

  • Utilizzarlo solo se le trasformazioni della configurazione hanno un effetto sulle prestazioni
  • Tenere presente che init viene eseguito una volta, mentre processConf può essere eseguito per ogni query; in questo caso la logica potrebbe essere compromessa se init dipende da campi di configurazione variabili (vedi sotto)

async init?()

Il metodo init viene chiamato una volta all'inizializzazione dell'oggetto base dello scraper, serve per eseguire azioni una tantum:

  • Avvio del browser
  • Inizializzazione del gestore delle sessioni tramite il metodo this.sessionManager.init()
  • Connessione al database e creazione di tabelle nel DB
  • Lettura di dati statici
  • Ecc.
avvertimento

Poiché il metodo viene chiamato una sola volta, tutti i campi di configurazione da cui dipende init() non possono essere utilizzati insieme ai modelli dei campi di configurazione o con overrides durante la chiamata a this.parsers.request

async destroy?()

Il metodo destroy viene chiamato una volta al termine del task, è necessario per la corretta distruzione delle risorse aperte:

  • Chiusura del browser
  • Chiusura della connessione al DB
  • Ecc.

async threadInit?()

Questo metodo viene avviato all'inizializzazione di ogni thread; ogni thread è una copia dell'oggetto base dello scraper con il proprio this.threadId unico, che inizia da 0 e finisce a threads_count - 1

Principali varianti di applicazione:

  • creazione di una pagina (scheda) del browser per ogni thread

async threadDestroy?()

Viene eseguito al termine del thread durante il processo di completamento del task, serve per liberare le risorse allocate per quel thread

async afterResultsProcessor?(results)

Questo metodo viene eseguito dopo l'elaborazione dei risultati: costruttore dei risultati, filtraggio e deduplicazione. Il caso d'uso principale è l'aggiunta di query alla coda utilizzando il metodo this.query.add dopo l'applicazione dei filtri personalizzati; in questo modo è implementato il filtraggio dei link da seguire (followlinks) per lo scraper HTML::LinkExtractorHTML::LinkExtractor