python regex python-2.7 beautifulsoup html-parsing

python - BeautifulSoup devuelve una lista vacía al buscar por nombres de clase compuestos



regex python-2.7 (2)

BeautifulSoup devuelve una lista vacía cuando busca por nombres de clase compuestos usando regex.

Ejemplo:

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*)$"))

Necesito que la selección de clase sea muy precisa. ¿Algunas ideas?


Desafortunadamente, cuando intenta hacer una coincidencia de expresión regular en un valor de atributo de clase que contiene varias clases, BeautifulSoup aplicaría la expresión regular a cada clase por separado. Estos son los temas relevantes sobre el problema:

Todo esto se debe a que la class es un atributo de valores múltiples muy especial y cada vez que analiza HTML, uno de los constructores de árboles de BeautifulSoup (según la elección del analizador) divide internamente un valor de cadena de clase en una lista de clases (cita de HTMLTreeBuilder de 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.

Hay varias soluciones alternativas, pero aquí hay una alternativa: vamos a pedirle a BeautifulSoup no maneje la class como un atributo de valores múltiples al hacer nuestro simple generador de árbol personalizado:

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)

En este caso, la expresión regular se aplicaría a un valor de atributo de class como un todo.

Alternativamente, puede analizar el HTML con las funciones xml habilitadas (si esto es aplicable):

soup = BeautifulSoup(data, "xml")

También puede usar selectores CSS y hacer coincidir todos los elementos con name-single clase de name-single y una clase con "nombre":

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

Luego puede aplicar la expresión regular manualmente si es necesario:

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)


Para este caso de uso, simplemente usaría un filtro personalizado , así:

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)