4

我正在尝试从下面给定的字符串中提取一些信息

>>> st = '''
... <!-- info mp3 here -->
...                             192 kbps<br />2:41<br />3.71 mb  </div>
... <!-- info mp3 here -->
...                             3.49 mb  </div>
... <!-- info mp3 here -->
...                             128 kbps<br />3:31<br />3.3 mb   </div>
... '''
>>>

现在,当我使用下面的正则表达式时,我的输出是

>>> p = re.findall(r'<!-- info mp3 here -->\s+(.*?)<br />(.*?)<br />(.*?)\s+</div>',st)
>>> p
[('192 kbps', '2:41', '3.71 mb'), ('128 kbps', '3:31', '3.3 mb')]

但我需要的输出是

[('192 kbps', '2:41', '3.71 mb'),(None,None,'3.49mb'), ('128 kbps', '3:31', '3.3 mb')]

所以,我的问题是如何更改上面的内容regex以匹配所有条件。我相信我当前的正则表达式严格依赖于<br />标签,所以我如何让它以此为条件。

我知道我不应该使用正则表达式来解析 html,但目前这对我来说是最合适的方式。

4

2 回答 2

6

以下将起作用,但我想知道是否没有更优雅的解决方案。您当然可以将列表推导合并为一行,但我认为这会使代码整体不太清晰。至少通过这种方式,您将能够在三个月后遵循您所做的事情......

st = '''
<!-- info mp3 here -->
                            192 kbps<br />2:41<br />3.71 mb  </div>
<!-- info mp3 here -->
                            3.49 mb  </div>
<!-- info mp3 here -->
                            128 kbps<br />3:31<br />3.3 mb   </div>
'''

p = re.findall(r'<!-- info mp3 here -->\s+(.*?)\s+</div>',st)
p2 = [row.split('<br />') for row in p]
p3 = [[None]*(3 - len(row)) + row for row in p2]

>>> p3
[['192 kbps', '2:41', '3.71 mb'], [None, None, '3.49 mb'], ['128 kbps', '3:31', '3.3 mb']]

而且,根据字符串中的可变性,您可能希望编写一个更通用的清理函数,将其剥离、案例等,并将其映射到您拉出的每个项目。

于 2012-05-24T20:33:56.140 回答
2

这是一个更具体的正则表达式解决方案。我不确定这是否比Karmel的回答更可取,但我想我会按要求回答这个问题。前两个可选组没有返回,而是None返回空字符串'',我认为这可能足够接近。

注意嵌套组结构。前两个外部组是可选的,但<br />它们需要标记才能匹配。这样,如果标签少于两个<br />,最后一项直到最后才会匹配:

rx = r'''<!--\ info\ mp3\ here\ -->\s+   # verbose mode; escape literal spaces
         (?:                             # outer non-capturing group  
            ([^<>]*)                     # inner capturing group without <>
            (?:<br\ />)                  # inner non-capturing group matching br
         )?                              # whole outer group is optional
         (?:                             
            ([^<>]*)                     # all same as above
            (?:<br\ />)                
         )?
         (?:                             # outer non-capturing group
            (.*?)                        # non-greedy wildcard match
            (?:\s+</div>)                # inner non-capturing group matching div
         )'''                            # final group is not optional

测试:

>>> re.findall(rx, st, re.VERBOSE)
[('192 kbps', '2:41', '3.71 mb'), 
 ('', '', '3.49 mb'), 
 ('128 kbps', '3:31', '3.3 mb')]

请注意该re.VERBOSE标志,除非您删除上面的所有空格和注释,否则这是必需的。

于 2012-05-24T20:48:05.773 回答