Ich benutze BeautifulSoup, um eine URL zu kratzen, und ich hatte den folgenden Code
import urllib
import urllib2
from BeautifulSoup import BeautifulSoup
url = "http://www.example.com/servlet/av/ResultTemplate=AVResult.html"
req = urllib2.Request(url)
response = urllib2.urlopen(req)
the_page = response.read()
soup = BeautifulSoup(the_page)
soup.findAll('td',attrs={'class':'empformbody'})
Jetzt können wir im obigen Code findAll
verwenden, um Tags und damit verbundene Informationen abzurufen, aber ich möchte xpath verwenden. Ist es möglich, xpath mit BeautifulSoup zu verwenden? Kann mir jemand nach Möglichkeit einen Beispielcode zur Verfügung stellen, damit dieser hilfreicher ist?
Nein, BeautifulSoup selbst unterstützt keine XPath-Ausdrücke.
Eine alternative Bibliothek ( lxml , unterstützt XPath 1.0. Es hat einen BeautifulSoup-kompatiblen Modus , in dem es versucht, kaputtes HTML zu analysieren, wie es Soup tut. Der Standard lxml HTML-Parser ist jedoch genauso gut geeignet, um kaputtes HTML zu analysieren, und ich glaube, er ist schneller.
Nachdem Sie Ihr Dokument in einen XML-Baum geparst haben, können Sie mit der .xpath()
-Methode nach Elementen suchen.
import urllib2
from lxml import etree
url = "http://www.example.com/servlet/av/ResultTemplate=AVResult.html"
response = urllib2.urlopen(url)
htmlparser = etree.HTMLParser()
tree = etree.parse(response, htmlparser)
tree.xpath(xpathselector)
Von möglichem Interesse für Sie ist die CSS Selector-Unterstützung ; Die Klasse CSSSelector
übersetzt CSS-Anweisungen in XPath-Ausdrücke, was die Suche nach td.empformbody
erheblich vereinfacht:
from lxml.cssselect import CSSSelector
td_empformbody = CSSSelector('td.empformbody')
for elem in td_empformbody(tree):
# Do something with these table cells.
Der Kreis schließt sich: BeautifulSoup selbst hat eine sehr vollständige CSS-Selektor-Unterstützung :
for cell in soup.select('table#foobar td.empformbody'):
# Do something with these table cells.
Ich kann bestätigen, dass es in Beautiful Soup keine XPath-Unterstützung gibt.
Martijns Code funktioniert nicht mehr richtig (er ist mittlerweile 4+ Jahre alt ...). Die Zeile etree.parse()
wird in der Konsole gedruckt und weist der Variablen tree
keinen Wert zu. Unter Bezugnahme auf this konnte ich herausfinden, dass dies mit Anfragen und lxml funktioniert:
from lxml import html
import requests
page = requests.get('http://econpy.pythonanywhere.com/ex/001.html')
tree = html.fromstring(page.content)
#This will create a list of buyers:
buyers = tree.xpath('//div[@title="buyer-name"]/text()')
#This will create a list of prices
prices = tree.xpath('//span[@class="item-price"]/text()')
print 'Buyers: ', buyers
print 'Prices: ', prices
BeautifulSoup hat eine Funktion namens findNext vom aktuellen Element gerichtet childern, so:
father.findNext('div',{'class':'class_value'}).findNext('div',{'id':'id_value'}).findAll('a')
Der obige Code kann den folgenden xpath imitieren:
div[class=class_value]/div[id=id_value]
wenn Sie lxml ganz einfach verwenden:
tree = lxml.html.fromstring(html)
i_need_element = tree.xpath('//a[@class="shared-components"]/@href')
aber wenn Sie BeautifulSoup BS4 verwenden, ist das auch ganz einfach:
versuche diese Magie:
soup = BeautifulSoup(html, "lxml")
i_need_element = soup.select ('a[class*="shared-components"]')
wie Sie sehen, unterstützt dies kein Sub-Tag, daher entferne ich den Teil "/ @ href"
Dies ist ein ziemlich alter Thread, aber es gibt jetzt eine Umgehungslösung, die zu diesem Zeitpunkt möglicherweise noch nicht in BeautifulSoup enthalten war.
Hier ist ein Beispiel von dem, was ich getan habe. Ich verwende das Modul "Anfragen", um einen RSS-Feed zu lesen und dessen Textinhalt in einer Variablen namens "rss_text" abzurufen. Damit starte ich es in BeautifulSoup, suche nach xpath/rss/channel/title und rufe dessen Inhalt ab. Es ist nicht gerade XPath in seiner ganzen Pracht (Platzhalter, mehrere Pfade usw.), aber wenn Sie nur einen grundlegenden Pfad haben, den Sie suchen möchten, funktioniert dies.
from bs4 import BeautifulSoup
rss_obj = BeautifulSoup(rss_text, 'xml')
cls.title = rss_obj.rss.channel.title.get_text()