BeautifulSoup извлечь XPATH или CSS путь узла
Я хочу извлечь некоторые данные из HTML, а затем выделить извлеченные элементы на стороне клиента без изменения исходного html. И XPath или CSS-путь отлично подходит для этого. возможно ли извлечь XPATH или CSS-путь непосредственно из BeautifulSoup?
Прямо сейчас я использую маркировку целевого элемента, а затем lxml lib для извлечения xpath, что очень плохо для производительности. Я знаю о BSXPath.py
-- это не работает с BS4.
Решение с переписыванием всего, что нужно использовать родной lxml lib не приемлем из-за сложности.
import bs4
import cStringIO
import random
from lxml import etree
def get_xpath(soup, element):
_id = random.getrandbits(32)
for e in soup():
if e == element:
e['data-xpath'] = _id
break
else:
raise LookupError('Cannot find {} in {}'.format(element, soup))
content = unicode(soup)
doc = etree.parse(cStringIO.StringIO(content), etree.HTMLParser())
element = doc.xpath('//*[@data-xpath="{}"]'.format(_id))
assert len(element) == 1
element = element[0]
xpath = doc.getpath(element)
return xpath
soup = bs4.BeautifulSoup('<div id=i>hello, <b id=i test=t>world!</b></div>')
xpath = get_xpath(soup, soup.div.b)
assert '//html/bodydiv/b' == xpath
2 ответов
на самом деле довольно легко извлечь простой CSS/XPath. Это тот же lxml lib дает вам.
def get_element(node):
# for XPATH we have to count only for nodes with same type!
length = len(list(node.previous_siblings)) + 1
if (length) > 1:
return '%s:nth-child(%s)' % (node.name, length)
else:
return node.name
def get_css_path(node):
path = [get_element(node)]
for parent in node.parents:
if parent.name == 'body':
break
path.insert(0, get_element(parent))
return ' > '.join(path)
soup = bs4.BeautifulSoup('<div></div><div><strong><i>bla</i></strong></div>')
assert get_css_path(soup.i) == 'div:nth-child(2) > strong > i'
боюсь, что библиотека пока не способна на это. Вы можете захватить их по пути css... ВРОДЕ... но, его немного запутанный, где вы называете каждый элемент и класс, пример:
soup.find("htmlelement", class_="theclass")
вы также можете использовать id вместо классов или обоих, если вы предпочитаете быть более конкретным в том, что вы хватаете.
вы можете изменить его, чтобы продолжать идти по пути:
soup.find("htmlelement", class_="theclass").find("htmlelement2", class_="theclass2")
и так далее.
есть также способы навигации по нему, вызывая встроенная функция "next":
find_next("td", class_="main").find_next("td", class_="main").next.next