Scraping Webpages in Python con Beautiful Soup ricerca e modifica DOM

Nell'ultimo tutorial, hai imparato le basi della libreria Beautiful Soup. Oltre alla navigazione dell'albero DOM, puoi anche cercare elementi con un dato classe o id. È inoltre possibile modificare l'albero DOM utilizzando questa libreria. 

In questo tutorial, imparerai i diversi metodi che ti aiuteranno nella ricerca e nelle modifiche. Scatteremo la stessa pagina di Wikipedia su Python dal nostro ultimo tutorial.

Filtri per la ricerca nell'albero

Beautiful Soup ha molti metodi per cercare l'albero DOM. Questi metodi sono molto simili e utilizzano gli stessi tipi di filtri degli argomenti. Pertanto, ha senso comprendere correttamente i diversi filtri prima di leggere i metodi. Userò lo stesso trova tutto() metodo per spiegare la differenza tra diversi filtri.

Il filtro più semplice che puoi passare a qualsiasi metodo di ricerca è una stringa. Beautiful Soup cercherà quindi nel documento per trovare un tag che corrisponda esattamente alla stringa.

per voce in soup.find_all ('h2'): print (heading.text) # Contenuto # Cronologia [modifica] # Funzioni e filosofia [modifica] # Sintassi e semantica [modifica] # Librerie [modifica] # Ambienti di sviluppo [modifica] #… e così via.

È anche possibile passare un oggetto di espressione regolare al trova tutto() metodo. Questa volta, Beautiful Soup filtrerà l'albero abbinando tutti i tag a una determinata espressione regolare.

import re per heading in soup.find_all (re.compile ("^ h [1-6]")): print (heading.name + "+ heading.text.strip ()) # h1 Python (linguaggio di programmazione) # h2 Contenuti # h2 Storia [modifica] # h2 Caratteristiche e filosofia [modifica] # h2 Sintassi e semantica [modifica] # h3 Indentazione [modifica] # h3 Istruzioni e controllo flusso [modifica] # ... un così via.

Il codice cercherà tutti i tag che iniziano con "h" e sono seguiti da una cifra da 1 a 6. In altre parole, cercherà tutti i tag di intestazione nel documento.

Invece di usare espressioni regolari, potresti ottenere lo stesso risultato passando un elenco di tutti i tag che vuoi che Beautiful Soup corrisponda al documento.

per la voce in soup.find_all (["h1", "h2", "h3", "h4", "h5", "h6"]): print (heading.name + "+ heading.text.strip ())

Puoi anche passare Vero come parametro per il trova tutto() metodo. Il codice restituirà quindi tutti i tag nel documento. L'output qui sotto indica che ci sono attualmente 4.339 tag nella pagina di Wikipedia che stiamo analizzando.

len (soup.find_all (True)) # 4339

Se non si è ancora in grado di trovare ciò che si sta cercando con uno dei filtri precedenti, è possibile definire la propria funzione che accetta un elemento come unico argomento. Anche la funzione deve tornare Vero se c'è una partita e falso altrimenti. A seconda di ciò che ti serve, puoi rendere la funzione tanto complicata quanto dover fare il lavoro. Ecco un esempio molto semplice:

def big_lists (tag): return len (tag.contents)> 20 e tag.name == 'ul' len (soup.find_all (liste_lande)) # 13

La funzione sopra riportata sta analizzando la stessa pagina Python di Wikipedia e sta cercando liste non ordinate con più di 20 bambini.

Ricerca nell'albero DOM utilizzando le funzioni integrate

Uno dei metodi più popolari per la ricerca attraverso il DOM è trova tutto(). Passerà attraverso tutti i discendenti del tag e restituirà un elenco di tutti i discendenti che corrispondono ai tuoi criteri di ricerca. Questo metodo ha la seguente firma:

find_all (nome, attrs, ricorsivo, stringa, limite, ** kwargs)

Il nome argomento è il nome del tag che si desidera che questa funzione cerchi mentre si attraversa l'albero. Sei libero di fornire una stringa, un elenco, un'espressione regolare, una funzione o il valore Vero come un nome.

Puoi anche filtrare gli elementi nell'albero DOM sulla base di diversi attributi come id, href, ecc. Puoi anche ottenere tutti gli elementi con un attributo specifico indipendentemente dal suo valore Attributo = True. La ricerca di elementi con una classe specifica è diversa dalla ricerca di attributi regolari. Da classe è una parola chiave riservata in Python, dovrai usare il classe_ argomento della parola chiave quando si cercano elementi con una classe specifica.

import re len (soup.find_all (id = True)) # 425 len (soup.find_all (class_ = True)) # 1734 len (soup.find_all (class _ = "mw-headline")) # 20 len (soup.find_all (href = True)) # 1410 len (soup.find_all (href = re.compile ("python"))) # 102

Puoi vedere che il documento ha 1.734 tag con a classe attributo e 425 tag con un id attributo. Se ti servono solo i primi di questi risultati, puoi passare un numero al metodo come valore di limite. Passando questo valore istruirai la bella zuppa a smettere di cercare più elementi una volta raggiunto un certo numero. Ecco un esempio:

soup.find_all (class _ = "mw-headline", limite = 4) # Storia # Caratteristiche e filosofia # Sintassi e semantica # dentellatura

Quando usi il trova tutto() metodo, stai dicendo a Beautiful Soup di esaminare tutti i discendenti di un determinato tag per trovare quello che stai cercando. A volte, vuoi cercare un elemento solo nei bambini diretti su un tag. Questo può essere ottenuto passando ricorsiva = False al trova tutto() metodo.

len (soup.html.find_all ("meta")) # 6 len (soup.html.find_all ("meta", recursive = False)) # 0 len (soup.head.find_all ("meta", recursive = False) ) # 6

Se sei interessato a trovare solo un risultato per una particolare query di ricerca, puoi utilizzare il comando trova() metodo per trovarlo invece di passare Limite = 1 a trova tutto(). L'unica differenza tra i risultati restituiti da questi due metodi è quella trova tutto() restituisce una lista con un solo elemento e trova() restituisce solo il risultato.

soup.find_all ("h2", limite = 1) # [

Contenuto

] soup.find ("h2") #

Contenuto

Il trova() e trova tutto() i metodi cercano tutti i discendenti di un determinato tag per cercare un elemento. Esistono altri dieci metodi molto simili che è possibile utilizzare per scorrere l'albero DOM in diverse direzioni.

find_parents (nome, attrs, stringa, limite, ** kwargs) find_parent (nome, attrs, stringa, ** kwargs) find_next_siblings (nome, attrs, stringa, limite, ** kwargs) find_next_sibling (nome, attrs, stringa, ** kwargs) find_previous_siblings (nome, attrs, stringa, limite, ** kwargs) find_previous_sibling (nome, attrs, stringa, ** kwargs) find_all_next (nome, attrs, stringa, limite, ** kwargs) find_next (nome, attrs, stringa, ** kwargs) find_all_previous (nome, attrs, stringa, limite, ** kwargs) find_previous (nome, attrs, stringa, ** kwargs)

Il find_parent () e find_parents () i metodi attraversano l'albero DOM per trovare l'elemento dato. Il find_next_sibling ()find_next_siblings () i metodi verranno iterati su tutti i fratelli dell'elemento che vengono dopo quello corrente. Allo stesso modo, il find_previous_sibling () e find_previous_siblings () i metodi verranno iterati su tutti i fratelli dell'elemento che precede quello corrente.

Il trova il prossimo() e find_all_next () i metodi eseguiranno iterazioni su tutti i tag e le stringhe che seguono l'elemento corrente. Allo stesso modo, il find_previous () e find_all_previous () i metodi eseguiranno iterazioni su tutti i tag e le stringhe che precedono l'elemento corrente.

Puoi anche cercare elementi usando selettori CSS con l'aiuto di selezionare() metodo. Ecco alcuni esempi:

len (soup.select ("p a")) # 411 len (soup.select ("p> a")) # 291 soup.select ("h2: nth-of-type (1)") # [

Contenuto

] len (soup.select ("p> a: nth-of-type (2)")) # 46 len (soup.select ("p> a: nth-of-type (10)")) # 6 len (soup.select ("[class * = section]")) # 80 len (soup.select ("[class $ = section]")) # 20

Modifica dell'albero

Non puoi solo cercare attraverso l'albero DOM per trovare un elemento ma anche modificarlo. È molto facile rinominare un tag e modificarne gli attributi.

heading_tag = soup.select ("h2: nth-of-type (2)") [0] heading_tag.name = "h3" print (heading_tag) # 

Feat ... heading_tag ['class'] = 'headingChanged' print (heading_tag) #

Continuando dal nostro ultimo esempio, puoi sostituire il contenuto di un tag con una determinata stringa usando il .stringa attributo. Se non vuoi sostituire il contenuto ma aggiungi qualcosa in più alla fine del tag, puoi utilizzare il comando aggiungere() metodo. 

Allo stesso modo, se desideri inserire qualcosa all'interno di un tag in una posizione specifica, puoi utilizzare il comando inserire() metodo. Il primo parametro per questo metodo è la posizione o l'indice a cui si desidera inserire il contenuto e il secondo parametro è il contenuto stesso. Puoi rimuovere tutto il contenuto all'interno di un tag usando il chiaro() metodo. Questo ti lascerà solo il tag stesso e i suoi attributi.

heading_tag.string = "Caratteristiche e filosofia" print (heading_tag) # 

Caratteristiche e filosofia

heading_tag.append ("[Aggiunta di questa parte].") print (heading_tag) #

Caratteristiche e filosofia [Aggiunta di questa parte].

print (heading_tag.contents) # ['Features and Philosophy', '[Appended This Part].'] heading_tag.insert (1, 'Inserted this part') stampa (titolo_tag) #

Caratteristiche e filosofia Inserita questa parte [Aggiunta di questa parte].

heading_tag.clear () print (heading_tag) #

All'inizio di questa sezione, hai selezionato un'intestazione di livello due dal documento e l'hai modificata in un'intestazione di livello tre. Usando di nuovo lo stesso selettore ora mostrerai il prossimo livello di due titoli che è arrivato dopo l'originale. Questo ha senso perché l'intestazione originale non è più un'intestazione di livello due. 

L'intestazione originale può ora essere selezionata usando h3: nth-of-type (2). Se si desidera rimuovere completamente un elemento o un tag e tutto il contenuto al suo interno dall'albero, è possibile utilizzare il comando decomporsi() metodo.

soup.select ("h3: nth-of-type (2)") [0] #  soup.select ("h3: nth-of-type (3)") [0] # 

dentellatura... soup.select ("h3: nth-of-type (2)") [0] .decompose () soup.select ("h3: nth-of-type (2)") [0] #

dentellatura...

Una volta decomposto o rimosso l'intestazione originale, l'intestazione nel terzo punto prende il suo posto.

Se vuoi rimuovere un tag e il suo contenuto dall'albero ma non vuoi distruggere completamente il tag, puoi utilizzare il comando estratto() metodo. Questo metodo restituirà il tag che ha estratto. Ora avrai due alberi diversi che puoi analizzare. La radice del nuovo albero sarà il tag che hai appena estratto.

heading_tree = soup.select ("h3: nth-of-type (2)") [0] .extract () len (heading_tree.contents) # 2

Puoi anche sostituire un tag all'interno dell'albero con qualcos'altro di tua scelta usando il sostituirlo con() metodo. Questo metodo restituirà il tag o la stringa che ha sostituito. Può essere utile se vuoi inserire il contenuto sostituito da qualche altra parte nel documento.

soup.h1 # 

Python (linguaggio di programmazione)

bold_tag ​​= soup.new_tag ("b") bold_tag.string = "Python" soup.h1.replace_with (bold_tag) print (soup.h1) # None print (soup.b) # Pitone

Nel codice precedente, l'intestazione principale del documento è stata sostituita con un B etichetta. Il documento non ha più un h1 tag, ed è per questo stampa (soup.h1) ora stampa Nessuna.

Pensieri finali

Dopo aver letto i due tutorial di questa serie, ora dovresti essere in grado di analizzare pagine Web diverse ed estrarre dati importanti dal documento. Dovresti anche essere in grado di recuperare la pagina web originale, modificarla in base alle tue esigenze e salvare la versione modificata a livello locale.

Se avete domande su questo tutorial, fatemelo sapere nei commenti.