Axxxy Skrevet 22. april 2014 Del Skrevet 22. april 2014 (endret) Hei! Noen som har kjennskap til lxml i python? Prøver å få samlet noe informasjon fra nettet, men sitter litt fast. Jeg lagde nettopp ett eksempel dokument (HTML koden under) for å forklare situasjonen. Jeg ønsker å få tak i teksen fra <title>, <color> & <day> under hver "box" classe. Vil ikke ha <food> <div class="main"> <div class="box"> <title>Title1</title> <color>Red</color> <day>Monday</day> <food>Bacon</food> </div> <div class="box"> <title>Title2</title> <color>Yellow</color> <food>Onion</food> </div> <div class="box"> <title>Title3</title> <color>Green</color> <day>Friday</day> <food>Sausage</food> </div> </div> Jeg gjør dette ved å bruke XPATH fra lxml.html slik: from lxml import html url = "http://www.blabla.com" tree = html.parse(url) title = tree.xpath(//div[@class="box"]/title) color = tree.xpath(//div[@class="box"]/color) day = tree.xpath(//div[@class="box"]/day) Her blir alle funn(Element span) av <title> lagret i en liste som heter title. Teksen henter jeg ut ved å skrive: for i in title: print i.text.strip() Systemet her går da ut på at alle titlene blir lagret i title, alle fargene blir lagret i color, osv. Jeg vil nå ut i fra dette samle første element i hver liste sammen, slik at jeg kan gruppere dem i en ny liste. Vanskelig forklart, men jeg kan altså brukezip(title, color, day), for å gjøre jobben jeg vil. Problemet oppstår når enkelte av classene ikke inneholder en <day>-tag. Da vil alt forskyve seg, og jeg vil ende opp med at zip-listen nr2 inneholder<day>Friday</day>, mens zip-listen nr3 ikke vil inneholde en <day>-tag i det hele tatt. Eksempel for forklaring ovenfor: >>> title ['Title1', 'Title2', 'Title3'] >>> color ['Red', 'Yellow', 'Green'] >>> day ['Monday', 'Friday'] >>> >>> zip(title, color, day) ['Title1', 'Red', 'Monday'] # OK ['Title2', 'Yellow', 'Friday'] # Her hører ikke friday til. ['Title3', 'Green'] # Blir ikke generert av zip. Derfor blir denne metoden av XPATH vanskelig å bruke til dette tilfellet, da den samler alt av en ting samtidig. Det jeg ønsker er å f.eks loope gjennom denne teksten, og for hver gang jeg møter på class="box", så skal jeg først lete etter én title, én color & én day. Finner den ingen day, kan jeg enkelte bruke lst.append('<tom>'), for å tilpasse det samme system. Men hvordan kan jeg gjøre dette? Beklager dårlig forklaring... Ønsket resultat: loop(each encounter of class="box"): #find one title title.append(x) #find one color color.append(x) #find one day if day not found: day.append('<tom>') else: day.append(x) #------ >>> title ['Title1', 'Title2', 'Title3'] >>> color ['Red', 'Yellow', 'Green'] >>> day ['Monday', '<tom>', 'Friday'] >>> >>> zip(title, color, day) ['Title1', 'Red', 'Monday'] ['Title2', 'Yellow', '<tom>'] ['Title3', 'Green', 'Friday'] Takk på forhånd. Endret 22. april 2014 av Axxxy Lenke til kommentar
snippsat Skrevet 23. april 2014 Del Skrevet 23. april 2014 Viss struktur alltid er den samme,insert <tom> i midten,viss lengden av listen er mindre enn 3. >>> day = tree.xpath('//div[@class="box"]/day/text()') >>> day ['Monday', 'Friday'] >>> if len(day) < 3: ... day.insert(1, '<tom>') ... >>> day ['Monday', '<tom>', 'Friday'] >>> zip(title, color, day) [('Title1', 'Red', 'Monday'), ('Title2', 'Yellow', '<tom>'), ('Title3', 'Green', 'Friday')] Lenke til kommentar
Axxxy Skrevet 23. april 2014 Forfatter Del Skrevet 23. april 2014 Strukturen er ikke alltid den samme. Sidene inneholder mangfoldige box classer, og <day> er som oftest plassert tilfeldig. Heldigvis fant jeg løsningen etter mange timers lesning og testing. I XPATH vil "//" representere absolute path, som vil si at den entene hever en error, eller søker i fra root elementet, selv når jeg prøver å søke via class="box" elementet. Løsningen var lett, bytt ut "//" med ".//". Ett punktum først, vil representere local path. Utestet kode til dette eksempelet: tree = html.parse(url) title, color, day = [], [], [] for elem in tree.xpath('//div[@class="box"]'): curTitle = elem.xpath('.//title/text()') curColor = elem.xpath('.//color/text()') curDay = elem.xpath('.//day/text()') if curTitle: title.append(curTitle[0].strip()) else: title.append('<tom>') if curColor: color.append(curColor[0].strip()) else: color.append('<tom>') if curDay: day.append(curDay[0].strip()) else: day.append('<tom>') >>> print (len(title), len(color), len(day)) (3, 3, 3) Lenke til kommentar
Hayer Skrevet 24. april 2014 Del Skrevet 24. april 2014 Ville det ikke vært mer "pytonisk" å skrive title.append(curTitle[0].strip() if curTitle else '<tom>') ? Lenke til kommentar
Axxxy Skrevet 24. april 2014 Forfatter Del Skrevet 24. april 2014 Ville det ikke vært mer "pytonisk" å skrive title.append(curTitle[0].strip() if curTitle else '<tom>') ? Joda, det har jeg allerede fikset. En liten glipp Lenke til kommentar
zotbar1234 Skrevet 24. april 2014 Del Skrevet 24. april 2014 (endret) Hei! Noen som har kjennskap til lxml i python? (...) Med forbehold om misforståelse av problemstillingen: from lxml import html from StringIO import StringIO as IO from collections import namedtuple tree = html.parse(IO(xml)) xmlobj = namedtuple("xmlobj", ["title", "color", "day"]) def extract_info(element): return xmlobj(title=element.findtext("title", ""), color=element.findtext("color", ""), day=element.findtext("day", "")) # end extract_info for node in tree.iterfind("//div[@class='box']"): print extract_info(node) ... og kjøring med eksempelet ditt gir: xmlobj(title='Title1', color='Red', day='Monday') xmlobj(title='Title2', color='Yellow', day='') xmlobj(title='Title3', color='Green', day='Friday') Var det det du ville? edit: trykkfeil. Også, det å bygge ut lister av dager/titler/farger er nokså trivielt, gitt en liste ut av iterfind(). Endret 24. april 2014 av zotbar1234 Lenke til kommentar
Foxboron Skrevet 24. april 2014 Del Skrevet 24. april 2014 Ville det ikke vært mer "pytonisk" å skrive title.append(curTitle[0].strip() if curTitle else '<tom>') ? Nei, siden "Pytonisk" impliserer at det er simpelt og lesbart. Jeg tror vi kan kjapt konkludere med at det han alt har skrevet er mer lesbart. Lenke til kommentar
Axxxy Skrevet 25. april 2014 Forfatter Del Skrevet 25. april 2014 (endret) ... og kjøring med eksempelet ditt gir: xmlobj(title='Title1', color='Red', day='Monday') xmlobj(title='Title2', color='Yellow', day='') xmlobj(title='Title3', color='Green', day='Friday') Var det det du ville? edit: trykkfeil. Også, det å bygge ut lister av dager/titler/farger er nokså trivielt, gitt en liste ut av iterfind(). Resultatet blir ganske det samme. Husker ikke helt hva jeg gjorde når jeg testet. Prøvde iterfind(), iter(), finall(), find(), og noe mer, men endte konstant opp med error eller None som svar. Ser nå at jeg prøvde å bruke absolute path, uten å vite at den søkte fra root elementet. Man oppnår det samme i ditt eksempel som her: tree = *chunk of html* for i in tree.xpath("//div[@class='box']"): # Finner tekst print i.findtext('title') # Finner kun første møtet med n i elementet print i.find('title').text # Finner alle møter med n i elementet print i.findall('title').text Jeg mente å ha lest flere plasser at xpath var det kjappeste. Kjørte nettopp en timeit test av xpath og findall(). findall() er dobbelt så rask. tree = *chunk of html* def func1(): x = tree.xpath("//div[@class='box']") def func2(): x = tree.findall("div[@class='box']") >>> timeit.timeit(func1, number=10000) 0.3098855718972793 #Best av 5 >>> timeit.timeit(func2, number=10000) 0.12920381991552077 #Best av 5 EDIT: Prøvde nettopp samme testen bare på en skikkelig html fil på rundt 1300 linjer >>> timeit.timeit(func1, number=10000) #xpath() 3.2193610692207812 #Best av 5 >>> timeit.timeit(func2, number=10000) #findall() 0.07786131816033048 #Best av 5 Endret 25. april 2014 av Axxxy Lenke til kommentar
dr.Oid Skrevet 25. april 2014 Del Skrevet 25. april 2014 Sjekk ut BeautifulSoup ... Da blir slike oppgaver trivielle Lenke til kommentar
snippsat Skrevet 25. april 2014 Del Skrevet 25. april 2014 (endret) Sjekk ut BeautifulSoup ... Da blir slike oppgaver trivielle Spiller ingen rolle problemstilling blir lik,da både lxml og BeautifulSoup parser dette lett. lxml er kraftigere(flere verktøy) enn BeautifulSoup,og har til og med BeautifulSoup bygd inn. Når det er sakt så har BeautifulSoup vært en av mine favoritter i mange år. Finne alle class='box' i BeautifulSoup soup.find_all('div', {'class': 'box'}) Så blir det samme jobben som i lxml og kjøre en løkke og ta ut ["title", "color", "day"]. Endret 26. april 2014 av snippsat Lenke til kommentar
Anbefalte innlegg
Opprett en konto eller logg inn for å kommentere
Du må være et medlem for å kunne skrive en kommentar
Opprett konto
Det er enkelt å melde seg inn for å starte en ny konto!
Start en kontoLogg inn
Har du allerede en konto? Logg inn her.
Logg inn nå