Можно ли использовать xpath с BeautifulSoup?

Я использую BeautifulSoup для очистки URL-адреса, и у меня был следующий код

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

Теперь в приведенном выше коде мы можем использовать findAll для получения тегов и связанной с ними информации, но я хочу использовать xpath. Можно ли использовать xpath с BeautifulSoup? Если возможно, может ли кто-нибудь указать мне примерный код, чтобы он стал более полезным?

Ответ 1

Нет, BeautifulSoup сам по себе не поддерживает выражения XPath.

Альтернативная библиотека lxml поддерживает XPath 1.0. Он имеет совместимый с BeautifulSoup режим, где он будет пытаться анализировать битый HTML, как это делает Soup. Тем не менее, стандартный HTML-анализатор lxml отлично справляется с задачей анализа разбитого HTML, и я считаю, что это быстрее.

Разобрав ваш документ в дерево lxml, вы можете использовать метод .xpath() для поиска элементов.

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)

Существует также специальный модуль lxml.html() с дополнительными функциями.

Обратите внимание, что в приведенном выше примере я передал объект response непосредственно в lxml, так как считывание парсера непосредственно из потока более эффективно, чем сначала чтение ответа в большую строку. Чтобы сделать то же самое с библиотекой requests, вы хотите установить stream=True и передать объект response.raw после включения прозрачной декомпрессии транспорта:

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)

Вас может заинтересовать поддержка CSS Selector; класс CSSSelector переводит операторы CSS в выражения XPath, что значительно упрощает поиск по td.empformbody:

from lxml.cssselect import CSSSelector

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

Полный круг: у самой BeautifulSoup очень полная поддержка селектора CSS:

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

Ответ 2

Я могу подтвердить, что в Beautiful Soup нет поддержки XPath.

Ответ 3

Как уже говорили другие, BeautifulSoup не имеет поддержки xpath. Вероятно, есть несколько способов получить что-то из xpath, включая использование Selenium. Однако вот решение, которое работает в Python 2 или 3:

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)

Я использовал этот в качестве ссылки.

Ответ 4

BeautifulSoup имеет функцию с именем findNext из текущего элемента, направленного на детей, поэтому:

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

Выше код может имитировать следующий xpath:

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

Ответ 5

Я просмотрел их docs, и, похоже, нет опции xpath. Кроме того, как вы можете видеть здесь по аналогичному вопросу о SO, OP просит перевод с xpath на BeautifulSoup, поэтому мой вывод будет - нет, есть Отсутствует синтаксический анализ xpath.

Ответ 6

когда вы используете lxml все просто:

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

но при использовании BeautifulSoup BS4 все тоже просто:

  • сначала удалите "//" и "@"
  • второе - добавьте звездочку перед "="

попробуй эту магию:

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

как вы видите, это не поддерживает под-тег, поэтому я удалил часть "/@href"

Ответ 7

Это довольно старый поток, но теперь есть решение для работы, которое, возможно, не было в BeautifulSoup в то время.

Вот пример того, что я сделал. Я использую модуль "запросы" для чтения RSS-канала и получаю его текстовое содержимое в переменной "rss_text". При этом я запускаю его через BeautifulSoup, ища xpath/rss/channel/title и извлекаю его содержимое. Это не совсем XPath во всей красе (подстановочные знаки, несколько путей и т.д.), Но если у вас есть только основной путь, который вы хотите найти, это работает.

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