Як знайти елементи за класом


386

У мене виникають проблеми з розбором елементів HTML з атрибутом "class" за допомогою Beautifulsoup. Код виглядає приблизно так

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

Я отримую помилку в тому ж рядку "після" сценарій закінчується.

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'

Як я можу позбутися цієї помилки?

Відповіді:


646

Ви можете вдосконалити пошук, щоб знайти лише ті діви із заданим класом за допомогою BS3:

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

@ Klaus - що робити, якщо замість цього я хочу використовувати findAll?

1
Дякую за це Це не лише для @class, але й для будь-чого.
prageeth

41
Це працює лише для точних відповідностей. <.. class="stylelistrow">сірники, але ні <.. class="stylelistrow button">.
Wernight

4
@pyCthon Дивіться відповідь на @jmunsch, BS тепер підтримує, class_що працює належним чином.
Wernight

25
Станом на Beautifulsoup4, findAll зараз знайдено all
Neoecos

273

З документації:

Станом на Beautiful Soup 4.1.2, ви можете шукати за класом CSS, використовуючи аргумент ключового слова class_ :

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

Що в цьому випадку буде:

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

Він також працюватиме для:

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

5
Ви також можете використовувати списки: soup.find_all("a", ["stylelistrowone", "stylelistrow"])Це безпечніше, якщо у вас не так багато занять.
Нуно Андре

4
Це має бути прийнятою відповіддю, це і правильніше і стисліше, ніж альтернативи.
goncalopp

1
Доповнення до @ відповідь NunoAndré для BeautifulSoup 3: soup.findAll("a", {'class':['stylelistrowone', 'stylelistrow']}).
Бред


18

Характерно для BeautifulSoup 3:

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

Ви знайдете все це:

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

Чому б не повторно шукати ('. * Stylelistrow. *', X)?
rjurney

тому що тоді stylelistrow2 відповідатиме. Кращий коментар - "чому б не використовувати string.find () замість повторно?"
FlipMcF

2
lambda x: 'stylelistrow' in x.split()просто і красиво
fferri

І я ненавиджу реджекси. Дякую! (оновлення відповіді) | зберігаючи 'x і' для тестування на None
FlipMcF

16

Прямий шлях вперед:

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

Переконайтесь, що ви приймаєте кожух findAll , його не findall


4
Це працює лише для точних відповідностей. <.. class="stylelistrow">сірники, але ні <.. class="stylelistrow button">.
Wernight

11

Як знайти елементи за класом

У мене виникають проблеми з розбором елементів html з атрибутом "class" за допомогою Beautifulsoup.

Ви можете легко знайти один клас, але якщо ви хочете знайти їх на перетині двох класів, це трохи складніше,

З документації (наголос додано):

Якщо ви хочете шукати теги, які відповідають двом або більше класам CSS, слід скористатися селектором CSS:

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

Щоб було зрозуміло, це вибирає лише теги p, які є як закресленими, так і класними.

Щоб знайти перетин будь-якого у наборі класів (не перетину, а об'єднання), ви можете надати список class_аргументу ключових слів (станом на 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) 

Також зауважте, що findAll було перейменовано з верблюда на випадок у більш пітонічний find_all.


11

CSS-селектори

однокласний перший матч

soup.select_one('.stylelistrow')

список збігів

soup.select('.stylelistrow')

складний клас (тобто ТА інший клас)

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

Пробіли у складених назвах класів, наприклад class = stylelistrow otherclassname, замінюються на ".". Ви можете продовжувати додавати заняття.

список класів (АБО - відповідність залежно від того, хто є

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

bs4 4.7.1 +

Специфічний клас, який innerTextмістить рядок

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

Специфічний клас, який містить певний дочірній елемент, наприклад aтег

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

5

Станом на BeautifulSoup 4+,

Якщо у вас є одне ім’я класу, ви можете просто передати ім'я класу як такий параметр, як:

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

Або якщо у вас є кілька імен класів, просто передайте список назв класів як такий параметр, як:

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

3

Спробуйте перевірити, чи є в div спочатку атрибут класу, як це:

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

1
Це не працює. Я думаю, ваш підхід був правильним, але 4-й рядок не працює за призначенням.
Нео

1
Ах, я думав, дів працював як словник, я не дуже знайомий з Beautiful Soup, тому це була лише здогадка.
Mew

3

Це працює для мене, щоб отримати доступ до атрибуту класу (на Beautifulsoup 4, всупереч тому, що йдеться в документації). KeyError приходить список, який повертається не словником.

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

3

наступне працювало для мене

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


1

Крім того, ми можемо використовувати lxml, він підтримує xpath і дуже швидко!

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

Це має працювати:

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

0

Інші відповіді для мене не спрацювали.

В інших відповідях findAllвживається на самому об'єкті супу, але мені знадобився спосіб зробити пошук за назвою класу на об'єктах всередині певного елемента, вилученого з об'єкта, який я отримав після виконання findAll.

Якщо ви намагаєтеся здійснити пошук всередині вкладених елементів HTML, щоб отримати об’єкти за назвою класу, спробуйте нижче -

# 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

Бали, які слід зазначити:

  1. Я чітко не визначаю пошук за атрибутом 'class' findAll("li", {"class": "song_item"}), оскільки це єдиний атрибут, за яким я шукаю, і він за замовчуванням шукає атрибут класу, якщо ви не вказуєте виключно, який атрибут ви хочете знайти.

  2. Коли ви робите findAllабо find, отриманий об'єкт класу, bs4.element.ResultSetякий є підкласом list. Ви можете використовувати всі способи ResultSetвсередині будь-якої кількості вкладених елементів (до тих пір, поки вони мають тип ResultSet), щоб знайти або знайти всі.

  3. Моя версія BS4 - 4.9.1, версія Python - 3.8.1


0

Наступне має працювати

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

замініть "totalcount" на ім'я вашого класу та "span" на тег, який ви шукаєте. Крім того, якщо ваш клас містить кілька імен з простором, просто виберіть одне і використовуйте.

PS Це знаходить перший елемент із заданими критеріями. Якщо ви хочете знайти всі елементи, замініть "find" на "find_all".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.