beautifulsoup,找到文本为'price'的th,然后获取下一个th中的价格

5 投票
2 回答
6217 浏览
提问于 2025-04-16 02:06

我的HTML代码是这样的:

<td>
   <table ..>
      <tr>
         <th ..>price</th>
         <th>$99.99</th>
      </tr>
   </table>
</td>

现在我在当前的表格单元格里,我该怎么获取99.99这个值呢?

到目前为止,我有:

td[3].findChild('th')

但我需要做的是:

找到包含'price'这个文字的标签,然后获取下一个标签的字符串值。

2 个回答

0

使用pyparsing库,我们可以很方便地在HTML中找到某种标签的模式,比如下面这个:

from pyparsing import makeHTMLTags, Combine, Word, nums

th,thEnd = makeHTMLTags("TH")
floatnum = Combine(Word(nums) + "." + Word(nums))
priceEntry = (th + "price" + thEnd + 
              th + "$" + floatnum("price") + thEnd)

tokens,startloc,endloc = priceEntry.scanString(html).next()

print tokens.price

pyparsing的makeHTMLTags这个助手函数会返回一对表达式,一个用于开始标签,一个用于结束标签。开始标签的模式不仅仅是把给定的字符串前后加上"<>",它还允许有额外的空格、大小写不敏感,以及标签属性的有无。例如,虽然我指定了"TH"作为表头标签,但它也可以匹配"th"、"Th"、"tH"和"TH"。pyparsing默认会跳过多余的空格,比如标签和"$"之间、"$"和数字价格之间等等,这样我们就不需要到处加上“这里可以有零个或多个空格”的提示了。最后,通过给结果命名为"price"(在priceEntry的定义中跟在floatum后面),我们可以很简单地从匹配priceEntry表达式的完整列表中获取这个特定的值。

(Combine的作用有两个:它禁止数字组件之间有空格;并且返回一个合并后的单一标记"99.99",而不是列表["99", ".", "99"]。)

8

想象一下这个过程是分步骤的……假设有一个x,它是你正在考虑的子树的根节点,

x.findAll(text='price')

那么这个子树里所有包含文本'price'的项目列表就是:

[t.parent for t in x.findAll(text='price')]

这些项目的父节点自然就是:

[t.parent for t in x.findAll(text='price')]

如果你只想保留那些“名字”(标签)是'th'的项目,那当然可以这样做:

[t.parent for t in x.findAll(text='price') if t.parent.name=='th']

接着你想要那些项目的“下一个兄弟节点”(但只限于它们也是'th'),所以:

[t.parent.nextSibling for t in x.findAll(text='price')
 if t.parent.name=='th' and t.parent.nextSibling and t.parent.nextSibling.name=='th']

在这里你会发现使用列表推导式的问题:重复的部分太多,因为我们不能把中间结果简单地命名。那我们就换回传统的循环方式……:

编辑:根据提问者的评论,增加了对父节点th和“下一个兄弟节点”之间文本的容忍度,以及后者是td的情况。

for t in x.findAll(text='price'):
  p = t.parent
  if p.name != 'th': continue
  ns = p.nextSibling
  if ns and not ns.name: ns = ns.nextSibling
  if not ns or ns.name not in ('td', 'th'): continue
  print ns.string

我添加了ns.string,这会在下一个兄弟节点的内容只有文本(没有进一步嵌套的标签)时返回内容——当然,你也可以在这个时候进行更深入的分析,这取决于你应用的需求!)。同样,我想你不会只是用print,而是会做一些更聪明的事情,但我给你提供了结构。

说到结构,注意我用了两次if...: continue:这样可以减少嵌套,相比于反转if的条件并缩进后面的所有语句,这样更简单——而“扁平化比嵌套更好”是Python Zen中的一个原则(在交互式提示符下输入import this可以看到所有原则并思考一下;-)。

撰写回答