Jak znaleźć elementy według klasy

386

Mam problem z analizowaniem elementów HTML z atrybutem „class” za pomocą Beautifulsoup. Kod wygląda następująco

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

W tym samym wierszu pojawia się błąd „po” zakończeniu skryptu.

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'

Jak pozbyć się tego błędu?

Neo
źródło

Odpowiedzi:

646

Możesz zawęzić wyszukiwanie, aby znaleźć tylko te div z daną klasą, używając BS3:

mydivs = soup.findAll("div", {"class": "stylelistrow"})
Klaus Byskov Pedersen
źródło
@ Klaus - co jeśli chcę zamiast tego użyć findAll?
1
Dzięki za to. To nie tylko dla @class, ale dla czegokolwiek.
prageeth
41
Działa to tylko w przypadku dokładnych dopasowań. <.. class="stylelistrow">pasuje, ale nie <.. class="stylelistrow button">.
Wernight
4
@pyCthon Zobacz odpowiedź na @jmunsch, BS obsługuje teraz, class_który działa poprawnie.
Wernight
25
Począwszy od beautifulsoup4, findAll jest teraz find_all
Neoecos
273

Z dokumentacji:

Począwszy od Beautiful Soup 4.1.2, możesz wyszukiwać według klasy CSS za pomocą argumentu słowa kluczowego class_ :

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

Co w tym przypadku byłoby:

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

Będzie również działał dla:

soup.find_all("div", class_="stylelistrowone stylelistrowtwo")
jmunsch
źródło
5
Możesz także użyć list: soup.find_all("a", ["stylelistrowone", "stylelistrow"])jest bezpieczniej, jeśli nie masz wielu klas.
Nuno André
4
To powinna być zaakceptowana odpowiedź, jest ona bardziej poprawna i zwięzła niż alternatywy.
goncalopp
1
Suplement do @ odpowiedź NunoAndré za BeautifulSoup 3: soup.findAll("a", {'class':['stylelistrowone', 'stylelistrow']}).
Brad
55

Aktualizacja: 2016 W najnowszej wersji beautifulsoup zmieniono nazwę metody „findAll” na „find_all”. Link do oficjalnej dokumentacji

Zmieniono listę nazw metod

Stąd odpowiedź będzie

soup.find_all("html_element", class_="your_class_name")
Pan, władca
źródło
18

Specyficzne dla BeautifulSoup 3:

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

Znajdziesz wszystkie te:

<div class="stylelistrow">
<div class="stylelistrow button">
<div class="button stylelistrow">
FlipMcF
źródło
Dlaczego nie przeszukać ponownie ('. * Stylelistrow. *', X)?
rjurney
ponieważ wtedy stylelistrow2 będzie pasować. Lepszym komentarzem jest „dlaczego nie użyć string.find () zamiast re?”
FlipMcF,
2
lambda x: 'stylelistrow' in x.split()jest prosty i piękny
fferri
I nienawidzę wyrażeń regularnych. Dziękuję Ci! (aktualizacja odpowiedzi) | trzymanie „x i” w celu przetestowania na brak
FlipMcF
16

Prostym sposobem byłoby:

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

Upewnij się, że weźmiesz obudowę findAll , to nie jest findall

Konark Modi
źródło
4
Działa to tylko w przypadku dokładnych dopasowań. <.. class="stylelistrow">pasuje, ale nie <.. class="stylelistrow button">.
Wernight
11

Jak znaleźć elementy według klasy

Mam problem z analizowaniem elementów HTML z atrybutem „class” za pomocą Beautifulsoup.

Możesz łatwo znaleźć według jednej klasy, ale jeśli chcesz znaleźć na przecięciu dwóch klas, jest to trochę trudniejsze,

Z dokumentacji (wyróżnienie dodane):

Jeśli chcesz wyszukać tagi, które pasują do dwóch lub więcej klas CSS, powinieneś użyć selektora CSS:

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

Dla jasności zaznacza tylko tagi p, które są zarówno przekreślone, jak i klasy ciała.

Aby znaleźć przecięcie dowolnego z zestawu klas (nie przecięcia, ale unii), możesz podać listę class_argumentów słów kluczowych (od 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) 

Zauważ też, że zmieniono nazwę findAll z camelCase na bardziej Pythonic find_all.

Aaron Hall
źródło
11

Selektory CSS

pierwszy mecz pojedynczej klasy

soup.select_one('.stylelistrow')

lista dopasowań

soup.select('.stylelistrow')

klasa złożona (tj. ORAZ inna klasa)

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

Spacje w nazwach klas złożonych np. class = stylelistrow otherclassnameSą zastępowane przez „.”. Możesz kontynuować dodawanie klas.

lista klas (LUB - dopasuj dowolną z obecnych

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

bs4 4.7.1 +

Określona klasa, która innerTextzawiera ciąg znaków

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

Określona klasa, która ma określony element potomny, np. aTag

soup.select_one('.stylelistrow:has(a)')
soup.select('.stylelistrow:has(a)')
QHarr
źródło
5

Począwszy od BeautifulSoup 4+,

Jeśli masz jedną nazwę klasy, możesz po prostu przekazać nazwę klasy jako parametr taki jak:

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

Lub jeśli masz więcej niż jedną nazwę klasy, po prostu przekaż listę nazw klas jako parametr taki jak:

mydivs = soup.find_all('div', ['class1', 'class2'])
Shivam Shah
źródło
3

Spróbuj najpierw sprawdzić, czy div ma najpierw atrybut klasy, na przykład:

soup = BeautifulSoup(sdata)
mydivs = soup.findAll('div')
for div in mydivs:
    if "class" in div:
        if (div["class"]=="stylelistrow"):
            print div
Miauczenie
źródło
1
To nie działa. Myślę, że twoje podejście było słuszne, ale 4. linia nie działa zgodnie z przeznaczeniem.
Neo
1
Ach, myślałem, że div działa jak słownik, nie znam się na Pięknej Zupie, więc to tylko przypuszczenie.
Mew
3

To działa dla mnie, aby uzyskać dostęp do atrybutu klasy (w beautifulsoup 4, w przeciwieństwie do tego, co mówi dokumentacja). KeyError zawiera zwracaną listę, a nie słownik.

for hit in soup.findAll(name='span'):
    print hit.contents[1]['class']
Stgltz
źródło
3

następujące działało dla mnie

a_tag = soup.find_all("div",class_='full tabpublist')
Preetham DP
źródło
1

To działało dla mnie:

for div in mydivs:
    try:
        clazz = div["class"]
    except KeyError:
        clazz = ""
    if (clazz == "stylelistrow"):
        print div
Larry Symms
źródło
1

Alternatywnie możemy użyć lxml, obsługuje xpath i bardzo szybko!

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
źródło
0

To powinno działać:

soup = BeautifulSoup(sdata)
mydivs = soup.findAll('div')
for div in mydivs: 
    if (div.find(class_ == "stylelistrow"):
        print div
Niebieskie niebo
źródło
0

Inne odpowiedzi nie działały dla mnie.

W innych odpowiedziach findAlljest on używany na samym obiekcie zupy, ale potrzebowałem sposobu, aby wykonać wyszukiwanie według nazwy klasy na obiektach wewnątrz określonego elementu wydobytego z obiektu, który uzyskałem po wykonaniu findAll.

Jeśli próbujesz przeprowadzić wyszukiwanie w zagnieżdżonych elementach HTML, aby uzyskać obiekty według nazwy klasy, spróbuj poniżej -

# 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

Punkty do odnotowania:

  1. Nie definiuję jawnie, że wyszukiwanie ma być oparte na atrybucie „klasa” findAll("li", {"class": "song_item"}), ponieważ jest to jedyny atrybut, którego szukam i domyślnie będzie szukał atrybutu klasy, jeśli nie powiesz wyłącznie, który atrybut chcesz znaleźć.

  2. Kiedy zrobić findAllalbo find, otrzymany przedmiot jest z klasy bs4.element.ResultSet, która jest podklasą list. Możesz użyć wszystkich metod ResultSetw dowolnej liczbie elementów zagnieżdżonych (o ile są one typu ResultSet), aby znaleźć lub znaleźć wszystko.

  3. Moja wersja BS4 - 4.9.1, wersja Python - 3.8.1

ZeroFlex
źródło
0

Następujące powinny działać

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

zamień „totalcount” na nazwę swojej klasy i „span” na szukany tag. Ponadto, jeśli twoja klasa zawiera wiele nazw ze spacją, po prostu wybierz jedną i użyj.

PS Znajduje pierwszy element z podanymi kryteriami. Jeśli chcesz znaleźć wszystkie elementy, zamień „find” na „find_all”.

Hari Sudhan
źródło