Cómo encontrar elementos por clase


386

Tengo problemas para analizar elementos HTML con el atributo "class" usando Beautifulsoup. El código se ve así

soup = BeautifulSoup(sdata)
mydivs = soup.findAll('div')
for div in mydivs: 
    if (div["class"] == "stylelistrow"):
        print div

Recibo un error en la misma línea "después" de que finaliza el script.

File "./beautifulcoding.py", line 130, in getlanguage
  if (div["class"] == "stylelistrow"):
File "/usr/local/lib/python2.6/dist-packages/BeautifulSoup.py", line 599, in __getitem__
   return self._getAttrMap()[key]
KeyError: 'class'

¿Cómo me deshago de este error?

Respuestas:


646

Puede refinar su búsqueda para encontrar solo esos divs con una clase dada usando BS3:

mydivs = soup.findAll("div", {"class": "stylelistrow"})

@ Klaus, ¿y si quiero usar findAll en su lugar?

1
Gracias por esto. No es solo para @class sino para cualquier cosa.
prageeth

41
Esto solo funciona para coincidencias exactas. <.. class="stylelistrow">coincide pero no <.. class="stylelistrow button">.
Wernight

44
@pyCthon Vea la respuesta para @jmunsch, BS ahora admite class_qué funciona correctamente.
Wernight

25
A partir de beautifulsoup4, findAll ahora es find_all
Neoecos

273

De la documentación:

A partir de Beautiful Soup 4.1.2, puede buscar por clase CSS utilizando el argumento de la palabra clave class_ :

soup.find_all("a", class_="sister")

Que en este caso sería:

soup.find_all("div", class_="stylelistrow")

También funcionaría para:

soup.find_all("div", class_="stylelistrowone stylelistrowtwo")

55
También puedes usar listas: soup.find_all("a", ["stylelistrowone", "stylelistrow"])es más seguro si no tienes muchas clases.
Nuno André

44
Esta debería ser la respuesta aceptada, es más correcta y concisa que las alternativas.
goncalopp

1
Suplemento a la respuesta de @ NunoAndré para BeautifulSoup 3: soup.findAll("a", {'class':['stylelistrowone', 'stylelistrow']}).
Brad

55

Actualización: 2016 En la última versión de beautifulsoup, el método 'findAll' ha cambiado de nombre a 'find_all'. Enlace a documentación oficial

Lista de nombres de métodos cambiados

Por lo tanto, la respuesta será

soup.find_all("html_element", class_="your_class_name")

18

Específico de BeautifulSoup 3:

soup.findAll('div',
             {'class': lambda x: x 
                       and 'stylelistrow' in x.split()
             }
            )

Encontrará todo esto:

<div class="stylelistrow">
<div class="stylelistrow button">
<div class="button stylelistrow">

¿Por qué no re.search ('. * Stylelistrow. *', X)?
rjurney

porque entonces stylelistrow2 coincidirá. Mejor comentario es "¿por qué no usar string.find () en lugar de re?"
FlipMcF

2
lambda x: 'stylelistrow' in x.split()es simple y hermoso
fferri

Y odio las expresiones regulares. ¡Gracias! (actualización de respuesta) | mantener la 'xy' para probar Ninguno
FlipMcF

16

Una forma directa sería:

soup = BeautifulSoup(sdata)
for each_div in soup.findAll('div',{'class':'stylelist'}):
    print each_div

Asegúrese de quitar la carcasa de findAll , no es findall


44
Esto solo funciona para coincidencias exactas. <.. class="stylelistrow">coincide pero no <.. class="stylelistrow button">.
Wernight

11

Cómo encontrar elementos por clase

Tengo problemas para analizar elementos html con el atributo "class" usando Beautifulsoup.

Puede encontrar fácilmente por una clase, pero si desea encontrar por la intersección de dos clases, es un poco más difícil,

De la documentación (énfasis agregado):

Si desea buscar etiquetas que coincidan con dos o más clases CSS, debe usar un selector CSS:

css_soup.select("p.strikeout.body")
# [<p class="body strikeout"></p>]

Para que quede claro, esto selecciona solo las etiquetas p que son tachadas y de clase corporal.

Para buscar la intersección de cualquiera en un conjunto de clases (no la intersección, sino la unión), puede dar una lista al class_argumento de la palabra clave (a partir de 4.1.2):

soup = BeautifulSoup(sdata)
class_list = ["stylelistrow"] # can add any other classes to this list.
# will find any divs with any names in class_list:
mydivs = soup.find_all('div', class_=class_list) 

También tenga en cuenta que findAll ha cambiado de nombre de camelCase a Pythonic find_all.


11

Selectores CSS

primer partido de una sola clase

soup.select_one('.stylelistrow')

lista de partidos

soup.select('.stylelistrow')

clase compuesta (es decir, Y otra clase)

soup.select_one('.stylelistrow.otherclassname')
soup.select('.stylelistrow.otherclassname')

Los espacios en los nombres de clase compuestos, por ejemplo, class = stylelistrow otherclassnamese reemplazan con ".". Puedes continuar agregando clases.

lista de clases (OR - coincide con el presente

soup.select_one('.stylelistrow, .otherclassname')
soup.select('.stylelistrow, .otherclassname')

bs4 4.7.1 +

Clase específica cuyo innerTextcontiene una cadena

soup.select_one('.stylelistrow:contains("some string")')
soup.select('.stylelistrow:contains("some string")')

Clase específica que tiene un cierto elemento hijo, por ejemplo, aetiqueta

soup.select_one('.stylelistrow:has(a)')
soup.select('.stylelistrow:has(a)')

5

A partir de BeautifulSoup 4+,

Si tiene un solo nombre de clase, puede pasar el nombre de la clase como parámetro como:

mydivs = soup.find_all('div', 'class_name')

O si tiene más de un nombre de clase, simplemente pase la lista de nombres de clase como parámetro como:

mydivs = soup.find_all('div', ['class1', 'class2'])

3

Intente verificar si el div tiene un atributo de clase primero, como este:

soup = BeautifulSoup(sdata)
mydivs = soup.findAll('div')
for div in mydivs:
    if "class" in div:
        if (div["class"]=="stylelistrow"):
            print div

1
Eso no funciona Supongo que su enfoque fue correcto, pero la cuarta línea no funciona según lo previsto.
Neo

1
Ah, pensé que div funcionaba como un diccionario, no estoy muy familiarizado con Beautiful Soup, así que era solo una suposición.
Mew

3

Esto funciona para mí para acceder al atributo de clase (en beautifulsoup 4, contrario a lo que dice la documentación). KeyError viene una lista que se devuelve, no un diccionario.

for hit in soup.findAll(name='span'):
    print hit.contents[1]['class']

3

lo siguiente funcionó para mí

a_tag = soup.find_all("div",class_='full tabpublist')

1

Esto funcionó para mí:

for div in mydivs:
    try:
        clazz = div["class"]
    except KeyError:
        clazz = ""
    if (clazz == "stylelistrow"):
        print div

1

Alternativamente, podemos usar lxml, es compatible con xpath y muy rápido.

from lxml import html, etree 

attr = html.fromstring(html_text)#passing the raw html
handles = attr.xpath('//div[@class="stylelistrow"]')#xpath exresssion to find that specific class

for each in handles:
    print(etree.tostring(each))#printing the html as string

0

Esto debería funcionar:

soup = BeautifulSoup(sdata)
mydivs = soup.findAll('div')
for div in mydivs: 
    if (div.find(class_ == "stylelistrow"):
        print div

0

Otras respuestas no me funcionaron.

En otras respuestas, findAllse está utilizando en el objeto de sopa en sí, pero necesitaba una forma de hacer una búsqueda por nombre de clase en objetos dentro de un elemento específico extraído del objeto que obtuve después de hacerlo findAll.

Si está intentando hacer una búsqueda dentro de elementos HTML anidados para obtener objetos por nombre de clase, intente a continuación:

# parse html
page_soup = soup(web_page.read(), "html.parser")

# filter out items matching class name
all_songs = page_soup.findAll("li", "song_item")

# traverse through all_songs
for song in all_songs:

    # get text out of span element matching class 'song_name'
    # doing a 'find' by class name within a specific song element taken out of 'all_songs' collection
    song.find("span", "song_name").text

Puntos a tener en cuenta:

  1. No estoy definiendo explícitamente la búsqueda para que esté en el atributo 'clase' findAll("li", {"class": "song_item"}), ya que es el único atributo en el que estoy buscando y buscará de forma predeterminada el atributo de clase si no dice exclusivamente en qué atributo desea encontrar.

  2. Cuando haces un findAllo find, el objeto resultante es de clase, bs4.element.ResultSetque es una subclase de list. Puede utilizar todos los métodos de ResultSet, dentro de cualquier número de elementos anidados (siempre que sean de tipo ResultSet) para hacer una búsqueda o encontrar todos.

  3. Mi versión BS4 - 4.9.1, versión de Python - 3.8.1


0

Lo siguiente debería funcionar

soup.find('span', attrs={'class':'totalcount'})

reemplace 'totalcount' con el nombre de su clase y 'span' con la etiqueta que está buscando. Además, si su clase contiene varios nombres con espacio, simplemente elija uno y úselo.

PD Esto encuentra el primer elemento con criterios dados. Si desea encontrar todos los elementos, reemplace 'find' con 'find_all'.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.