können wir xpath mit BeautifulSoup verwenden?


105

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 findAllTags und Informationen dazu abrufen, aber ich möchte xpath verwenden. Ist es möglich, xpath mit BeautifulSoup zu verwenden? Wenn möglich, kann mir bitte jemand einen Beispielcode geben, damit dieser hilfreicher ist?

Antworten:


168

Nein, BeautifulSoup selbst unterstützt keine XPath-Ausdrücke.

Eine alternative Bibliothek, lxml , tut Unterstützung XPath 1.0. Es verfügt über einen BeautifulSoup-kompatiblen Modus, in dem versucht wird, fehlerhaftes HTML wie Soup zu analysieren. Der Standard-HTML-Parser lxml kann jedoch genauso gut fehlerhaftes HTML analysieren , und ich glaube, er ist schneller.

Sobald Sie Ihr Dokument in einen lxml-Baum analysiert haben, können Sie mit der .xpath()Methode nach Elementen suchen.

try:
    # Python 2
    from urllib2 import urlopen
except ImportError:
    from urllib.request import urlopen
from lxml import etree

url =  "http://www.example.com/servlet/av/ResultTemplate=AVResult.html"
response = urlopen(url)
htmlparser = etree.HTMLParser()
tree = etree.parse(response, htmlparser)
tree.xpath(xpathselector)

Es gibt auch ein spezielles lxml.html()Modul mit zusätzlichen Funktionen.

Beachten Sie, dass ich im obigen Beispiel das responseObjekt direkt an übergeben habe lxml, da es effizienter ist, den Parser direkt aus dem Stream zu lesen, als die Antwort zuerst in eine große Zeichenfolge einzulesen. Um dasselbe mit der requestsBibliothek zu tun , möchten Sie stream=Truedas response.rawObjekt festlegen und übergeben, nachdem Sie die transparente Transportdekomprimierung aktiviert haben :

import lxml.html
import requests

url =  "http://www.example.com/servlet/av/ResultTemplate=AVResult.html"
response = requests.get(url, stream=True)
response.raw.decode_content = True
tree = lxml.html.parse(response.raw)

Von möglichem Interesse für Sie ist die Unterstützung von CSS Selector . Die CSSSelectorKlasse übersetzt CSS-Anweisungen in XPath-Ausdrücke, was die Suche td.empformbodyerheblich erleichtert:

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 bietet eine sehr umfassende Unterstützung für die CSS-Auswahl :

for cell in soup.select('table#foobar td.empformbody'):
    # Do something with these table cells.

2
Vielen Dank Pieters, ich habe zwei Informationen von Ihrem Code erhalten, 1. Eine Klarstellung, dass wir xpath mit BS 2 nicht verwenden können. Ein schönes Beispiel für die Verwendung von lxml. Können wir in einer bestimmten Dokumentation sehen, dass "wir xpath nicht mit BS in schriftlicher Form implementieren können", weil wir jemandem, der um Klärung bittet, einen Beweis vorlegen sollten?
Shiva Krishna Bavandla

8
Es ist schwer, ein Negativ zu beweisen; Die BeautifulSoup 4-Dokumentation verfügt über eine Suchfunktion und es gibt keine Treffer für 'xpath'.
Martijn Pieters

122

Ich kann bestätigen, dass es in Beautiful Soup keine XPath-Unterstützung gibt.


76
Hinweis: Leonard Richardson ist der Autor von Beautiful Soup. Sie werden sehen, ob Sie sich durch sein Benutzerprofil klicken.
Senshin

22
Es wäre sehr schön, XPATH in BeautifulSoup
DarthOpto

4
Was ist die Alternative?
static_rtti

40

Wie andere gesagt haben, hat BeautifulSoup keine xpath-Unterstützung. Es gibt wahrscheinlich eine Reihe von Möglichkeiten, etwas von einem xpath zu erhalten, einschließlich der Verwendung von Selen. Hier ist jedoch eine Lösung, die entweder in Python 2 oder 3 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)

Ich habe dies als Referenz verwendet.


Eine Warnung: Ich habe festgestellt, dass, wenn sich etwas außerhalb des Stammverzeichnisses befindet (z. B. ein \ n außerhalb der äußeren <html> -Tags), das Verweisen auf xpaths durch das Stammverzeichnis nicht funktioniert. Sie müssen relative xpaths verwenden. lxml.de/xpathxslt.html
Wörter für den

Martijns Code funktioniert nicht mehr richtig (er ist mittlerweile 4+ Jahre alt ...), die Zeile etree.parse () wird auf der Konsole gedruckt und weist der Baumvariablen keinen Wert zu. Das ist ein ziemlicher Anspruch. Ich kann das sicher nicht reproduzieren, und es würde keinen Sinn ergeben . Sind Sie sicher, dass Sie Python 2 zum Testen meines Codes verwenden oder die urllib2Bibliotheksverwendung in Python 3 übersetzt haben urllib.request?
Martijn Pieters

Ja, das kann der Fall sein, dass ich Python3 beim Schreiben verwendet habe und es nicht wie erwartet funktioniert hat. Gerade getestet und deins funktioniert mit Python2, aber Python3 wird sehr bevorzugt, da 2 im Jahr 2020 Sonnenuntergang ist (nicht mehr offiziell unterstützt).
Wörter für den

stimme absolut zu, aber die Frage hier verwendet Python 2 .
Martijn Pieters

17

BeautifulSoup hat eine Funktion namens findNext von aktuellem Element gerichtet childern, also:

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]

1

Ich habe ihre Dokumente durchsucht und es scheint, dass es keine xpath-Option gibt. Wie Sie hier bei einer ähnlichen Frage zu SO sehen können, bittet das OP um eine Übersetzung von xpath nach BeautifulSoup. Mein Fazit wäre also: Nein, es ist keine xpath-Analyse verfügbar.


Ja, bis jetzt habe ich Scrapy verwendet, das xpath verwendet, um die Daten in Tags abzurufen. Es ist sehr praktisch und einfach, Daten abzurufen, aber ich musste dasselbe mit beautifulsoup tun, also freue ich mich darauf.
Shiva Krishna Bavandla

1

Wenn Sie lxml verwenden, ist alles ganz einfach:

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:

  • Entfernen Sie zuerst "//" und "@".
  • zweitens - Stern vor "=" hinzufügen

Probieren Sie diese Magie aus:

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"


select()ist für CSS-Selektoren, es ist überhaupt nicht XPath. Wie Sie sehen, unterstützt dies kein Sub-Tag. Obwohl ich nicht sicher bin, ob dies zu diesem Zeitpunkt der Fall war, ist dies jetzt sicherlich nicht der Fall.
AMC

1

Vielleicht können Sie Folgendes ohne XPath versuchen

from simplified_scrapy.simplified_doc import SimplifiedDoc 
html = '''
<html>
<body>
<div>
    <h1>Example Domain</h1>
    <p>This domain is for use in illustrative examples in documents. You may use this
    domain in literature without prior coordination or asking for permission.</p>
    <p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>
'''
# What XPath can do, so can it
doc = SimplifiedDoc(html)
# The result is the same as doc.getElementByTag('body').getElementByTag('div').getElementByTag('h1').text
print (doc.body.div.h1.text)
print (doc.div.h1.text)
print (doc.h1.text) # Shorter paths will be faster
print (doc.div.getChildren())
print (doc.div.getChildren('p'))

1
from lxml import etree
from bs4 import BeautifulSoup
soup = BeautifulSoup(open('path of your localfile.html'),'html.parser')
dom = etree.HTML(str(soup))
print dom.xpath('//*[@id="BGINP01_S1"]/section/div/font/text()')

Oben verwendet die Kombination von Soup-Objekt mit lxml und man kann den Wert mit xpath extrahieren


0

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 dafür, was ich getan habe. Ich verwende das Modul "Anfragen", um einen RSS-Feed zu lesen und seinen Textinhalt in einer Variablen namens "rss_text" abzurufen. Damit starte ich es durch BeautifulSoup, suche nach xpath / rss / channel / title und rufe seinen Inhalt ab. Es ist nicht gerade XPath in seiner ganzen Pracht (Platzhalter, mehrere Pfade usw.), aber wenn Sie nur einen einfachen 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()

Ich glaube, das findet nur die untergeordneten Elemente. XPath ist eine andere Sache?
Raffaem
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.