Извлеките текст td из тега td colspan с помощью bs4

#python #html #python-3.x #beautifulsoup

Вопрос:

Моя цель здесь-извлечь все содержимое из тегов «td» под colspan. Я только начинаю работать с bs4. До сих пор я могу извлечь все «trs» со страницы, однако я хотел бы получить информацию только в классе info_row с именем Диски. любая помощь с логикой и / или кодом очень ценится.

Это выдержка из HTML

 <tbody>
    <tr>
        <td colspan="100%" class="info_row">Disks</td>
    </tr>
    <tr>
        <td> sda </td>
        <td> 123456 </td>
        <td> abcdefg </td>
    </tr>
    <tr>
        <td> sdb </td>
        <td> 123456 </td>
        <td> abcdefg </td>
    </tr>
</tbody>
 

Желаемый результат:

 sda 123456 abcdefg
sbd 123456 abcdefg
 

до сих пор у меня есть следующее:

 src = open("my_page.html").read()
soup = BeautifulSoup(src, "html.parser")
tbody = soup.findAll("tbody")
for tr in tbody:
      tds = tr.findAll('td')
      for td in tds:
        print(td.text)
 

Комментарии:

1. Я изменил код, чтобы проверить, имеет ли тег td определенный класс и текст, упомянутый в вашем вопросе

Ответ №1:

Начальный цикл for находит индекс тега tr , в котором есть тег td class="info_row" , и текст как Disks , используя этот индекс, вы можете просто найти индекс следующего тега tr, текст которого вы хотите извлечь, и использовать его в качестве индекса списка, чтобы применить get_text() метод для извлечения текста внутри тегов:

 from bs4 import BeautifulSoup

src = '''<tbody>
    <tr>
        <td colspan="100%" class="info_row">Disks</td>
    </tr>
    <tr>
        <td> sda </td>
        <td> 123456 </td>
        <td> abcdefg </td>
    </tr>
    <tr>
        <td> sdb </td>
        <td> 123456 </td>
        <td> abcdefg </td>
    </tr>
</tbody>'''

soup = BeautifulSoup(src, "html.parser")
trs = soup.findAll("tr")

for i in range(len(trs)):
    if trs[i].td:
        if 'class' in trs[i].td.attrs and trs[i].td.text == 'Disks':
            if "info_row" in trs[i].td.attrs['class']:
                idx = i
                break

print(' '.join(trs[idx 1].get_text(' ').split()))
print(' '.join(trs[idx 2].get_text(' ').split()))
 

Выход

 sda 123456 abcdefg
sdb 123456 abcdefg
 

Чтобы получить отдельные элементы внутри тегов, вы можете использовать этот код, потому trs[idx 1].get_text(' ').split() что на самом деле это список:

 for item in trs[idx 1].get_text(' ').split():
    print(item)

for item in trs[idx 2].get_text(' ').split():
    print(item)
 

Выход

 sda
123456
abcdefg
sdb
123456
abcdefg
 

Комментарии:

1. Это отлично работает и очень легко понять! я получал «AttributeError: объект ‘NoneType’ не имеет атрибута ‘attrs'», но я смог обойти это, добавив блок try/except. Еще раз спасибо

2. один вопрос, если ты не против. можно ли также получить доступ к текстам td по отдельности? например, если бы я просто хотел» sda » или «123456», моя цель-сохранить эти значения в словаре.

3. @mondragonfx Да, безусловно, trs[idx 1].get_text(' ').split() это список, и вы можете получить отдельный элемент из этого списка в соответствии с вашими требованиями

4. @mondragonfx Я изменил код, чтобы решить проблему «Ошибка атрибута: объект ‘NoneType’ не имеет атрибута ‘attrs'».

Ответ №2:

В этом примере из таблицы выбираются только строки, отличные от»colspan» :

 from bs4 import BeautifulSoup


html_doc = """<tbody>
    <tr>
        <td colspan="100%" class="info_row">Disks</td>
    </tr>
    <tr>
        <td> sda </td>
        <td> 123456 </td>
        <td> abcdefg </td>
    </tr>
    <tr>
        <td> sdb </td>
        <td> 123456 </td>
        <td> abcdefg </td>
    </tr>
</tbody>"""

soup = BeautifulSoup(html_doc, "html.parser")

for tr in soup.select("tr:not(:has([colspan]))"):
    print(*[td.get_text(strip=True) for td in tr.select("td")])
 

С принтами:

 sda 123456 abcdefg
sdb 123456 abcdefg
 

Или: вы можете выбрать все строки, которые находятся после строки, содержащей ячейку «Диски».:

 for tr in soup.select('tr:has(> td:contains("Disks")) ~ tr'):
    print(*[td.get_text(strip=True) for td in tr.select("td")])
 

РЕДАКТИРОВАТЬ: Использование lambda :

 for tr in soup.find(lambda tag: tag.name == "tr" and tag.text.strip() == "Disks").find_next_siblings("tr"):
    print(*[td.get_text(strip=True) for td in tr.select("td")])
 

Комментарии:

1. Спасибо за ваш ответ, похоже, это работа, как и ожидалось. возможно ли, чтобы td:содержит(«Диски») учитывал регистр/слово? у меня есть другие tds на HTML-странице, в которых есть слово «диски», и это, похоже, тоже их печатает. в противном случае это в точку. я действительно ценю это.

2. @mondragonfx, которым вы можете воспользоваться lambda , я обновил свой ответ.