使用BeautifulSoup(Python)解析HTML表格

2 投票
2 回答
7316 浏览
提问于 2025-04-16 10:36

##### 更新 ###### : 使用 renderContents() 而不是 contents[0] 解决了问题。如果有人能提供更好、更优雅的解决方案,我会继续保持这个问题开放!

我正在尝试从多个网页中提取所需的数据。这个表格没有类名或ID标签。所以我必须在 tr 的内容中搜索“网站”。

当前问题: 显示 td.contents 的文本没问题,但不知道为什么超链接却不行?我哪里做错了?有没有更好的方法可以在 Python 中使用 BeautifulSoup 来实现这个?

那些建议使用 lxml 的朋友,我在这里有一个正在进行的讨论 这里,在 centOS 上安装 lxml 而没有管理员权限现在有点麻烦。所以我在探索使用 BeautifulSoup 的选项。

HTML 示例:

                   <table border="2" width="100%">
                      <tbody><tr>
                        <td width="33%" class="BoldTD">Website</td>
                        <td width="33%" class="BoldTD">Last Visited</td>
                        <td width="34%" class="BoldTD">Last Loaded</td>
                      </tr>
                      <tr>
                        <td width="33%">
                          <a href="http://google.com"></a>
                        </td>
                        <td width="33%">01/14/2011
                                </td>
                        <td width="34%">
                                </td>
                      </tr>
                      <tr>
                        <td width="33%">
                          stackoverflow.com
                        </td>
                        <td width="33%">01/10/2011
                                </td>
                        <td width="34%">
                                </td>
                      </tr>
                      <tr>
                        <td width="33%">
                          <a href="http://stackoverflow.com"></a>
                        </td>
                        <td width="33%">01/10/2011
                                </td>
                        <td width="34%">
                                </td>
                      </tr>
                    </tbody></table>

到目前为止的 Python 代码:

        f1 = open(PATH + "/" + FILE)
        pageSource = f1.read()
        f1.close()
        soup = BeautifulSoup(pageSource)
        alltables = soup.findAll( "table", {"border":"2", "width":"100%"} )
        print "Number of tables found : " , len(alltables)

        for table in alltables:
            rows = table.findAll('tr')
            for tr in rows:
                cols = tr.findAll('td')
                for td in cols:
                    print td.contents[0]

2 个回答

1

我在这里回答过一个类似的问题 这里。希望对你有帮助。

一个简单易懂的解决方案:

alltables = soup.findAll( "table", {"border":"2", "width":"100%"} )

t = [x for x in soup.findAll('td')]

[x.renderContents().strip('\n') for x in t]

输出结果:

['Website',
 'Last Visited',
 'Last Loaded',
 '<a href="http://google.com"></a>',
 '01/14/2011\n                                ',
 '',
 '                          stackoverflow.com\n                        ',
 '01/10/2011\n                                ',
 '',
 '<a href="http://stackoverflow.com"></a>',
 '01/10/2011\n                                ',
 '']
1
from BeautifulSoup import BeautifulSoup

pageSource='''...omitted for brevity...'''    

soup = BeautifulSoup(pageSource)
alltables = soup.findAll( "table", {"border":"2", "width":"100%"} )

results=[]
for table in alltables:
    rows = table.findAll('tr')
    lines=[]
    for tr in rows:
        cols = tr.findAll('td')
        for td in cols:
            text=td.renderContents().strip('\n')
            lines.append(text)
    text_table='\n'.join(lines)
    if 'Website' in text_table:
        results.append(text_table) 
print "Number of tables found : " , len(results)
for result in results:
    print(result)

产生

Number of tables found :  1
Website
Last Visited
Last Loaded
<a href="http://google.com"></a>
01/14/2011

stackoverflow.com
01/10/2011

<a href="http://stackoverflow.com"></a>
01/10/2011

这是不是你想要的结果?问题在于 td.contents 返回的是一个包含 NavigableStrings 和 soup 标签 的列表。例如,运行 print(td.contents) 可能会得到

['', '<a href="http://stackoverflow.com"></a>', '']

所以直接取列表的第一个元素会让你错过 <a> 标签。

撰写回答