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?
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.
Je peux confirmer qu'il n'y a pas de support XPath dans Beautiful Soup.
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
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]
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:
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"
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()