beautifulsoup,找到文本'价格',然后从下一个获得价格

时间:2010-07-31 04:30:35

标签: python beautifulsoup

我的HTML看起来像:

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

所以我在当前的表格单元格中,如何获得99.99的值?

我到目前为止:

td[3].findChild('th')

但我需要这样做:

找到文本'price',然后获取下一个标记的字符串值。

2 个答案:

答案 0 :(得分:8)

在“步骤”中考虑一下......假设某些x是您正在考虑的子树的根,

x.findAll(text='price')

是包含文本'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而不是OP的评论。

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条件和缩小循环中所有以下语句的替代方法相比,这减少了嵌套 - “ flat比嵌套更好“是Python中的一个koans(import this,在交互式提示中看到它们并冥想; - )。

答案 1 :(得分: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助手返回一对pyparsing表达式,一个用于开始标记,另一个用于结束标记。开始标记模式不仅仅是在给定字符串周围添加“&lt;&gt;”,而且还允许额外的空格,变量大小写以及标记属性的存在与否。例如,请注意,即使我将“TH”指定为表头标记,它也将匹配“th”,“Th”,“tH”和“TH”。 Pyparsing的默认空白跳过行为还将处理额外的空格,标记和“$”之间,“$”和数字价格之间等,而不必洒“零或更多的空白字符可以到这里”指标。最后,通过在floatum的定义中指定结果名称“price”(priceEntry之后),可以非常简单地从匹配整体{{1}的完整标记列表中访问该特定值表达。

(Combine用于两个目的:它不允许数字组成部分之间的空格;并返回单个组合标记“99.99”而不是列表priceEntry。)