2014-09-09 13 views
23

Sono in qualche web scraping con Node.js. Mi piacerebbe usare XPath in quanto posso generarlo in modo semi-automatico con diversi tipi di GUI. Il problema è che non riesco a trovare un modo per farlo in modo efficace.Esegui l'analisi delle pagine con Node.js e XPath

  1. jsdom è estremamente lento. Analizza il file 500KiB in un minuto o più con il pieno carico della CPU e un ingombro di memoria pesante.
  2. Le librerie popolari per l'analisi HTML (ad esempio cheerio) non supportano XPath, né espongono DOM compatibile con W3C.
  3. L'analisi HTML efficace è, ovviamente, implementata in WebKit, quindi usare phantom o casper sarebbe un'opzione, ma quelli richiedono di essere eseguiti in un modo speciale, non solo node <script>. Non posso fare affidamento sul rischio implicito da questo cambiamento. Ad esempio, è molto più difficile trovare come eseguire node-inspector con phantom.
  4. Spooky è un'opzione, ma è buggy enough, in modo che non è stato eseguito affatto sulla mia macchina.

Qual è il modo corretto di analizzare una pagina HTML con XPath, quindi?

+1

https://www.npmjs.org/package/xpath o https://github.com/yaronn/xpath.js? – mb21

+0

@ mb21 Qualsiasi implementazione DOM performante per eseguirli? –

risposta

29

È possibile farlo in più passaggi.

  1. Parse HTML con parse5. La parte cattiva è che il risultato non è DOM. Anche se è abbastanza veloce e conforme al W3C.
  2. Serializzarlo su XHTML con xmlserializer che accetta strutture DOM-like di parse5 come input.
  3. Analizza nuovamente XHTML con xmldom. Ora hai finalmente quel DOM.
  4. La libreria xpath si basa su xmldom e consente di eseguire query XPath. Essere consapevoli del fatto che XHTML ha il proprio spazio dei nomi e query come //a non funzioneranno.

Finalmente si ottiene qualcosa di simile.

const fs = require('mz/fs'); 
const xpath = require('xpath'); 
const parse5 = require('parse5'); 
const xmlser = require('xmlserializer'); 
const dom = require('xmldom').DOMParser; 

(async() => { 
    const html = await fs.readFile('./test.htm'); 
    const document = parse5.parse(html.toString()); 
    const xhtml = xmlser.serializeToString(document); 
    const doc = new dom().parseFromString(xhtml); 
    const select = xpath.useNamespaces({"x": "http://www.w3.org/1999/xhtml"}); 
    const nodes = select("//x:a/@href", doc); 
    console.log(nodes); 
})(); 
+0

Grazie, funziona perfettamente. Tranne che ho dovuto sostituire 'var document = parser.parse (html.toString());' da 'var document = parse5.parse (html.toString()); 'e elimina la riga' var parser = new parse5.Parser(); '(usando parse5 versione 2.0.2) – qqilihq

1

Ho appena iniziato a utilizzare npm install htmlstrip-native che utilizza uno native implementation per analizzare ed estrarre le parti html rilevanti. Si sta affermando di essere 50 volte più veloce rispetto all'implementazione pura di js (non ho verificato tale affermazione).

A seconda delle esigenze è possibile utilizzare htmlstrip direttamente, o sollevare il codice e binding per rendere il proprietario di C++ utilizzato internamente in htmlstrip-nativa

Se si desidera utilizzare XPath, quindi utilizzare il wrapper già avaialble Qui; https://www.npmjs.org/package/xpath

+0

0. Il tuo link è rotto. 1. Quella libreria sta analizzando le entità, e questo è abbastanza ovvio dal suo nome. 2. XPath non è nemmeno menzionato nella tua risposta. –

+0

risolto il collegamento interrotto; aggiunto il collegamento all'implementazione di xpath, qualunque ragione tu non abbia trovato/usato tu stesso? – Soren

+0

Stava anche indicando il codice sbagliato .. corretto ... – Soren

7

Libxmljs è attualmente l'applicazione più veloce (qualcosa like a benchmark) dal momento che è solo attacchi alla LibXML C-libreria che supporta XPath 1.0 query:

var libxmljs = require("libxmljs"); 
var xmlDoc = libxmljs.parseXml(xml); 
// xpath queries 
var gchild = xmlDoc.get('//grandchild'); 

Tuttavia, è necessario disinfettare il codice HTML prima e convertire a XML corretto. Per fare ciò, è possibile utilizzare l'utilità della riga di comando (tidy -q -asxml input.html) o se si desidera che mantenga solo il nodo, qualcosa come xmlserializer dovrebbe fare il trucco.

0

Potrebbe non esserci mai un modo giusto per analizzare pagine HTML. Una prima recensione su web scraping e crawling mi mostra che Scrapy può essere un buon candidato per le tue necessità. Accetta sia i selettori CSS che XPath. Nel regno di Node.js, abbiamo un nuovo modulo node-osmosis. Questo modulo è costruito su libxmljs in modo tale che suppone supportare sia CSS che XPath anche se non ho trovato alcun esempio utilizzando XPath.

1

Penso che Osmosis sia quello che stai cercando.

  • Usa binding libxml nativo C
  • Supporta CSS 3.0 e XPath ibridi 1.0 selettore
  • selettori Sizzle, selettori Slick, e più
  • Nessun grandi dipendenze come jQuery, Cheerio o jsdom
  • Funzioni parser HTML

    • parsin veloce g
    • Molto veloce la ricerca
    • piccola orma di memoria
  • HTML DOM presenta

    • carico e cercare contenuti ajax
    • interazione DOM e gli eventi
    • Execute embedded e script remoti
    • Esegui codice nel DOM

Here's an example:

osmosis.get(url) 
    .find('//div[@class]/ul[2]/li') 
    .then(function() { 
     count++; 
    }) 
    .done(function() { 
     assert.ok(count == 2); 
     assert.done(); 
    }); 
Problemi correlati