мы можем использовать 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? Если возможно, может ли кто-нибудь предоставить мне пример кода, чтобы он был более полезным?


person Shiva Krishna Bavandla    schedule 13.07.2012    source источник


Ответы (10)


Нет, 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; класс 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.
person Martijn Pieters    schedule 13.07.2012
comment
Большое спасибо, Питерс, я получил две информации из вашего кода: 1. Уточнение, что мы не можем использовать xpath с BS 2. Хороший пример использования lxml. Можем ли мы увидеть в конкретной документации, что мы не можем реализовать xpath с помощью BS в письменной форме, потому что мы должны предъявить некоторые доказательства тем, кто просит разъяснений, верно? - person Shiva Krishna Bavandla; 13.07.2012
comment
Трудно доказать отрицательное; в документация BeautifulSoup 4 есть функция поиска, и нет совпадений для ' xpath'. - person Martijn Pieters; 13.07.2012
comment
Я попытался запустить ваш код выше, но получил ошибку, имя которой «xpathselector» не определено. - person Zvi; 11.01.2021
comment
@Zvi код не определяет селектор Xpath; Я имел в виду, что это следует читать как «используйте свое собственное выражение XPath здесь». - person Martijn Pieters; 13.01.2021

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

person Leonard Richardson    schedule 13.07.2012
comment
Примечание: Леонард Ричардсон является автором Beautiful Soup, в чем вы убедитесь, если перейдете к его профилю пользователя. - person senshin; 14.05.2014
comment
Было бы очень неплохо иметь возможность использовать XPATH в BeautifulSoup. - person DarthOpto; 02.12.2014
comment
Итак, какова альтернатива? - person static_rtti; 08.05.2017
comment
@ leonard-richardson Сейчас 2021 год, вы все еще подтверждаете, что BeautifulSoup ЕЩЕ не поддерживает xpath? - person mshaffer; 05.04.2021

Как уже говорили другие, 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)

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

person wordsforthewise    schedule 06.01.2017
comment
Одно предупреждение: я заметил, что если есть что-то за пределами корня (например, \n вне внешних тегов ‹html›), то ссылка на xpaths по корню не будет работать, вы должны использовать относительные xpaths. lxml.de/xpathxslt.html - person wordsforthewise; 06.09.2018
comment
Код Мартейна больше не работает должным образом (ему уже больше 4 лет...), строка etree.parse() выводится на консоль и не присваивает значение переменной дерева. Это довольно претензия. Я, конечно, не могу воспроизвести это, и это было бы бессмысленно. Вы уверены, что используете Python 2 для тестирования моего кода или перевели использование библиотеки urllib2 на Python 3 urllib.request? - person Martijn Pieters; 29.09.2019
comment
Да, может быть дело в том, что я использовал Python3 при написании этого, и это не сработало, как ожидалось. Только что протестировано, и ваш работает с Python2, но Python3 гораздо предпочтительнее, так как 2 закрывается (официально больше не поддерживается) в 2020 году. - person wordsforthewise; 30.09.2019
comment
абсолютно согласен, но вопрос здесь использует Python 2. - person Martijn Pieters; 30.09.2019

В BeautifulSoup есть функция с именем findNext от текущего элемента, направленного дочернему элементу, поэтому:

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

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

div[class=class_value]/div[id=id_value]
person user3820561    schedule 09.07.2014

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

Выше использовалась комбинация объекта Soup с lxml, и можно извлечь значение с помощью xpath

person Deepak rayathurai    schedule 06.05.2020

при использовании 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"

person Oleksandr Panchenko    schedule 18.08.2018
comment
select() предназначен для селекторов CSS, это вообще не XPath. как вы видите, это не поддерживает вложенные теги Хотя я не уверен, что это было правдой в то время, сейчас это точно не так. - person AMC; 11.05.2020

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

person Nikola    schedule 13.07.2012
comment
да, на самом деле до сих пор я использовал scrapy, который использует xpath для извлечения данных внутри тегов. Это очень удобно и легко извлекать данные, но мне нужно сделать то же самое с BeautifulSoup, поэтому с нетерпением жду этого. - person Shiva Krishna Bavandla; 13.07.2012

Может быть, вы можете попробовать следующее без XPath

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'))
person dabingsou    schedule 06.12.2019

Это довольно старая тема, но теперь есть обходное решение, которого в то время не было в 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()
person David A    schedule 15.12.2017
comment
Я считаю, что это находит только дочерние элементы. XPath это другое? - person robertspierre; 03.01.2019

используйте 1_

person Γιωργος Αλεξανδρου    schedule 01.03.2021