Webpagina's schrapen in Python met Beautiful Soup zoeken en DOM-modificatie

In de laatste zelfstudie leerde je de basis van de Beautiful Soup-bibliotheek. Naast het navigeren door de DOM-structuur, kun je ook naar elementen met een gegeven zoeken klasse of ID kaart. U kunt ook de DOM-structuur wijzigen met behulp van deze bibliotheek. 

In deze tutorial leer je over verschillende methoden die je helpen bij het zoeken en modificeren. We zullen dezelfde Wikipedia-pagina over Python schrappen uit onze laatste zelfstudie.

Filters voor het doorzoeken van de stamboom

Beautiful Soup heeft veel methoden om de DOM-boom te doorzoeken. Deze methoden lijken erg op elkaar en nemen dezelfde soorten filters als argumenten. Daarom is het logisch om de verschillende filters goed te begrijpen voordat u over de methoden leest. Ik zal hetzelfde gebruiken vind alle() methode om het verschil tussen verschillende filters uit te leggen.

Het eenvoudigste filter dat u kunt doorgeven aan een zoekmethode is een tekenreeks. Beautiful Soup doorzoekt vervolgens het document om een ​​tag te vinden die exact overeenkomt met de string.

voor kop in soep.find_all ('h2'): print (heading.text) # Inhoud # Geschiedenis [edit] # Functies en filosofie [bewerken] # Syntaxis en semantiek [bewerken] # Bibliotheken [bewerken] # Ontwikkelingsomgevingen [bewerken] #… enzovoorts.

U kunt ook een reguliere expressie-object doorgeven aan de vind alle() methode. Deze keer filtert Beautiful Soup de boom door alle tags te vergelijken met een gegeven reguliere expressie.

importeer opnieuw voor heading in soup.find_all (re.compile ("^ h [1-6]")): print (heading.name + "+ heading.text.strip ()) # h1 Python (programmeertaal) # h2 Inhoud # h2 Geschiedenis [edit] # h2 Kenmerken en filosofie [bewerken] # h2 Syntaxis en semantiek [bewerken] # h3 Inspringen [bewerken] # h3 Uitspraken en controlestroom [bewerken] # ... enzovoort.

De code zoekt naar alle tags die beginnen met "h" en gevolgd door een cijfer van 1 tot 6. Met andere woorden, het zoekt naar alle heading-tags in het document.

In plaats van regex te gebruiken, kunt u hetzelfde resultaat bereiken door een lijst met alle tags door te geven die u met Beautiful Soup wilt vergelijken met het document.

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

Je kunt ook passen waar als een parameter voor de vind alle() methode. De code retourneert vervolgens alle tags in het document. De onderstaande uitvoer betekent dat er momenteel 4,339 tags op de Wikipedia-pagina staan ​​die we analyseren.

len (soup.find_all (True)) # 4339

Als u nog steeds niet kunt vinden wat u zoekt met een van de bovenstaande filters, kunt u uw eigen functie definiëren waarbij een element als het enige argument wordt gebruikt. De functie moet ook terugkeren waar als er een overeenkomst is en vals anders. Afhankelijk van wat u nodig heeft, kunt u de functie zo gecompliceerd maken als nodig is om de klus te klaren. Hier is een heel eenvoudig voorbeeld:

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

De bovenstaande functie doorloopt dezelfde Wikipedia Python-pagina en zoekt naar ongeordende lijsten met meer dan 20 kinderen.

Zoeken in de DOM-boom met ingebouwde functies

Een van de populairste methoden om door de DOM te zoeken is vind alle(). Het doorloopt alle afstammelingen van de tag en retourneert een lijst met alle afstammelingen die voldoen aan uw zoekcriteria. Deze methode heeft de volgende handtekening:

find_all (naam, attrs, recursief, string, limiet, ** kwargs)

De naam argument is de naam van de tag waarnaar u met deze functie wilt zoeken terwijl u door de structuur gaat. U bent vrij om een ​​tekenreeks, een lijst, een reguliere expressie, een functie of de waarde op te geven waar als een naam.

U kunt de elementen in de DOM-structuur ook filteren op basis van verschillende kenmerken, zoals ID kaart, href, enz. U kunt ook alle elementen met een specifiek attribuut krijgen, ongeacht de waarde ervan attribuut = True. Het zoeken naar elementen met een specifieke klasse verschilt van het zoeken naar reguliere attributen. Sinds klasse is een gereserveerde sleutelwoord in Python, je zult het moeten gebruiken klasse_ zoekwoordargument bij het zoeken naar elementen met een specifieke klasse.

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

U kunt zien dat het document 1.734 tags heeft met een klasse attribuut en 425 tags met een ID kaart attribuut. Als u alleen de eerste paar van deze resultaten nodig heeft, kunt u een getal aan de methode doorgeven als de waarde van begrenzing. Als deze waarde wordt overschreden, geeft Beautiful Soup opdracht om te stoppen met zoeken naar meer elementen zodra een bepaald aantal is bereikt. Hier is een voorbeeld:

soup.find_all (class _ = "mw-headline", limit = 4) # Geschiedenis # Kenmerken en filosofie # Syntaxis en semantiek # deuk

Wanneer u de vind alle() methode, vertelt u Beautiful Soup om alle afstammelingen van een gegeven label door te nemen om te vinden wat u zoekt. Soms wilt u alleen naar een element zoeken in de directe kinderen op een tag. Dit kan worden bereikt door te geven recursieve = False naar de vind alle() methode.

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

Als u geïnteresseerd bent in het vinden van slechts één resultaat voor een bepaalde zoekopdracht, kunt u de vind() methode om het te vinden in plaats van te passeren limit = 1 naar vind alle(). Het enige verschil tussen de resultaten geretourneerd door deze twee methoden is dat vind alle() geeft een lijst terug met slechts één element en vind() geeft gewoon het resultaat terug.

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

Inhoud

] soup.find ("h2") #

Inhoud

De vind() en vind alle() methoden zoeken door alle afstammelingen van een gegeven label om naar een element te zoeken. Er zijn tien andere zeer vergelijkbare methoden die u kunt gebruiken om de DOM-structuur in verschillende richtingen te doorlopen.

find_parents (name, attrs, string, limit, ** kwargs) find_parent (name, attrs, string, ** kwargs) find_next_siblings (name, attrs, string, limit, ** kwargs) find_next_sibling (naam, attrs, tekenreeks, ** kwargs) find_previous_siblings (naam, attrs, string, limiet, ** kwargs) find_previous_sibling (naam, attrs, string, ** kwargs) find_all_next (naam, attrs, string, limiet, ** kwargs) find_next (naam, attrs, tekenreeks, ** kwargs) find_all_previous (naam, attrs, string, limiet, ** kwargs) find_previous (naam, attrs, string, ** kwargs)

De find_parent () en find_parents () methodes doorkruisen de DOM-boom om het gegeven element te vinden. De find_next_sibling () en find_next_siblings () methoden zullen herhalen over alle broers en zussen van het element dat na de huidige komt. Evenzo is de find_previous_sibling () en find_previous_siblings () methoden zullen herhalen over alle broers en zussen van het element dat vóór de huidige komt.

De find_next () en find_all_next () methoden herhalen alle tags en tekenreeksen die na het huidige element komen. Evenzo is de find_previous () en find_all_previous () methoden herhalen alle tags en tekenreeksen die vóór het huidige element komen.

U kunt ook met behulp van CSS-kiezers naar elementen zoeken met behulp van de select () methode. Hier zijn een paar voorbeelden:

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

Inhoud

] len (soup.select ("p> a: nth-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

De boom aanpassen

Je kunt niet alleen door de DOM-boom zoeken om een ​​element te vinden, maar het ook wijzigen. Het is heel gemakkelijk om een ​​tag te hernoemen en zijn attributen aan te passen.

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) #

Uitgaande van ons laatste voorbeeld, kunt u de inhoud van een tag vervangen door een gegeven string met behulp van de .draad attribuut. Als u de inhoud niet wilt vervangen maar iets extra wilt toevoegen aan het einde van de tag, kunt u de toevoegen () methode. 

Evenzo, als u iets in een tag op een specifieke locatie wilt invoegen, kunt u de voegen () methode. De eerste parameter voor deze methode is de positie of index waarop u de inhoud wilt invoegen en de tweede parameter is de inhoud zelf. U kunt alle inhoud in een tag verwijderen met behulp van de duidelijk() methode. Dit laat je gewoon achter met de tag zelf en zijn attributen.

heading_tag.string = "Functies en filosofie" print (heading_tag) # 

Kenmerken en filosofie

heading_tag.append ("[Appended This Part].") print (heading_tag) #

Kenmerken en filosofie [Toegevoegd bij dit deel].

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

Functies en filosofie Dit deel is toegevoegd [Toegevoegd aan dit deel].

heading_tag.clear () print (heading_tag) #

Aan het begin van dit gedeelte hebt u een kop van niveau twee uit het document geselecteerd en deze gewijzigd in een kop van niveau drie. Als u dezelfde selector nogmaals gebruikt, wordt nu de kop van het volgende niveau twee weergegeven die na het origineel is gekomen. Dit is logisch omdat de oorspronkelijke kop niet langer een niveau twee is. 

De originele kop kan nu worden geselecteerd met h3: n-of type (2). Als u een element of tag en alle inhoud erin volledig uit de structuur wilt verwijderen, kunt u de ontleden() methode.

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

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

deuk...

Zodra je de originele kop hebt ontbonden of verwijderd, neemt de kop op de derde plek zijn plaats in.

Als u een tag en de inhoud ervan uit de structuur wilt verwijderen maar niet de tag volledig wilt vernietigen, kunt u de extract() methode. Met deze methode wordt de tag geretourneerd die is geëxtraheerd. Je hebt nu twee verschillende bomen die je kunt analyseren. De hoofdmap van de nieuwe boom is de tag die u zojuist hebt uitgepakt.

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

Je kunt ook een tag in de boom vervangen door iets anders naar keuze met behulp van de vervangen door() methode. Met deze methode wordt de tag of tekenreeks geretourneerd die is vervangen. Het kan handig zijn als u de vervangen inhoud ergens anders in het document wilt plaatsen.

soup.h1 # 

Python (programmeertaal)

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

In de bovenstaande code is de hoofdkop van het document vervangen door a b label. Het document heeft niet langer een h1 tag, en dat is waarom afdruk (soup.h1) nu wordt afgedrukt Geen.

Laatste gedachten

Nadat u de twee zelfstudies in deze serie hebt gelezen, kunt u nu verschillende webpagina's analyseren en belangrijke gegevens uit het document extraheren. U zou ook de originele webpagina moeten kunnen ophalen, deze moeten aanpassen aan uw eigen behoeften en de gewijzigde versie lokaal kunnen opslaan.

Als je nog vragen hebt over deze tutorial, laat het me dan weten in de comments.