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?

Neo
fuente

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 Byskov Pedersen
fuente
@ 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")
jmunsch
fuente
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")
señor
fuente
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">
FlipMcF
fuente
¿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

Konark Modi
fuente
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.

Aaron Hall
fuente
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)')
QHarr
fuente
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'])
Shivam Shah
fuente
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
Maullar
fuente
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']
Stgltz
fuente
3

lo siguiente funcionó para mí

a_tag = soup.find_all("div",class_='full tabpublist')
Preetham DP
fuente
1

Esto funcionó para mí:

for div in mydivs:
    try:
        clazz = div["class"]
    except KeyError:
        clazz = ""
    if (clazz == "stylelistrow"):
        print div
Larry Symms
fuente
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
Sohan Das
fuente
0

Esto debería funcionar:

soup = BeautifulSoup(sdata)
mydivs = soup.findAll('div')
for div in mydivs: 
    if (div.find(class_ == "stylelistrow"):
        print div
Cielo azul
fuente
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

ZeroFlex
fuente
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'.

hari sudhan
fuente