我正在尝试从下面给出的字符串中提取一些信息
>>> 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,但目前这对我来说是最合适的方式。
答案 0 :(得分: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']]
而且,根据字符串中的可变性,您可能希望编写一个更通用的清除函数,即条带,案例等,并将其映射到您提取的每个项目。
答案 1 :(得分: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
标志,除非您删除上面的所有空格和注释,否则这是必需的。