2014-09-22 15 views
8

Chcę wyodrębnić niektóre dane z HTML, a następnie być w stanie podświetlić wyodrębnione elementy po stronie klienta bez modyfikowania źródłowego html. Ścieżka XPath lub CSS wygląda świetnie. Czy można wyodrębnić ścieżkę XPATH lub CSS bezpośrednio z BeautifulSoup? Obecnie używam oznaczania elementu docelowego, a następnie biblioteki lxml, aby wyodrębnić ścieżkę xpath, co jest bardzo niekorzystne dla wydajności. Wiem o BSXPath.py - to nie działa z BS4. Rozwiązanie polegające na przepisywaniu wszystkiego w celu użycia natywnej biblioteki LXML jest niedopuszczalne ze względu na złożoność.BeautifulSoup extract XPATH lub CSS Ścieżka węzła

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 
+1

Krótka odpowiedź: nie, nie istnieje żadna istniejąca metoda. Musisz to zbudować samodzielnie. –

Odpowiedz

4

To rzeczywiście dość łatwo wyodrębnić prosty CSS/XPath. Jest to ta sama biblioteka plików lxml.

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' 
1

Obawiam się, że biblioteka nie jest jeszcze zdolna do tego. Można chwycić je za pomocą ścieżki css ... jakoś ... ale jest trochę zawiłe, gdzie jesteś nazywania każdego elementu i klasę, przykład:

soup.find("htmlelement", class_="theclass") 

można również używać identyfikatorów zamiast klas lub zarówno jeśli wolisz być bardziej konkretny w tym, co łapiesz.

można zmienić go zachować schodząc ścieżką:

soup.find("htmlelement", class_="theclass").find("htmlelement2", class_="theclass2") 

tak dalej i tak dalej.

Istnieją również sposoby poruszania się poprzez wywołanie wbudowanej "next" funkcja:

find_next("td", class_="main").find_next("td", class_="main").next.next 
Powiązane problemy