web-dev-qa-db-fra.com

pouvons-nous utiliser xpath avec BeautifulSoup?

J'utilise BeautifulSoup pour gratter une URL et j'avais le code suivant

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'})

Maintenant, dans le code ci-dessus, nous pouvons utiliser findAll pour obtenir les balises et les informations les concernant, mais je souhaite utiliser xpath. Est-il possible d'utiliser xpath avec BeautifulSoup? Si possible, quelqu'un peut-il me fournir un exemple de code pour qu'il soit plus utile?

89

Nope, BeautifulSoup, par lui-même, ne supporte pas les expressions XPath.

Une bibliothèque alternative, lxml , ne supporte XPath 1.0. Il a un mode compatible BeautifulSoup où il va essayer d’analyser du HTML cassé comme le fait Soup. Cependant, le analyseur HTML lxml par défaut effectue tout aussi bien le travail d’analyse de HTML brisé, et j’estime qu’il est plus rapide.

Une fois que vous avez analysé votre document dans une arborescence lxml, vous pouvez utiliser la méthode .xpath() pour rechercher des éléments.

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)

Le support du sélecteur CSS ; la classe CSSSelector traduit les instructions CSS en expressions XPath, rendant votre recherche de td.empformbody beaucoup plus facile:

from lxml.cssselect import CSSSelector

td_empformbody = CSSSelector('td.empformbody')
for elem in td_empformbody(tree):
    # Do something with these table cells.

Cercle complet à venir: BeautifulSoup lui-même est très complet support du sélecteur CSS :

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

Je peux confirmer qu'il n'y a pas de support XPath dans Beautiful Soup.

97
Leonard Richardson

Le code de Martijn ne fonctionne plus correctement (il a plus de 4 ans maintenant ...), la ligne etree.parse() est imprimée sur la console et n'attribue pas la valeur à la variable tree. Référencement this , j'ai pu comprendre que cela fonctionne à l'aide de requêtes et de lxml:

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
35
wordsforthewise

BeautifulSoup a une fonction nommée findNext à partir de l'enfant actuel dirigé vers l'élément, donc:

father.findNext('div',{'class':'class_value'}).findNext('div',{'id':'id_value'}).findAll('a') 

Le code ci-dessus peut imiter le xpath suivant:

div[class=class_value]/div[id=id_value]
13
user3820561

J'ai cherché dans leur docs et il semble qu'il n'y ait pas d'option xpath. En outre, comme vous pouvez le voir ici sur une question similaire sur SO, l'OP demande une traduction de xpath vers BeautifulSoup. Ma conclusion est donc: non, il n'y a pas d'analyse xpath disponible.

1
Nikola

quand vous utilisez lxml tout simple:

tree = lxml.html.fromstring(html)
i_need_element = tree.xpath('//a[@class="shared-components"]/@href')

mais quand vous utilisez BeautifulSoup BS4 tout aussi simple:

  • d'abord supprimer "//" et "@"
  • seconde - ajoute une étoile avant "="

essayez cette magie:

soup = BeautifulSoup(html, "lxml")
i_need_element = soup.select ('a[class*="shared-components"]')

comme vous le voyez, cela ne supporte pas la sous-balise, donc je supprime la partie "/ @ href"

1
Oleksandr Panchenko

C'est un très vieux fil, mais il existe maintenant une solution de contournement, qui n'était peut-être pas dans BeautifulSoup à l'époque.

Voici un exemple de ce que j'ai fait. J'utilise le module "request" pour lire un flux RSS et obtenir son contenu textuel dans une variable appelée "rss_text". Avec cela, je le passe par BeautifulSoup, recherche le xpath/rss/channel/title et récupère son contenu. Ce n'est pas exactement XPath dans toute sa splendeur (caractères génériques, chemins multiples, etc.), mais si vous souhaitez simplement localiser un chemin de base, cela fonctionne.

from bs4 import BeautifulSoup
rss_obj = BeautifulSoup(rss_text, 'xml')
cls.title = rss_obj.rss.channel.title.get_text()
0
David A