Comment trouver des éléments par classe

386

J'ai du mal à analyser les éléments HTML avec l'attribut "class" à l'aide de Beautifulsoup. Le code ressemble à ceci

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

J'obtiens une erreur sur la même ligne "après" la fin du 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'

Comment puis-je me débarrasser de cette erreur?

Néo
la source

Réponses:

646

Vous pouvez affiner votre recherche pour trouver uniquement les divs d'une classe donnée en utilisant BS3:

mydivs = soup.findAll("div", {"class": "stylelistrow"})
Klaus Byskov Pedersen
la source
@ Klaus- et si je veux utiliser findAll à la place?
1
Merci pour cela. Ce n'est pas seulement pour @class mais pour n'importe quoi.
prageeth
41
Cela ne fonctionne que pour les correspondances exactes. <.. class="stylelistrow">correspond mais pas <.. class="stylelistrow button">.
Wernight
4
@pyCthon Voir la réponse pour @jmunsch, BS prend désormais en charge class_ce qui fonctionne correctement.
Wernight
25
À partir de beautifulsoup4, le findAll est maintenant find_all
Neoecos
273

De la documentation:

Depuis Beautiful Soup 4.1.2, vous pouvez rechercher par classe CSS en utilisant l'argument mot-clé class_ :

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

Ce qui dans ce cas serait:

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

Cela fonctionnerait également pour:

soup.find_all("div", class_="stylelistrowone stylelistrowtwo")
jmunsch
la source
5
Vous pouvez également utiliser des listes: soup.find_all("a", ["stylelistrowone", "stylelistrow"])c'est plus sûr si vous n'avez pas beaucoup de classes.
Nuno André
4
Cela devrait être la réponse acceptée, c'est à la fois plus correct et concis que les alternatives.
goncalopp
1
Supplément @ réponse de NunoAndré pour BeautifulSoup 3: soup.findAll("a", {'class':['stylelistrowone', 'stylelistrow']}).
Brad
55

Mise à jour: 2016 Dans la dernière version de beautifulsoup, la méthode 'findAll' a été renommée en 'find_all'. Lien vers la documentation officielle

Liste des noms de méthode modifiée

La réponse sera donc

soup.find_all("html_element", class_="your_class_name")
suzerain
la source
18

Spécifique à BeautifulSoup 3:

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

Trouvera tous ces éléments:

<div class="stylelistrow">
<div class="stylelistrow button">
<div class="button stylelistrow">
FlipMcF
la source
Pourquoi ne pas re.search ('. * Stylelistrow. *', X)?
rjurney
car alors stylelistrow2 correspondra. Un meilleur commentaire est "pourquoi ne pas utiliser string.find () au lieu de re?"
FlipMcF
2
lambda x: 'stylelistrow' in x.split()est simple et beau
fferri
Et je déteste les regex. Je vous remercie! (mise à jour de la réponse) | garder le 'x et' pour tester None
FlipMcF
16

Une façon simple serait:

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

Assurez-vous de prendre le boîtier de findAll , ce n'est pas findall

Konark Modi
la source
4
Cela ne fonctionne que pour les correspondances exactes. <.. class="stylelistrow">correspond mais pas <.. class="stylelistrow button">.
Wernight
11

Comment trouver des éléments par classe

J'ai du mal à analyser les éléments html avec l'attribut "class" à l'aide de Beautifulsoup.

Vous pouvez facilement trouver par une classe, mais si vous voulez trouver par l'intersection de deux classes, c'est un peu plus difficile,

D'après la documentation (emphase ajoutée):

Si vous souhaitez rechercher des balises qui correspondent à deux ou plusieurs classes CSS, vous devez utiliser un sélecteur CSS:

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

Pour être clair, cela ne sélectionne que les balises p qui sont à la fois barrées et classe de corps.

Pour rechercher l'intersection de tout dans un ensemble de classes (pas l'intersection, mais l'union), vous pouvez donner une liste à l' class_argument mot - clé (à 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) 

Notez également que findAll a été renommé de camelCase en plus Pythonic find_all.

Aaron Hall
la source
11

Sélecteurs CSS

premier match en classe unique

soup.select_one('.stylelistrow')

liste des correspondances

soup.select('.stylelistrow')

classe composée (c.-à-d. ET une autre classe)

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

Les espaces dans les noms de classe composés, par exemple, class = stylelistrow otherclassnamesont remplacés par ".". Vous pouvez continuer à ajouter des classes.

liste des classes (OU - correspond à celle qui est présente

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

bs4 4.7.1 +

Classe spécifique dont innerText contient une chaîne

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

Classe spécifique qui a un certain élément enfant, par exemple une abalise

soup.select_one('.stylelistrow:has(a)')
soup.select('.stylelistrow:has(a)')
QHarr
la source
5

Depuis BeautifulSoup 4+,

Si vous avez un seul nom de classe, vous pouvez simplement passer le nom de classe en paramètre comme:

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

Ou si vous avez plusieurs noms de classe, passez simplement la liste des noms de classe en paramètre comme:

mydivs = soup.find_all('div', ['class1', 'class2'])
Shivam Shah
la source
3

Essayez de vérifier si le div a un attribut de classe en premier, comme ceci:

soup = BeautifulSoup(sdata)
mydivs = soup.findAll('div')
for div in mydivs:
    if "class" in div:
        if (div["class"]=="stylelistrow"):
            print div
Miauler
la source
1
Ça ne marche pas. Je suppose que votre approche était bonne, mais la 4ème ligne ne fonctionne pas comme prévu.
Neo
1
Ah je pensais que div fonctionnait comme un dictionnaire, je ne suis pas vraiment familier avec Beautiful Soup alors c'était juste une supposition.
Mew
3

Cela fonctionne pour moi d'accéder à l'attribut class (sur beautifulsoup 4, contrairement à ce que dit la documentation). Le KeyError vient une liste renvoyée pas un dictionnaire.

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

ce qui suit a fonctionné pour moi

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

Cela a fonctionné pour moi:

for div in mydivs:
    try:
        clazz = div["class"]
    except KeyError:
        clazz = ""
    if (clazz == "stylelistrow"):
        print div
Larry Symms
la source
1

Alternativement, nous pouvons utiliser lxml, il prend en charge xpath et très rapidement!

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
la source
0

Cela devrait fonctionner:

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

D'autres réponses n'ont pas fonctionné pour moi.

Dans d'autres réponses, le findAllest utilisé sur l'objet soupe lui-même, mais j'avais besoin d'un moyen de faire une recherche par nom de classe sur des objets à l'intérieur d'un élément spécifique extrait de l'objet que j'ai obtenu après avoir faitfindAll .

Si vous essayez de faire une recherche dans des éléments HTML imbriqués pour obtenir des objets par nom de classe, essayez ci-dessous -

# 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

Points à noter:

  1. Je ne définis pas explicitement la recherche sur l'attribut 'class' findAll("li", {"class": "song_item"}), car c'est le seul attribut sur lequel je recherche et il recherchera par défaut l'attribut class si vous ne dites pas exclusivement sur quel attribut vous voulez trouver.

  2. Lorsque vous effectuez un findAllou find, l'objet résultant est de classe bs4.element.ResultSetqui est une sous-classe de list. Vous pouvez utiliser toutes les méthodes de ResultSet, à l'intérieur de n'importe quel nombre d'éléments imbriqués (tant qu'ils sont de type ResultSet) pour effectuer une recherche ou trouver tout.

  3. Ma version BS4 - 4.9.1, version Python - 3.8.1

ZeroFlex
la source
0

Les éléments suivants devraient fonctionner

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

remplacez «totalcount» par le nom de votre classe et «span» par la balise que vous recherchez. De plus, si votre classe contient plusieurs noms avec espace, choisissez-en un et utilisez-le.

PS Ceci trouve le premier élément avec des critères donnés. Si vous voulez trouver tous les éléments, remplacez 'find' par 'find_all'.

hari sudhan
la source