【问题标题】:Parse HTML Table with Python BeautifulSoup使用 Python BeautifulSoup 解析 HTML 表
【发布时间】:2013-06-23 01:36:42
【问题描述】:

我正在尝试使用 BeautifulSoup 解析我上传到 http://pastie.org/8070879 的 html 表,以便将三列(0 到 735、0.50 到 1.0 和 0.5 到 0.0)作为列表。为了解释原因,我希望整数 0-735 是键,十进制数是值。

通过阅读关于 SO 的许多其他帖子,我想出了以下内容,这些内容与创建我想要的列表并不接近。它所做的只是在表格中显示文本,如下所示http://i1285.photobucket.com/albums/a592/TheNexulo/output_zps20c5afb8.png

from bs4 import BeautifulSoup

soup = BeautifulSoup(open("fide.html"))
table = soup.find('table')

rows = table.findAll('tr')

for tr in rows:
  cols = tr.findAll('td')
  for td in cols:
     text = ''.join(td.find(text=True))
     print text + "|",
  print 

我是 Python 和 BeautifulSoup 的新手,所以请对我温柔一点!谢谢

【问题讨论】:

  • 上传一张您希望数据最终呈现方式的图片。国际象棋相关问题 +1。
  • 它将文本显示在表格中,因为这就是您的代码所做的。为什么不将每个字段推入字典,其中键是整数,小数列表是值?

标签: python html beautifulsoup


【解决方案1】:

BeautifulSoup 等 HTML 解析器假定您需要的是一个反映输入 HTML 结构的对象模型。但有时(例如在这种情况下)该模型会阻碍而不是帮助。 Pyparsing 包括一些 HTML 解析功能,这些功能比仅使用原始正则表达式更强大,但以类似的方式工作,让您定义感兴趣的 HTML 的 sn-ps,而忽略其余部分。这是一个解析器,它会读取您发布的 HTML 源代码:

from pyparsing import makeHTMLTags,withAttribute,Suppress,Regex,Group

""" looking for this recurring pattern:
          <td valign="top" bgcolor="#FFFFCC">00-03</td>
          <td valign="top">.50</td>
          <td valign="top">.50</td>

    and want a dict with keys 0, 1, 2, and 3 all with values (.50,.50)
"""

td,tdend = makeHTMLTags("td")
keytd = td.copy().setParseAction(withAttribute(bgcolor="#FFFFCC"))
td,tdend,keytd = map(Suppress,(td,tdend,keytd))

realnum = Regex(r'1?\.\d+').setParseAction(lambda t:float(t[0]))
integer = Regex(r'\d{1,3}').setParseAction(lambda t:int(t[0]))
DASH = Suppress('-')

# build up an expression matching the HTML bits above
entryExpr = (keytd + integer("start") + DASH + integer("end") + tdend + 
                    Group(2*(td + realnum + tdend))("vals"))

这个解析器不仅挑选出匹配的三元组,它还提取起始整数和实数对(并且在解析时已经从字符串转换为整数或浮点数)。

看一下表格,我猜您实际上想要一个查找,该查找将采用 700 之类的键,并返回一对值 (0.99, 0.01),因为 700 在 620-735 的范围内。这段代码搜索源 HTML 文本,遍历匹配的条目并将键值对插入到 dict 查找中:

# search the input HTML for matches to the entryExpr expression, and build up lookup dict
lookup = {}
for entry in entryExpr.searchString(sourcehtml):
    for i in range(entry.start, entry.end+1):
        lookup[i] = tuple(entry.vals)

现在尝试一些查找:

# print out some test values
for test in (0,20,100,700):
    print (test, lookup[test])

打印:

0 (0.5, 0.5)
20 (0.53, 0.47)
100 (0.64, 0.36)
700 (0.99, 0.01)

【讨论】:

  • 非常感谢!我只是通过它来确保我理解,然后我会将其标记为我接受的答案
  • 当我运行代码时,我在尝试打印一些测试值的行上得到一个 KeyError:0,这表明我要查找的键不在字典中?
  • 如果您遇到错误,请开始向后工作。看看lookup的内容是什么。如果查找为空,则可能 entryExpr 不匹配任何内容。如果 entryExpr 不匹配任何内容,那么您正在解析的文本可能与您发布的示例不匹配。
  • 查找为空。对于 sourcehtml,我输入了“fide.html”,这是与我的模块在同一目录中的文件,也是我发布到 paste 的文件。
  • "fide.html" 只是一个文件名。您必须将实际文件内容传递给 scanString,而不是文件名。尝试输入sourceHtml:open("fide.html").read()
【解决方案2】:

我认为上述答案比我提供的要好,但我有一个 BeautifulSoup 答案可以帮助您入门。这有点骇人听闻,但我想我还是会提供它。

使用 BeautifulSoup,您可以通过以下方式找到所有具有特定属性的标签(假设您已经设置了一个 soup.object):

soup.find_all('td', attrs={'bgcolor':'#FFFFCC'})

这会找到你所有的钥匙。诀窍是将这些与您想要的值相关联,这些值随后都会立即显示并且成对出现(如果这些事情发生变化,顺便说一下,这个解决方案将不起作用)。

因此,您可以尝试以下方法来访问您的关键条目之后的内容并将其放入 your_dictionary:

 for node in soup.find_all('td', attrs={'bgcolor':'#FFFFCC'}):
   your_dictionary[node.string] = node.next_sibling

问题是“next_sibling”实际上是一个'\n',所以你必须执行以下操作来捕获next值(你想要的第一个值):

for node in soup.find_all('td', attrs={'bgcolor':'#FFFFCC'}):
  your_dictionary[node.string] = node.next_sibling.next_sibling.string

如果您想要 两个 以下值,则必须将其加倍:

for node in soup.find_all('td', attrs={'bgcolor':'#FFFFCC'}):
  your_dictionary[node.string] = [node.next_sibling.next_sibling.string, node.next_sibling.next_sibling.next_sibling.next_sibling.string]

免责声明:最后一行对我来说非常难看。

【讨论】:

  • 这就是我所说的 BS 生成的模型有时会妨碍而不是帮助的意思。 BS 中的另一个薄弱环节是它必须解析整个 HTML,这使得它在面对奇怪的 HTML 时变得脆弱。基于 Pyparsing 的抓取工具可以只查找 HTML 中的特定结构(即使 它们 不是有效的 HTML - pyparsing 不会判断)并跳过其余部分。当然,使用正则表达式的方法是相似的,但是 re 强制您指定每个可能的空格、大写/小写、HTML 属性 - makeHTMLTags 和 pyparsing 的空格跳过会处理所有这些内容。
  • 我同意。这就是为什么我认为你的答案更好,即使它是,老实说,有点过头了。我有点希望有人能指出一些我忽略的东西,这样就不需要做“next_sibling.next_sibling”(这实际上是我通过阅读 BeautifulSoup 文档学到的一个技巧)。我正在考虑一些正则表达式或空白测试,以确保我们正在捕获请求的内容,但这并没有摆脱丑陋的线条。我也在考虑某种看起来更干净的生成器对象,但我没有继续考虑。
  • 嗯,不仅next_siblinging 丑陋,而且对空格非常敏感——如果&lt;td&gt; 行都在同一行,没有中间的换行符或空格,那么您将使用更少的 next_sibling 调用来遍历 BS 生成的 DOM。如果您编写了一个名为next_element 的简单生成器,它将执行next_sibling 调用直到找到一个元素节点(下一个&lt;td&gt; 节点),那么您可以使用next_element(node).string, next_element(next_element(node)).string - 可能不那么难看,但肯定更健壮面对不可预测的空白。
  • 啊,我看到在 BS API 中有一个 node.findNext('td') 调用,所以你可以写 (node.findNext('td').string, node.findNext('td').findNext('td').string) 来获取接下来两个 td 标签内容的 2 元组。跨度>
  • 是的!这实际上对我正在处理的一些东西很有用。谢谢你帮我思考
【解决方案3】:

我用过 BeautifulSoup 3,但它可能会在 4 下工作。

# Import System libraries
import re

# Import Custom libraries
from BeautifulSoup import BeautifulSoup

# This may be different between BeautifulSoup 3 and BeautifulSoup 4
with open("fide.html") as file_h:
    # Read the file into the BeautifulSoup class
    soup = BeautifulSoup(file_h.read())

tr_location = lambda x: x.name == u"tr" # Row location
key_location = lambda x: x.name == u"td" and bool(set([(u"bgcolor", u"#FFFFCC")]) & set(x.attrs)) # Integer key location
td_location = lambda x: x.name == u"td" and not dict(x.attrs).has_key(u"bgcolor") # Float value location

str_key_dict = {}
num_key_dict = {}
for tr in soup.findAll(tr_location): # Loop through all found rows
    for key in tr.findAll(key_location): # Loop through all found Integer key tds
        key_list = []
        key_str = key.text.strip()
        for td in key.findNextSiblings(td_location)[:2]: # Loop through the next 2 neighbouring Float values
            key_list.append(td.text)
        key_list = map(float, key_list) # Convert the text values to floats

        # String based dictionary section
        str_key_dict[key_str] = key_list

        # Number based dictionary section
        num_range = map(int, re.split("\s*-\s*", key_str)) # Extract a value range to perform interpolation
        if(len(num_range) == 2):
            num_key_dict.update([(x, key_list) for x in range(num_range[0], num_range[1] + 1)])
        else:
            num_key_dict.update([(num_range[0], key_list)])

for x in num_key_dict.items():
    print x

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-01
    • 2011-06-15
    • 2020-02-06
    • 2014-03-06
    • 2011-07-21
    • 2018-07-10
    • 2013-12-10
    相关资源
    最近更新 更多