2015-12-15 9 views
5

BeautifulSoup zwraca pustą listę podczas wyszukiwania według nazw klas złożonych za pomocą wyrażeń regularnych.BeautifulSoup zwraca pustą listę podczas wyszukiwania według nazw klas związków

przykład:

import re 
from bs4 import BeautifulSoup 

bs = 
    """ 
    <a class="name-single name692" href="www.example.com"">Example Text</a> 
    """ 

bsObj = BeautifulSoup(bs) 

# this returns the class 
found_elements = bsObj.find_all("a", class_= re.compile("^(name-single.*)$")) 

# this returns an empty list 
found_elements = bsObj.find_all("a", class_= re.compile("^(name-single name\d*)$")) 

muszę klasy wybór być bardzo precyzyjne. Jakieś pomysły?

+4

Tak, ponieważ klasa wartości atrybutów są traktowane jako lists/tablice wartości oddzielone spacją. –

+3

Poszukałem kiedyś rozwiązania, ale nie mogłem. Po prostu sugeruję zrobić to w 2 krokach: 1) uzyskaj wszystkie zakotwiczenia z klasą "nazwa-pojedyncza" i 2) uzyskaj te, które pasują również do wyrażenia '^ name \ d * $'. –

+1

FYI, utworzyła kontynuację i bardziej ogólne pytanie dotyczące obsługi atrybutu 'class': [Wyłącz specjalną obsługę klasy" class "] (http://stackoverflow.com/questions/34295928/disable-special-class-attribute -obsługa). – alecxe

Odpowiedz

4

Niestety, podczas próby dopasowania wyrażenia regularnego dla wartości atrybutu klasy zawierającej wiele klas, BeautifulSoup zastosowałby wyrażenie regularne do każdej klasy osobno. Oto istotne tematy dotyczące problemu:

To wszystko dlatego class is a very special multi-valued attribute i za każdym razem, gdy parsowanie HTML, jednego z drzew budowniczowie BeautifulSoup „s (w zależności od wybór parsera) wewnętrznie dzieli wartość ciągu klasowego na listę klas (cytat z docstringu HTMLTreeBuilder):

# The HTML standard defines these attributes as containing a 
# space-separated list of values, not a single value. That is, 
# class="foo bar" means that the 'class' attribute has two values, 
# 'foo' and 'bar', not the single value 'foo bar'. When we 
# encounter one of these attributes, we will parse its value into 
# a list of values if possible. Upon output, the list will be 
# converted back into a string. 

Istnieje wiele obejścia, ale tutaj jest hack-owski jeden - mamy zamiar poprosić BeautifulSoup nie obsługiwać class jako atrybut wielowartościowym poprzez uczynienie prosty zwyczaj Tree Builder:

import re 

from bs4 import BeautifulSoup 
from bs4.builder._htmlparser import HTMLParserTreeBuilder 


class MyBuilder(HTMLParserTreeBuilder): 
    def __init__(self): 
     super(MyBuilder, self).__init__() 

     # BeautifulSoup, please don't treat "class" specially 
     self.cdata_list_attributes["*"].remove("class") 


bs = """<a class="name-single name692" href="www.example.com"">Example Text</a>""" 
bsObj = BeautifulSoup(bs, "html.parser", builder=MyBuilder()) 
found_elements = bsObj.find_all("a", class_=re.compile(r"^name\-single name\d+$")) 

print(found_elements) 

W takim przypadku wyrażenie regularne zostanie zastosowane do wartości atrybutu class jako całości.


Alternatywnie, można po prostu parsowania HTML z xml funkcje włączone (jeśli ma to zastosowanie):

soup = BeautifulSoup(data, "xml") 

Można również użyć CSS selectors i dopasować wszystkie elementy z name-single klasy oraz klasa z "nazwą":

soup.select("a.name-single,a[class^=name]") 

Yo u można następnie zastosować wyrażenie regularne ręcznie w razie potrzeby:

pattern = re.compile(r"^name-single name\d+$") 
for elm in bsObj.select("a.name-single,a[class^=name]"): 
    match = pattern.match(" ".join(elm["class"])) 
    if match: 
     print(elm) 
+0

Świetna odpowiedź. Czy opcja MyBuilder ma jakieś efekty uboczne, coś nie działa/działa inaczej po tym hackowaniu? –

+1

@ivan_bilan na razie działa zgodnie z oczekiwaniami, dzięki. – alecxe

1

W tym przypadku użycia chciałbym po prostu użyć custom filter, tak:

import re 

from bs4 import BeautifulSoup 
from bs4.builder._htmlparser import HTMLParserTreeBuilder 

def myclassfilter(tag): 
    return re.compile(r"^name\-single name\d+$").search(' '.join(tag['class'])) 

bs = """<a class="name-single name692" href="www.example.com"">Example Text</a>""" 
bsObj = BeautifulSoup(bs, "html.parser") 
found_elements = bsObj.find_all(myclassfilter) 

print(found_elements) 
Powiązane problemy