XML 文档的正则表达式
Regex for XML document
我正在尝试为本质上是 DASH mpd 文件的 XML 文档提出一个正则表达式。用例是这个 XML 文档有 AdaptationSet 标签,而 AdaptationSet 标签又可以有多个 Representation 标签,如图所示。我需要匹配所有 bandwidth 属性超过指定输入的表示标签,即 2000000 或 4000000如下所示。我可以想出以下一个,但它没有解决属性跨越多行的情况,如 Representation with id=1.
所示
正则表达式中的范围可以取 1-9 之间的任何值,可以假定这些值是整数格式,可以被正则表达式使用。 RANGE后面的6位数字将根据RANGE的值分别是1还是2或3来匹配带宽值1000000或2000000或3000000等等。
正则表达式:
<[Rr]epresentation.*?[Bb]andwidth="0?[%(RANGE)]\d{6}"[\s\S]*?[Rr]epresentation>
<AdaptationSet segmentAlignment="true" maxWidth="1280" maxHeight="720" maxFrameRate="24" par="16:9">
<Representation id="1"
mimeType="video/mp4"
codecs="avc1.4d401f"
width="512"
height="288"
frameRate="24"
sar="1:1"
startWithSAP="1"
bandwidth="1000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
</Representation>
<Representation id="2" mimeType="video/mp4" codecs="avc1.4d401f" width="512" height="288" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="2000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
</Representation>
<Representation id="3" mimeType="video/mp4" codecs="avc1.4d401f" width="768" height="432" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="4000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_768_1440K_video_$Number$.mp4" startNumber="1" initialization="BBB_768_1440K_video_init.mp4" />
</Representation>
</AdaptationSet>
更新
我会在底部推荐 ElementTree 版本。但这里是请求的正则表达式版本:
import re
txt = """
<AdaptationSet segmentAlignment="true" maxWidth="1280" maxHeight="720" maxFrameRate="24" par="16:9">
<Representation id="1"
mimeType="video/mp4"
codecs="avc1.4d401f"
width="512"
height="288"
frameRate="24"
sar="1:1"
startWithSAP="1"
bandwidth="1000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
</Representation>
<Representation id="2" mimeType="video/mp4" codecs="avc1.4d401f" width="512" height="288" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="2000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
</Representation>
<Representation id="3" mimeType="video/mp4" codecs="avc1.4d401f" width="768" height="432" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="4000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_768_1440K_video_$Number$.mp4" startNumber="1" initialization="BBB_768_1440K_video_init.mp4" />
</Representation>
</AdaptationSet>
"""
input=2000000
reps = re.findall(r'<\s*representation(?:\s*\w+="[^"]*")*\s*>.*?<\/\s*representation\s*>',
txt, flags=re.IGNORECASE + re.DOTALL)
for rep in reps:
bandwidth = int(re.search(r'bandwidth="([^"]*)"', rep, flags=re.IGNORECASE).group(1))
if (bandwidth > input):
print(rep)
我认为分几步更容易完成:
将 Representation
一个一个地分块。上面的正则表达式就是这样做的,但是你可以用更简单的东西比如 [^>]*?>
替换 attribute-matching 部分(non-capture 组 (?:\s*\w+="[^"]*")*\s*>
中的部分),因为你只需要整个表示元素及其 children。分解完整的正则表达式:
<\s*
- 匹配 <
后跟 0 个或多个空格
representation
- 显然匹配 representation
。 IGNORECASE
标志确保它匹配大小写变化
(?:\s*\w+="[^"]*")*
- 这匹配 blab_blah="value123"
形式的零个或多个属性,包括它们周围的空格。 (?:
表示它是一个 non-capturing 组,因此之后无法通过 python group()
方法使用。它只是为了重复而存在,即零个或多个属性,或 (?:...)*
。同样,由于您不需要此处的属性匹配,因此可以将其简化为类似 [^>]*?>
的内容,但它对我有用。
\s*>
- 空格后跟 >
.*?
- 元素内的一堆内容(包括由于 DOTALL
标志引起的换行符),但是 anti-greedy 匹配所以我们确保我们在第一个关闭标签处停止我们遇到了,不匹配后面的一个。
<\/\s*representation\s*>
- 关闭标签,带有可选的空格
一旦我们有了每个 "representation" 元素,我们就可以将带宽拉出到 first-class python 整数中,以便于与输入
- 根据带宽值过滤。
我认为将带宽提取为整数并将其与输入进行比较比尝试在正则表达式本身内进行整数比较更容易。
另请注意,如果没有(或超过 1 个)带宽属性的实例,则代码不会处理它。可能还有其他脆弱的方面...
这是使用 ElementTree 的版本。这通常更好的原因是您不依赖于自己的能力来解析 XML 语法的所有可能组合的细节。使用库意味着他们已经想好了所有的东西,而你所需要匹配的只是元素和属性名称等小片段,因此代码不太可能被破坏。但也许这是一道作业题什么的...
import xml.etree.ElementTree as ET
input = 4000
tree = ET.parse('content.xml')
root = tree.getroot()
nodes = [n for n in root.findall('Representation') if int(n.attrib['bandwidth']) >= input]
print(nodes)
试试这个更健壮的 RegEx
输入:
范围 1
- 9
输出:
bw[0] 包含整个打开到关闭元素
bw[2] 包含带宽
>>> import re
>>>
>>> range = "2"
>>>
>>> regx = r"(?s)(<[Rr]epresentation(?=\s)(?=(?:[^>\"']|\"[^\"]*\"|'[^']*')*?(?<=\s)[Bb]andwidth\s*=\s*(?:(['\"])\s*0*([" + \
... range + \
... r"-9]\d{6}|[1-9]\d{7,17})\s*))(?=(\s+(?:\".*?\"|'.*?'|[^>]*?)+>))(?<!/>).*?</[Rr]epresentation\s*>)"
>>>
>>> txt = """
... <AdaptationSet segmentAlignment="true" maxWidth="1280" maxHeight="720" maxFrameRate="24" par="16:9">
... <Representation id="1"
... mimeType="video/mp4"
... codecs="avc1.4d401f"
... width="512"
... height="288"
... frameRate="24"
... sar="1:1"
... startWithSAP="1"
... bandwidth="1000000">
... <SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
... </Representation>
... <Representation id="2" mimeType="video/mp4" codecs="avc1.4d401f" width="512" height="288" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="2000000">
... <SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
... </Representation>
... <Representation id="3" mimeType="video/mp4" codecs="avc1.4d401f" width="768" height="432" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="4000000">
... <SegmentTemplate timescale="12288" duration="61440" media="BBB_768_1440K_video_$Number$.mp4" startNumber="1" initialization="BBB_768_1440K_video_init.mp4" />
... </Representation>
... </AdaptationSet>
... """
>>>
>>> bands = re.findall( regx, txt )
>>> for bw in bands:
... print ( bw[2] + " : " )
... print ( bw[0] )
... print ( "" )
...
2000000 :
<Representation id="2" mimeType="video/mp4" codecs="avc1.4d401f" width="512" height="288" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="2000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
</Representation>
4000000 :
<Representation id="3" mimeType="video/mp4" codecs="avc1.4d401f" width="768" height="432" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="4000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_768_1440K_video_$Number$.mp4" startNumber="1" initialization="BBB_768_1440K_video_init.mp4" />
</Representation>
>>>
学习你和爱德华的代码。但是我不建议大家直接使用XML的regex解析。
n = '4'
reg = '<[Rr]epresentation.*?[Bb]andwidth="(['+n+'-9]\d{6}|\d{8})[\d]*"[\s\S]*?</[Rr]epresentation>'
给你一个使用SimplifiedDoc的例子
from simplified_scrapy import SimplifiedDoc
html = '''Your xml'''
doc = SimplifiedDoc(html)
n = '4'
Representations = doc.selects('Representation|representation').containsReg('(['+n+'-9]\d{6}|\d{8})[\d]*',attr='bandwidth')
print(Representations)
结果:
[{'id': '3', 'mimeType': 'video/mp4', 'codecs': 'avc1.4d401f', 'width': '768', 'height': '432', 'frameRate': '24', 'sar': '1:1', 'startWithSAP': '1', 'bandwidth': '4000000', 'tag': 'Representation', 'html': '\n <SegmentTemplate timescale="12288" duration="61440" media="BBB_768_1440K_video_$Number$.mp4" startNumber="1" initialization="BBB_768_1440K_video_init.mp4" />\n '}]
你可以使用这个正则表达式
<[Rr]epresentation[^>]*?[Bb]andwidth="0?[2-9]\d{6}"[\s\S]*?[Rr]epresentation>
我正在尝试为本质上是 DASH mpd 文件的 XML 文档提出一个正则表达式。用例是这个 XML 文档有 AdaptationSet 标签,而 AdaptationSet 标签又可以有多个 Representation 标签,如图所示。我需要匹配所有 bandwidth 属性超过指定输入的表示标签,即 2000000 或 4000000如下所示。我可以想出以下一个,但它没有解决属性跨越多行的情况,如 Representation with id=1.
所示正则表达式中的范围可以取 1-9 之间的任何值,可以假定这些值是整数格式,可以被正则表达式使用。 RANGE后面的6位数字将根据RANGE的值分别是1还是2或3来匹配带宽值1000000或2000000或3000000等等。
正则表达式:
<[Rr]epresentation.*?[Bb]andwidth="0?[%(RANGE)]\d{6}"[\s\S]*?[Rr]epresentation>
<AdaptationSet segmentAlignment="true" maxWidth="1280" maxHeight="720" maxFrameRate="24" par="16:9">
<Representation id="1"
mimeType="video/mp4"
codecs="avc1.4d401f"
width="512"
height="288"
frameRate="24"
sar="1:1"
startWithSAP="1"
bandwidth="1000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
</Representation>
<Representation id="2" mimeType="video/mp4" codecs="avc1.4d401f" width="512" height="288" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="2000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
</Representation>
<Representation id="3" mimeType="video/mp4" codecs="avc1.4d401f" width="768" height="432" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="4000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_768_1440K_video_$Number$.mp4" startNumber="1" initialization="BBB_768_1440K_video_init.mp4" />
</Representation>
</AdaptationSet>
更新
我会在底部推荐 ElementTree 版本。但这里是请求的正则表达式版本:
import re
txt = """
<AdaptationSet segmentAlignment="true" maxWidth="1280" maxHeight="720" maxFrameRate="24" par="16:9">
<Representation id="1"
mimeType="video/mp4"
codecs="avc1.4d401f"
width="512"
height="288"
frameRate="24"
sar="1:1"
startWithSAP="1"
bandwidth="1000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
</Representation>
<Representation id="2" mimeType="video/mp4" codecs="avc1.4d401f" width="512" height="288" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="2000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
</Representation>
<Representation id="3" mimeType="video/mp4" codecs="avc1.4d401f" width="768" height="432" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="4000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_768_1440K_video_$Number$.mp4" startNumber="1" initialization="BBB_768_1440K_video_init.mp4" />
</Representation>
</AdaptationSet>
"""
input=2000000
reps = re.findall(r'<\s*representation(?:\s*\w+="[^"]*")*\s*>.*?<\/\s*representation\s*>',
txt, flags=re.IGNORECASE + re.DOTALL)
for rep in reps:
bandwidth = int(re.search(r'bandwidth="([^"]*)"', rep, flags=re.IGNORECASE).group(1))
if (bandwidth > input):
print(rep)
我认为分几步更容易完成:
将
Representation
一个一个地分块。上面的正则表达式就是这样做的,但是你可以用更简单的东西比如[^>]*?>
替换 attribute-matching 部分(non-capture 组(?:\s*\w+="[^"]*")*\s*>
中的部分),因为你只需要整个表示元素及其 children。分解完整的正则表达式:<\s*
- 匹配<
后跟 0 个或多个空格representation
- 显然匹配representation
。IGNORECASE
标志确保它匹配大小写变化(?:\s*\w+="[^"]*")*
- 这匹配blab_blah="value123"
形式的零个或多个属性,包括它们周围的空格。(?:
表示它是一个 non-capturing 组,因此之后无法通过 pythongroup()
方法使用。它只是为了重复而存在,即零个或多个属性,或(?:...)*
。同样,由于您不需要此处的属性匹配,因此可以将其简化为类似[^>]*?>
的内容,但它对我有用。\s*>
- 空格后跟>
.*?
- 元素内的一堆内容(包括由于DOTALL
标志引起的换行符),但是 anti-greedy 匹配所以我们确保我们在第一个关闭标签处停止我们遇到了,不匹配后面的一个。<\/\s*representation\s*>
- 关闭标签,带有可选的空格
一旦我们有了每个 "representation" 元素,我们就可以将带宽拉出到 first-class python 整数中,以便于与输入
- 根据带宽值过滤。
我认为将带宽提取为整数并将其与输入进行比较比尝试在正则表达式本身内进行整数比较更容易。
另请注意,如果没有(或超过 1 个)带宽属性的实例,则代码不会处理它。可能还有其他脆弱的方面...
这是使用 ElementTree 的版本。这通常更好的原因是您不依赖于自己的能力来解析 XML 语法的所有可能组合的细节。使用库意味着他们已经想好了所有的东西,而你所需要匹配的只是元素和属性名称等小片段,因此代码不太可能被破坏。但也许这是一道作业题什么的...
import xml.etree.ElementTree as ET
input = 4000
tree = ET.parse('content.xml')
root = tree.getroot()
nodes = [n for n in root.findall('Representation') if int(n.attrib['bandwidth']) >= input]
print(nodes)
试试这个更健壮的 RegEx
输入:
范围 1
- 9
输出:
bw[0] 包含整个打开到关闭元素
bw[2] 包含带宽
>>> import re
>>>
>>> range = "2"
>>>
>>> regx = r"(?s)(<[Rr]epresentation(?=\s)(?=(?:[^>\"']|\"[^\"]*\"|'[^']*')*?(?<=\s)[Bb]andwidth\s*=\s*(?:(['\"])\s*0*([" + \
... range + \
... r"-9]\d{6}|[1-9]\d{7,17})\s*))(?=(\s+(?:\".*?\"|'.*?'|[^>]*?)+>))(?<!/>).*?</[Rr]epresentation\s*>)"
>>>
>>> txt = """
... <AdaptationSet segmentAlignment="true" maxWidth="1280" maxHeight="720" maxFrameRate="24" par="16:9">
... <Representation id="1"
... mimeType="video/mp4"
... codecs="avc1.4d401f"
... width="512"
... height="288"
... frameRate="24"
... sar="1:1"
... startWithSAP="1"
... bandwidth="1000000">
... <SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
... </Representation>
... <Representation id="2" mimeType="video/mp4" codecs="avc1.4d401f" width="512" height="288" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="2000000">
... <SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
... </Representation>
... <Representation id="3" mimeType="video/mp4" codecs="avc1.4d401f" width="768" height="432" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="4000000">
... <SegmentTemplate timescale="12288" duration="61440" media="BBB_768_1440K_video_$Number$.mp4" startNumber="1" initialization="BBB_768_1440K_video_init.mp4" />
... </Representation>
... </AdaptationSet>
... """
>>>
>>> bands = re.findall( regx, txt )
>>> for bw in bands:
... print ( bw[2] + " : " )
... print ( bw[0] )
... print ( "" )
...
2000000 :
<Representation id="2" mimeType="video/mp4" codecs="avc1.4d401f" width="512" height="288" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="2000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_512_640K_video_$Number$.mp4" startNumber="1" initialization="BBB_512_640K_video_init.mp4" />
</Representation>
4000000 :
<Representation id="3" mimeType="video/mp4" codecs="avc1.4d401f" width="768" height="432" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="4000000">
<SegmentTemplate timescale="12288" duration="61440" media="BBB_768_1440K_video_$Number$.mp4" startNumber="1" initialization="BBB_768_1440K_video_init.mp4" />
</Representation>
>>>
学习你和爱德华的代码。但是我不建议大家直接使用XML的regex解析。
n = '4'
reg = '<[Rr]epresentation.*?[Bb]andwidth="(['+n+'-9]\d{6}|\d{8})[\d]*"[\s\S]*?</[Rr]epresentation>'
给你一个使用SimplifiedDoc的例子
from simplified_scrapy import SimplifiedDoc
html = '''Your xml'''
doc = SimplifiedDoc(html)
n = '4'
Representations = doc.selects('Representation|representation').containsReg('(['+n+'-9]\d{6}|\d{8})[\d]*',attr='bandwidth')
print(Representations)
结果:
[{'id': '3', 'mimeType': 'video/mp4', 'codecs': 'avc1.4d401f', 'width': '768', 'height': '432', 'frameRate': '24', 'sar': '1:1', 'startWithSAP': '1', 'bandwidth': '4000000', 'tag': 'Representation', 'html': '\n <SegmentTemplate timescale="12288" duration="61440" media="BBB_768_1440K_video_$Number$.mp4" startNumber="1" initialization="BBB_768_1440K_video_init.mp4" />\n '}]
你可以使用这个正则表达式
<[Rr]epresentation[^>]*?[Bb]andwidth="0?[2-9]\d{6}"[\s\S]*?[Rr]epresentation>