漂亮的汤不解析嵌套表数据

时间:2019-04-30 04:58:48

标签: python-3.x html-table beautifulsoup

我有一个嵌套的表结构。我正在使用下面的代码来解析数据。

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>
<table><tr><td>11</td><td>22</td></tr></table>
</td></tr>
</table>

预期输出:

1  2
3  4
11 22

但是我得到的是:

1 2
3 4
11 22
11 22

也就是说,再次解析内部表。

4 个答案:

答案 0 :(得分:2)

使用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。如果是这样,我们在桌子上劈开,拿走所有东西,并用正则表达式匹配每个数字。

请注意,此功能仅适用于两个深度级别,但适用于任何深度

答案 1 :(得分:2)

我尝试使用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</table>标签之后获得了6的值。

答案 2 :(得分:0)

您可以检查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()

答案 3 :(得分:0)

在您的示例中,使用bs4 4.7.1时,我使用:has:not排除带有子表的循环行

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')])
相关问题