красивый суп не разбирать данные вложенных таблиц - PullRequest
3 голосов
/ 30 апреля 2019

У меня есть структура вложенных таблиц. Я использую приведенный ниже код для анализа данных.

for row in table.find_all("tr")[1:][:-1]:
    for td in row.find_all("td")[1:]:
        dataset = td.get_text()

Проблема здесь в том, что когда есть вложенные таблицы, как в моем случае, есть таблицы внутри <td></td>, поэтому они анализируются снова после первоначального анализа, так как я использую find_all(tr) и find_all(td). Так как же избежать анализа вложенной таблицы, поскольку она уже проанализирована?

Введите:

<table>
<tr>
   <td>1</td><td>2</td>
</tr>
<tr>
   <td>3</td><td>4</td>
</tr>
<tr>
  <td>5 
    <table><tr><td>11</td><td>22</td></tr></table>
      6
  </td>
</tr>
</table>

Ожидаемый результат:

1  2
3  4
5  
11 22
6

Но то, что я получаю, это:

1 2
3 4
5
11 22
11 22
6

То есть внутренняя таблица снова анализируется.

Технические характеристики:
beautifulsoup4 == 4.6.3

Порядок данных должен быть сохранен, и содержимое может быть любым, включая любые буквенно-цифровые символы.

Ответы [ 4 ]

2 голосов
/ 05 июня 2019

Я пробовал с методом findChilden () и каким-то образом удалось вывести результат. Я не уверен, поможет ли это вам в любых других обстоятельствах.

from bs4 import BeautifulSoup
data='''<table>
<tr>
   <td>1</td><td>2</td>
</tr>
<tr>
   <td>3</td><td>4</td>
</tr>
<tr>
  <td>5 
    <table><tr><td>11</td><td>22</td></tr></table>
      6
  </td>
</tr>
</table>'''


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

for child in soup.find('table').findChildren("tr" , recursive=False):
  tdlist = []
  if child.find('table'):
     for td in child.findChildren("td", recursive=False):
         print(td.next_element.strip())
         for td1 in td.findChildren("table", recursive=False):
             for child1 in td1.findChildren("tr", recursive=False):
                 for child2 in child1.findChildren("td", recursive=False):
                     tdlist.append(child2.text)
                 print(' '.join(tdlist))
                 print(child2.next_element.next_element.strip())
  else:

     for td in child.findChildren("td" , recursive=False):
         tdlist.append(td.text)
     print(' '.join(tdlist))

Вывод:

1 2
3 4
5
11 22
6

РЕДАКТИРОВАНИЕ для объяснения

Шаг 1:

При использовании findChilden() внутри таблицы сначала возвращается 3 записи.

for child in soup.find('table').findChildren("tr", recursive=False):
    print(child)

Вывод:

<tr>
<td>1</td><td>2</td>
</tr>
<tr>
<td>3</td><td>4</td>
</tr>
<tr>
<td>5 
        <table><tr><td>11</td><td>22</td></tr></table>
          6
      </td>
</tr>

Шаг 2:

Убедитесь, что у любого ребенка есть тег <table> и выполните некоторые действия.

 if child.find('table'):

Шаг 3:

Выполните шаг 1 и используйте findChilden(), чтобы получить <td> тег.

Как только вы получите <td> выполните шаги 1, чтобы снова получить дочерние элементы.


Шаг 4:

for td in child.findChildren("td", recursive=False)            
      print(td.next_element.strip())

Следующий элемент вернет первый текст тега, поэтому в этом случаеон вернет значение 5.


Шаг 5

for td in child.findChildren("td", recursive=False):
             print(td.next_element.strip())
             for td1 in td.findChildren("table", recursive=False):
                 for child1 in td1.findChildren("tr", recursive=False):
                     for child2 in child1.findChildren("td", recursive=False):
                         tdlist.append(child2.text)
                     print(' '.join(tdlist))
                     print(child2.next_element.next_element.strip())

Если вы видите здесь, я только что выполнил шаг 1 рекурсивно. Да И снова я использовалchild2.next_element.next_element чтобы получить значение 6 после тега </table>.

2 голосов
/ 04 июня 2019

Используя комбинации bs4 и re, вы можете достичь желаемого.

Я использую bs4 4.6.3

from bs4 import BeautifulSoup as bs
import re

html = '''
<table>
<tr>
   <td>1</td><td>2</td>
</tr>
<tr>
   <td>3</td><td>4</td>
</tr>
<tr>
  <td>5 
    <table><tr><td>11</td><td>22</td></tr></table>
      6
  </td>
</tr>
</table>'''

soup = bs(html, 'lxml')

ans = []

for x in soup.findAll('td'):
    if x.findAll('td'):
        for y in re.split('<table>.*</table>', str(x)):
            ans += re.findall('\d+', y)
    else:
        ans.append(x.text)
print(ans)

Для каждого td мы проверяем, если этогнездо td.Если это так, мы разбиваем таблицу и берем все и сопоставляем регулярное выражение для каждого числа.

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

0 голосов
/ 04 июня 2019

В вашем примере с bs4 4.7.1 я использую: has: не исключать зацикливание строк с дочерним столом

from bs4 import BeautifulSoup as bs

html = '''
<table>
    <tr>
        <td>1</td>
        <td>2</td>
    </tr>
    <tr>
        <td>3</td>
        <td>4</td>
    </tr>
    <tr>
        <td>
            <table>
                <tr>
                    <td>11</td>
                    <td>22</td>
                </tr>
            </table>
        </td>
    </tr>
</table>'''

soup = bs(html, 'lxml')
for tr in soup.select('tr:not(:has(table))'):
    print([td.text for td in tr.select('td')])
0 голосов
/ 04 июня 2019

Вы можете проверить, существует ли другой table внутри тега td, если он существует, просто пропустите этот td, в противном случае используйте его как обычный td.

for row in table.find_all("tr")[1:][:-1]:
    for td in row.find_all("td")[1:]:
        if td.find('table'):  # check if td has nested table
            continue
        dataset = td.get_text()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...