如何使用BeautifulSoup获取特定表中的所有行?

29

我正在学习Python和BeautifulSoup,用于从网页上爬取数据并读取HTML表格。我可以将其读入Open Office,它会显示这是第11个表格。

似乎BeautifulSoup是首选,但有人能告诉我如何获取特定的表格和所有行吗?我已经查看了模块文档,但无法理解它。我在网上找到的许多示例似乎做得比我需要的更多。

3个回答

56

如果你有一个需要解析的HTML块,使用BeautifulSoup库应该是相当简单的。基本思路是使用findChildren方法找到表格,然后使用string属性获取单元格内的文本值。

>>> from BeautifulSoup import BeautifulSoup
>>> 
>>> html = """
... <html>
... <body>
...     <table>
...         <th><td>column 1</td><td>column 2</td></th>
...         <tr><td>value 1</td><td>value 2</td></tr>
...     </table>
... </body>
... </html>
... """
>>>
>>> soup = BeautifulSoup(html)
>>> tables = soup.findChildren('table')
>>>
>>> # This will get the first (and only) table. Your page may have more.
>>> my_table = tables[0]
>>>
>>> # You can find children with multiple tags by passing a list of strings
>>> rows = my_table.findChildren(['th', 'tr'])
>>>
>>> for row in rows:
...     cells = row.findChildren('td')
...     for cell in cells:
...         value = cell.string
...         print("The value in this cell is %s" % value)
... 
The value in this cell is column 1
The value in this cell is column 2
The value in this cell is value 1
The value in this cell is value 2
>>> 

那就是诀窍!代码运行正常,我应该能够根据需要进行修改。非常感谢。最后一个问题。我可以理解代码,除了当您搜索表格中的子元素th和tr时。那只是在搜索我的表格并返回表头和表行吗?如果我只想要表行,我只需要搜索tr就可以了吗?再次感谢! - Btibert3
3
是的,.findChildren(['th', 'tr']) 是在查找带有 thtr 标签类型的元素。如果您只想查找 tr 元素,则应该使用 .findChildren('tr')(注意不是列表,只是一个字符串)。 - JJ Geewax
值得一提的是,PyQuery 是一个非常好的替代 BeautifulSoup 的选择。 - JJ Geewax
1
th 是一个表头单元格。这是一个格式不正确的表格。 - Andriy Makukha

5
如果您曾经使用过嵌套表格(例如在老式设计的网站上),上面的方法可能会失败。
作为解决方案,您可能需要先提取非嵌套表格:
html = '''<table>
<tr>
<td>Top level table cell</td>
<td>
    <table>
    <tr><td>Nested table cell</td></tr>
    <tr><td>...another nested cell</td></tr>
    </table>
</td>
</tr>
</table>'''

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
non_nested_tables = [t for t in soup.find_all('table') if not t.find_all('table')]

另外,如果您想提取所有表格内容,包括嵌套其他表格的表格,您可以仅提取顶级trth/td标题。为此,在调用find_all方法时,需要关闭递归:

soup = BeautifulSoup(html, 'lxml')
tables = soup.find_all('table')
cnt = 0
for my_table in tables:
    cnt += 1
    print ('=============== TABLE {} ==============='.format(cnt))
    rows = my_table.find_all('tr', recursive=False)                  # <-- HERE
    for row in rows:
        cells = row.find_all(['th', 'td'], recursive=False)          # <-- HERE
        for cell in cells:
            # DO SOMETHING
            if cell.string: print (cell.string)

输出:

=============== TABLE 1 ===============
Top level table cell
=============== TABLE 2 ===============
Nested table cell
...another nested cell

0

如果您没有嵌套表格,递归是一个很好的技巧,但如果您有嵌套表格,则需要一次处理一级。

可能会让您感到困扰的一个HTML变化是以下情况,其中也使用了tbody和/或thead元素。

html = '
    <table class="fancy">
        <thead>
           <tr><th>Nested table cell</th></tr>
        </thead>
        <tbody>
            <tr><td><table id=2>...another nested cell</table></td></tr>
        </tbody> 
        </table>
    </table>

在这种情况下,您需要执行以下操作。
   table = soup.find_all("table", {"class": "fancy"})[0]
    thead = table.find_all('thead', recursive=False)
    header = thead[0].findChildren('th')
    
    tbody = table.find_all('tbody', recursive=False)
    rows = tbody[0].find_all('tr', recursive=False)

现在你有表头和数据行


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接