我有一个嵌套的表结构。我正在使用下面的代码来解析数据。
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
也就是说,再次解析内部表。
答案 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')])