首页 文章

对于缺少</ td>标记的HTML表使用Beautiful Soup

提问于
浏览
5

我正在努力解决一些片状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解析器决定在</ tr>的下一个实例之前添加</ td>标记,而不是<td>的下一个实例 .

在这一点上,我想知道是否有一个很好的选择让解析器将结束的td标记放在正确的位置,或者如果在将字符串抛入BeautifulSoup之前使用正则表达式来手动放置它们会更容易 . . 有什么想法吗?提前致谢!

3 回答

  • 2

    你're seeing decisions made by 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>]]
    
  • 4

    这对我来说听起来像一个BeautifulSoup错误 . 我发现this page详细说明为什么BS 3.1中的回归来自3.0.8(包括"'bad end tag' errors"),这表明,对于解析错误的HTML,一个选项是跳回几个版本 . 也就是说,该页面说's been superseded and now exists only for historical reference. It'不清楚BS4究竟解决了BS 3.1中引入的问题 - 至少,试用旧版本也不会有什么坏处 .

  • 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>
    

相关问题