Я работаю над проблемой с очисткой веб-таблицы с помощью Python. Некоторое время я чистил то, что я бы назвал «стандартными» таблицами, и я чувствую, что понимаю это достаточно хорошо. Я определяю стандартную таблицу как имеющую такую структуру:

<table>
<tr class="row-class">
  <th>Bill</th>
  <td>1</td>
  <td>2</td>
  <td>3</td>
  <td>4</td>
</tr>
<tr class="row-class">
  <th>Ben</th>
  <td>2</td>
  <td>3</td>
  <td>4</td>
  <td>1</td>
</tr>
<tr class="row-class">
  <th>Barry</th>
  <td>3</td>
  <td>4</td>
  <td>1</td>
  <td>2</td>
</tr>
</table>

Теперь я наткнулся на экземпляр таблицы, который имеет немного другую структуру, и я не могу понять, как извлечь из него данные в нужном мне формате. Формат, который я сейчас пытаюсь очистить:

<table>
<tr class="row-class">
  <th>Bill</th></tr>
  <tr><td>1</td>
  <td>2</td>
  <td>3</td>
  <td>4</td>
</tr>
<tr class="row-class">
  <th>Ben</th></tr>
  <tr>
  <td>2</td>
  <td>3</td>
  <td>4</td>
  <td>1</td>
</tr>
<tr class="row-class">
  <th>Barry</th></tr>
  <tr>
  <td>3</td>
  <td>4</td>
  <td>1</td>
  <td>2</td>
</tr>
</table>

Результат, который я пытаюсь достичь:

Bill,1,2,3,4
Ben,2,3,4,1
Barry,3,4,1,2

Я предполагаю, что проблема, с которой я сталкиваюсь, заключается в том, что, поскольку заголовок хранится в отдельной строке tr, я получаю только вывод:

Bill
Ben
Barry

Мне интересно, если решение состоит в том, чтобы пройти через строки и определить, является ли следующий тег th или td, а затем выполнить соответствующее действие? Буду признателен за любые советы о том, как код, который я использую для проверки этого, может быть изменен для достижения желаемого результата. Код является:

from bs4 import BeautifulSoup

t_obj = """<tr class="row-class">
  <th>Bill</th></tr>
  <tr><td>1</td>
  <td>2</td>
  <td>3</td>
  <td>4</td>
</tr>
<tr class="row-class">
  <th>Ben</th></tr>
  <tr>
  <td>2</td>
  <td>3</td>
  <td>4</td>
  <td>1</td>
</tr>
<tr class="row-class">
  <th>Barry</th></tr>
  <tr>
  <td>3</td>
  <td>4</td>
  <td>1</td>
  <td>2</td>
</tr>"""


soup = BeautifulSoup(t_obj)

trs = soup.find_all("tr", {"class":"row-class"})

for tr in trs:
    for th in tr.findAll('th'):
        print (th.get_text())
        for td in tr.findAll('td'):
            print(td.get_text())
            print(td.get_text())
2
Andy B 21 Дек 2019 в 02:51

3 ответа

Лучший ответ

Здесь я использую 3 метода для объединения двух тегов <tr>:

  • 1-й метод использует zip() и селектор CSS
  • 2-й метод использует метод BeautifulSoup find_next_sibling()
  • 3-й метод использует zip() и простую нарезку с пользовательским шагом

from bs4 import BeautifulSoup

t_obj = """<tr class="row-class">
  <th>Bill</th></tr>
  <tr><td>1</td>
  <td>2</td>
  <td>3</td>
  <td>4</td>
</tr>
<tr class="row-class">
  <th>Ben</th></tr>
  <tr>
  <td>2</td>
  <td>3</td>
  <td>4</td>
  <td>1</td>
</tr>
<tr class="row-class">
  <th>Barry</th></tr>
  <tr>
  <td>3</td>
  <td>4</td>
  <td>1</td>
  <td>2</td>
</tr>"""


soup = BeautifulSoup(t_obj, 'html.parser')

for tr1, tr2 in zip(soup.select('tr.row-class'), soup.select('tr.row-class ~ tr:not(.row-class)')):
    print( ','.join(tag.get_text() for tag in tr1.select('th') + tr2.select('td')) )

print()

for tr in soup.select('tr.row-class'):
    print( ','.join(tag.get_text() for tag in tr.select('th') + tr.find_next_sibling('tr').select('td')) )

print()

trs = soup.select('tr')
for tr1, tr2 in zip(trs[::2], trs[1::2]):
    print( ','.join(tag.get_text() for tag in tr1.select('th') + tr2.select('td')) )

Печать :

Bill,1,2,3,4
Ben,2,3,4,1
Barry,3,4,1,2

Bill,1,2,3,4
Ben,2,3,4,1
Barry,3,4,1,2

Bill,1,2,3,4
Ben,2,3,4,1
Barry,3,4,1,2
3
Andrej Kesely 21 Дек 2019 в 00:24

Обработать HTML, чтобы соответствовать

from simplified_scrapy.simplified_doc import SimplifiedDoc 
t_obj = """<tr class="row-class">
  <th>Bill</th></tr>
  <tr><td>1</td>
  <td>2</td>
  <td>3</td>
  <td>4</td>
</tr>
<tr class="row-class">
  <th>Ben</th></tr>
  <tr>
  <td>2</td>
  <td>3</td>
  <td>4</td>
  <td>1</td>
</tr>
<tr class="row-class">
  <th>Barry</th></tr>
  <tr>
  <td>3</td>
  <td>4</td>
  <td>1</td>
  <td>2</td>
</tr>"""
doc = SimplifiedDoc()
doc.loadHtml(doc.replaceReg(t_obj,"</tr>\s*<tr>",''))# merge tr
trs = doc.trs # get all tr
for tr in trs:
  tds = tr.children # get td and th
  data = [td.text for td in tds]
  print (data) 

Результат:

['Bill', '1', '2', '3', '4']
['Ben', '2', '3', '4', '1']
['Barry', '3', '4', '1', '2']
0
dabingsou 21 Дек 2019 в 09:40

Вы можете использовать индексирование:

from bs4 import BeautifulSoup as soup
d = soup(html, 'html.parser').find_all('tr')
result = [[d[i].text]+[c.text for c in d[i+1].find_all('td')] for i in range(0, len(d), 2)]

Чтобы напечатать ваш результат:

print('\n'.join(f'{a[1:]},{",".join(b)}' for a, *b in result))

Выход:

Bill,1,2,3,4
Ben,2,3,4,1
Barry,3,4,1,2
0
Ajax1234 21 Дек 2019 в 00:58