首页 > 解决方案 > 使用 lxml 解析表中的 HTML 数据

问题描述

我仍然是编码的学习者,我的一个朋友告诉我使用 BeautifulSoup。遇到一些问题后 - 我认为我应该使用 lxml 而不是 BeautifulSoup 因为它更好。我希望有人能给我提示如何抓取我正在寻找的文本。我想要的是在“一般信息”字段中找到一个包含以下行和数据的表格。

顺便提一句; 我也尝试使用 pandas 获取表格的元素,但是 - 同时 Pandas 真的很棒,它在每种情况下都没有帮助。我认为我必须刮一些表格元素,我不希望整个表格都是我的 HTML 表格的结构:

<table border="" width="100%">
<tbody><tr valign="top"><td width="50%">
<h3 align="center">item 1</h3>
<ul>
<li><a href="/link.html">name <b>mike/b></a><b>
</b>
<hr width="50%">
</li><li><a href="/link.html">name <b>john</b></a>,
<a href="link.html">name</a>fred</li></ul>


</td><td>
<h3 align="center"> General Information </h3><p></p><ul>
<li>Type of company
</li><li>foundet <a href="/calendar/dayoffoundation.html">10 December</a> <a href="/foundet.html">1900</a>
</li><li> category 1
</li><li>  category 2 
</li><li>Country: <a href="/country/california.html">california</a></li><li>
</li><li> Town: <a href="/country/sggf.html">san francisco</a>
</li><li>Official Web Site: <a href="https://www.demo-company.net/">https://www.demo-company/</a>
</li><li>Mailing Address: 
</li><li>Telephone: 3453455
</li><li>Fax: 433532
</li></ul></td></tr></tbody></table>

其中 td 代表“表数据”,即数据以文本形式存储的位置。

如何使用 lxml 抓取网站并获得以下结果?

[['General Information' 'foundet', 'category1', 'category2', 'country', '等等'] 注意:页面上的所有其他内容都不是必需的!

我通常使用非常有用的模式,所以我们剩下要做的就是使用 BeautifulSoup 选择正确的元素。首先要做的是找到桌子。

我通常使用 find_all() 方法返回满足我们传递给它的要求的所有元素的列表。然后我们必须在该列表中选择我们需要的表:

table = soup.find_all('table')[xy - here a number]

标签: pythonhtmlweb-scrapingbeautifulsoup

解决方案


您可以lxml使用bs4. 只需添加nth-child/nth-of-type到目标 right td,然后向下获取 theh3和 the li(还有其他方法,例如相邻兄弟组合):

from bs4 import BeautifulSoup

html = '''
<table border="" width="100%">
<tbody><tr valign="top"><td width="50%">
<h3 align="center">item 1</h3>
<ul>
<li><a href="/link.html">name <b>mike/b></a><b>
</b>
<hr width="50%">
</li><li><a href="/link.html">name <b>john</b></a>,
<a href="link.html">name</a>fred</li></ul>
</td><td>
<h3 align="center"> General Information </h3><p></p><ul>
<li>Type of company
</li><li>foundet <a href="/calendar/dayoffoundation.html">10 December</a> <a href="/foundet.html">1900</a>
</li><li> category 1
</li><li>  category 2 
</li><li>Country: <a href="/country/california.html">california</a></li><li>
</li><li> Town: <a href="/country/sggf.html">san francisco</a>
</li><li>Official Web Site: <a href="https://www.demo-company.net/">https://www.demo-company/</a>
</li><li>Mailing Address: 
</li><li>Telephone: 3453455
</li><li>Fax: 433532
</li></ul></td></tr></tbody></table>
'''
soup = bs(html, 'lxml')

print([i.text.strip() for i in soup.select('td:nth-child(2) > h3, td:nth-child(2) > ul > li')])

如果您事先知道标题(您似乎知道),您可以使用更有针对性的方法:contains(最新版本中的:-soup-contains ):

from bs4 import BeautifulSoup
import pandas as pd

html = '''
<table border="" width="100%">
<tbody><tr valign="top"><td width="50%">
<h3 align="center">item 1</h3>
<ul>
<li><a href="/link.html">name <b>mike/b></a><b>
</b>
<hr width="50%">
</li><li><a href="/link.html">name <b>john</b></a>,
<a href="link.html">name</a>fred</li></ul>
</td><td>
<h3 align="center"> General Information </h3><p></p><ul>
<li>Type of company
</li><li>foundet <a href="/calendar/dayoffoundation.html">10 December</a> <a href="/foundet.html">1900</a>
</li><li> category 1
</li><li>  category 2 
</li><li>Country: <a href="/country/california.html">california</a></li><li>
</li><li> Town: <a href="/country/sggf.html">san francisco</a>
</li><li>Official Web Site: <a href="https://www.demo-company.net/">https://www.demo-company/</a>
</li><li>Mailing Address: 
</li><li>Telephone: 3453455
</li><li>Fax: 433532
</li></ul></td></tr></tbody></table>
'''
soup = bs(html, 'lxml')

df = pd.DataFrame(
                  [i.text.strip() for i in soup.select('td:has(h3:contains("General Information")) > ul > li')]
                 ,columns = ['General Information']
                )
print(df)

推荐阅读