我正在努力将一些片状HTML表格解析为Beautiful Soup列表。有问题的表缺少< / td>标签
使用以下代码(不是我正在解析的实际表,但功能相似):
import bs4
test = "<table> <tr><td>1<td>2<td>3</tr> <tr><td>1<td>2<td>3</tr> </table>"
def walk_table2(text):
"Take an HTML table and spit out a list of lists (of entries in a row)."
soup = bs4.BeautifulSoup(text)
return [[x for x in row.findAll('td')] for row in soup.findAll('tr')]
print walk_table2(test)
给我:
[[<td>1<td>2<td>3</td></td></td>, <td>2<td>3</td></td>, <td>3</td>], [<td>4<td>5<td>6</td></td></td>, <td>5<td>6</td></td>, <td>6</td>]]
而不是预期的:
[[<td>1</td>, <td>2</td>, <td>3</td>], [<td>1</td>, <td>2</td>, <td>3</td>]]
似乎Beautiful Soup正在使用的lxml解析器决定添加&lt; / td&gt; &lt; / tr&gt;的下一个实例之前的标记而不是&lt; td&gt;的下一个实例。
此时,我想知道是否有一个很好的选择让解析器将结束的td标记放在正确的位置,或者如果在将字符串输入之前使用正则表达式手动放置它们会更容易BeautifulSoup ...有什么想法吗?提前谢谢!
答案 0 :(得分:4)
您正在看到Python的内置HTML解析器做出的决定。如果您不喜欢解析器执行操作的方式,您可以tell Beautiful Soup to use a different parser。 html5lib解析器和lxml解析器都提供了你想要的结果:
>>> soup = bs4.BeautifulSoup(test, "lxml")
>>> [[x for x in row.findAll('td')] for row in soup.findAll('tr')]
[[<td>1</td>, <td>2</td>, <td>3</td>], [<td>1</td>, <td>2</td>, <td>3</td>]]
>>> soup = bs4.BeautifulSoup(test, "html5lib")
>>> [[x for x in row.findAll('td')] for row in soup.findAll('tr')]
[[<td>1</td>, <td>2</td>, <td>3</td>], [<td>1</td>, <td>2</td>, <td>3</td>]]
答案 1 :(得分:2)
这对我来说听起来像是一个BeautifulSoup的错误。我发现this page详细说明了为什么BS 3.1中的回归来自3.0.8(包括&#34;&#39;坏结束标记&#39;错误&#34;)这表明,对于解析错误的HTML,一个选项是跳回几个版本。也就是说,该页面表示它已被取代,现在仅供历史参考。然而,目前还不清楚BS4究竟解决了BS 3.1中引入的问题 - 至少,试用旧版本不会有什么坏处。
答案 2 :(得分:1)
一个不完整的修复程序,可以帮助您完成这个特定的操作:
使用正则表达式按下传入的数据(这非常脆弱,我知道stackoverflow对正则表达式和html的感觉,但C'MON,只有这一次......)
import re
r1 = re.compile('(?<!\<tr\>)\<td', re.IGNORECASE)
r2 = re.compile('\<\/tr>', re.IGNORECASE)
test = "<table> <tr><td>1<td>2<td>3</tr> <tr><td>1<td>2<td>3</tr> </table>"
test = r1.sub('</td><td', test)
test = r2.sub('</td></tr>', test)
print test
哦,然后test
:
<table> <tr><td>1</td><td>2</td><td>3</td></tr> <tr><td>1</td><td>2</td><td>3</td></tr> </table>