Skraping av nettsider i Python med vakker suppe Søk og DOM-modifikasjon

I den siste opplæringen lærte du grunnleggende om Beautiful Soup-biblioteket. I tillegg til å navigere i DOM-treet, kan du også søke etter elementer med en gitt klasse eller id. Du kan også endre DOM-treet ved hjelp av dette biblioteket. 

I denne opplæringen lærer du om ulike metoder som vil hjelpe deg med søket og modifikasjonen. Vi vil skrape den samme Wikipedia-siden om Python fra vår siste opplæring.

Filtre for å søke på treet

Beautiful Soup har mange metoder for å søke på DOM-treet. Disse metodene er svært like og tar de samme filtypene som argumenter. Derfor er det fornuftig å forstå de forskjellige filtrene riktig før du leser om metodene. Jeg skal bruke det samme find_all () metode for å forklare forskjellen mellom forskjellige filtre.

Det enkleste filteret du kan overføre til en hvilken som helst søkemetode er en streng. Beautiful Soup vil da søke gjennom dokumentet for å finne en tag som stemmer overens med strengen.

for å gå i soup.find_all ('h2'): print (heading.text) # Innhold # Historie [rediger] # Funksjoner og filosofi [rediger] # Syntaks og semantikk [rediger] # Biblioteker [rediger] # Utviklingsmiljøer [rediger] #… og så videre.

Du kan også sende et vanlig uttrykksobjekt til find_all () metode. Denne gangen vil Beautiful Soup filtrere treet ved å matche alle kodene mot et gitt regulært uttrykk.

import re for overskrift i soup.find_all (re.compile ("^ h [1-6]")): skriv ut (overskrift.name + "+ overskrift.text.strip ()) # h1 Python (programmeringsspråk) # h2 Innhold # h2 Historie [redigere] # h2 Egenskaper og filosofi [rediger] # h2 Syntaks og semantikk [rediger] # h3 Innrykk [rediger] # h3 Statements and control flow [rediger] # ... en så videre.

Koden vil se etter alle kodene som begynner med "h" og etterfølges av et tall fra 1 til 6. Med andre ord vil det være på jakt etter alle overskriftskodene i dokumentet.

I stedet for å bruke regex, kan du oppnå det samme resultatet ved å sende en liste over alle kodene du vil ha Beautiful Soup for å matche mot dokumentet.

for overskrift i soup.find_all (["h1", "h2", "h3", "h4", "h5", "h6"]): skriv ut (overskrift.name + "+ overskrift.text.strip

Du kan også passere ekte som en parameter til find_all () metode. Koden vil da returnere alle kodene i dokumentet. Utgangen nedenfor betyr at det for tiden er 4,339 tagger på Wikipedia-siden som vi analyserer.

len (soup.find_all (True)) # 4339

Hvis du fortsatt ikke kan finne det du leter etter med noen av de ovennevnte filtre, kan du definere din egen funksjon som tar et element som eneste argument. Funksjonen må også returnere ekte hvis det er en kamp og Falsk ellers. Avhengig av hva du trenger, kan du gjøre funksjonen så komplisert som det må være å gjøre jobben. Her er et veldig enkelt eksempel:

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

Ovennevnte funksjon går gjennom samme Wikipedia Python-siden og ser etter uordnede lister som har mer enn 20 barn.

Søke på DOM-treet ved hjelp av innebygde funksjoner

En av de mest populære metodene for å søke gjennom DOM er find_all (). Det vil gå gjennom alle taggens etterkommere og returnere en liste over alle etterkommere som samsvarer med dine søkekriterier. Denne metoden har følgende signatur:

find_all (navn, attrs, rekursiv, streng, grense, ** kwargs)

De Navn argument er navnet på taggen som du vil at denne funksjonen skal søke etter mens du går gjennom treet. Du er fri til å gi en streng, en liste, et vanlig uttrykk, en funksjon eller verdien ekte som et navn.

Du kan også filtrere elementene i DOM-treet på grunnlag av forskjellige attributter som id, href, etc. Du kan også få alle elementene med et spesifikt attributt uansett hvilken verdi de bruker attributt = True. Søke etter elementer med en bestemt klasse er forskjellig fra å søke etter vanlige attributter. Siden klasse er et reservert søkeord i Python, du må bruke klasse_ søkeord argument når du ser etter elementer med en bestemt klasse.

importere re len (soup.find_all (id = True)) # 425 len (soup.find_all (class_ = True)) # 1734 len (suppe.find_all (klasse _ = "mw overskrift")) # 20 len (suppe.find_all (href = True)) # 1410 len (soup.find_all (href = re.compile ("python")))) # 102

Du kan se at dokumentet har 1,734 koder med a klasse attributt og 425 tagger med en id Egenskap. Hvis du bare trenger de første få av disse resultatene, kan du sende et tall til metoden som verdien av grense. Passerer denne verdien vil instruere Beautiful Soup for å slutte å lete etter flere elementer når den har nådd et visst tall. Her er et eksempel:

soup.find_all (klasse _ = "mw-overskrift", grense = 4) # Historie # Funksjoner og filosofi # Syntaks og semantikk # innrykk

Når du bruker find_all () Metode, du forteller Beautiful Soup å gå gjennom alle etterkommerne av en gitt tag for å finne det du leter etter. Noen ganger vil du lete etter et element bare i de direkte barna på en tag. Dette kan oppnås ved å passere rekursiv = False til find_all () metode.

len (suppe) = # 6 len (suppe ) # 6

Hvis du er interessert i å finne bare ett resultat for et bestemt søk, kan du bruke finne() metode for å finne den i stedet for å passere grense = 1 til find_all (). Den eneste forskjellen mellom resultatene returnert av disse to metodene er det find_all () returnerer en liste med bare ett element og finne() returnerer bare resultatet.

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

innhold

] suppe.find ("h2") #

innhold

De finne() og find_all () metoder søk gjennom alle etterkommere av en gitt kode for å søke etter et element. Det er ti andre svært liknende metoder som du kan bruke til å iterere gjennom DOM-treet i forskjellige retninger.

find_parents (navn, attrs, streng, grense, ** kwargs) find_parent (navn, attrs, streng, ** kwargs) find_next_siblings (navn, attrs, streng, grense, ** kwargs) find_next_sibling (navn, attrs, streng, ** (navn, attrs, streng, ** kwargs) find_all_next (navn, attrs, streng, grense, ** kwargs) find_next (navn, attrs, streng, ** kwargs) find_all_previous (navn, attrs, streng, grense, ** kwargs) find_previous (navn, attrs, streng, ** kwargs)

De find_parent () og find_parents () metoder går gjennom DOM-treet for å finne det oppgitte elementet. De find_next_sibling () og find_next_siblings () metoder vil detere over alle søsken av elementet som kommer etter den nåværende. På samme måte, find_previous_sibling () og find_previous_siblings () metoder vil detere over alle søsken av elementet som kommer før den nåværende.

De Finn neste() og find_all_next () metoder vil iterere over alle merkene og strenger som kommer etter det nåværende elementet. På samme måte, find_previous () og find_all_previous () metoder vil iterere over alle merkene og strenger som kommer før det nåværende elementet.

Du kan også søke etter elementer ved hjelp av CSS selectors ved hjelp av å velge() metode. Her er noen eksempler:

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

innhold

] len (suppe.select ("p> a: nth-of-type (2)")) # 46 len (suppe.select ("p> a: nth-of-type (10)")) # 6 len (suppe.selekt ("[klasse * = seksjon]")) # 80 len (suppe.selekt ("[klasse $ = seksjon]")) # 20

Endre treet

Du kan ikke bare søke gjennom DOM-treet for å finne et element, men også endre det. Det er veldig enkelt å gi nytt navn til en tag og endre attributter.

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

Fortsett fra vårt siste eksempel, kan du erstatte innholdet i en etikett med en gitt streng ved hjelp av .string Egenskap. Hvis du ikke vil erstatte innholdet, men legg til noe ekstra på slutten av taggen, kan du bruke føye () metode. 

På samme måte, hvis du vil sette inn noe i en tag på et bestemt sted, kan du bruke sett inn() metode. Den første parameteren for denne metoden er posisjonen eller indeksen der du vil sette inn innholdet, og den andre parameteren er selve innholdet. Du kan fjerne alt innholdet inne i en tag ved hjelp av klar() metode. Dette vil bare gi deg taggen selv og dens attributter.

heading_tag.string = "Funksjoner og filosofi" print (heading_tag) # 

Funksjoner og filosofi

heading_tag.append ("[Tilføyde denne delen].") print (heading_tag) #

Funksjoner og filosofi [vedlagt denne delen].

Skriv ut (heading_tag.contents) # ['Funksjoner og filosofi', '[Tilføy denne delen].'] heading_tag.insert (1, 'Sett inn denne delen') print (heading_tag) #

Funksjoner og filosofi Sett inn denne delen [Legg til denne delen].

heading_tag.clear () print (heading_tag) #

I begynnelsen av denne delen valgte du et nivå to overskrift fra dokumentet og endret det til et nivå tre overskrift. Ved å bruke samme velger igjen vil du vise deg neste nivå to overskrifter som kom etter originalen. Dette gir mening fordi den opprinnelige overskriften ikke lenger er et nivå to overskrift. 

Den opprinnelige overskriften kan nå velges ved hjelp av h3: n-te-i-typen (2). Hvis du helt vil fjerne et element eller en tag og alt innholdet inne i det fra treet, kan du bruke spaltes () metode.

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

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

innrykk...

Når du har dekomponert eller fjernet den opprinnelige overskriften, tar overskriften på det tredje stedet sin plass.

Hvis du vil fjerne en tag og innholdet fra treet, men ikke vil ødelegge taggen helt, kan du bruke ekstrakt() metode. Denne metoden vil returnere taggen som den hentet ut. Du vil nå ha to forskjellige trær som du kan analysere. Roten til det nye treet vil være den taggen du nettopp har hentet ut.

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

Du kan også erstatte en tag inne i treet med noe annet du velger med Erstatt med() metode. Denne metoden vil returnere taggen eller strengen som den erstattet. Det kan være nyttig hvis du vil sette det erstattede innholdet et annet sted i dokumentet.

suppe.h1 # 

Python (programmeringsspråk)

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

I ovennevnte kode er hovedoverskriften i dokumentet erstattet med a b stikkord. Dokumentet har ikke lenger en h1 tag, og det er derfor print (soup.h1) nå skriver ut Ingen.

Siste tanker

Etter å ha lest de to opplæringene i denne serien, bør du nå kunne analysere forskjellige nettsider og trekke ut viktige data fra dokumentet. Du bør også kunne hente den opprinnelige nettsiden, endre den slik at den passer til dine egne behov, og lagre den endrede versjonen lokalt.

Hvis du har spørsmål angående denne opplæringen, vennligst gi meg beskjed i kommentarene.