如何使用 Python XML findall 查找 '<v:imagedata r:id="rId7" o:title="1-REN"/>'

Posted

技术标签:

【中文标题】如何使用 Python XML findall 查找 \'<v:imagedata r:id="rId7" o:title="1-REN"/>\'【英文标题】:How to use Python XML findall to find '<v:imagedata r:id="rId7" o:title="1-REN"/>'如何使用 Python XML findall 查找 '<v:imagedata r:id="rId7" o:title="1-REN"/>' 【发布时间】:2020-09-18 11:14:07 【问题描述】:

我正在尝试从具有命名空间 xmlns:v="urn:schemas-microsoft-com:vml"&lt;v:imagedata r:id="rId7" o:title="1-REN"/&gt; 的 Word 文档中查找所有内容,但我无法弄清楚语法到底是什么。

docs 仅涵盖了非常简单的案例,并且使用了 URN 和 VML 组合,我似乎无法让我在网上看到的任何示例都可以工作。有谁碰巧知道它是什么?

我正在尝试做这样的事情:

namespace = 'v': "urn:schemas-microsoft-com:vml"

results = ET.fromstring(xml).findall("imagedata", namespace)
for image_id in results:
    print(image_id)

编辑:@aneroid 写的是 1000% 的正确答案并且非常有帮助。你应该赞成它。就是说,在了解了所有这些之后-我选择了BS4答案,因为它完全按照我的需要在两行中完成了整个工作????。如果您实际上并不关心命名空间,那似乎更容易。

【问题讨论】:

【参考方案1】:

使用 Python 3.8 中的 ElementTree,您可以简单地为命名空间使用通配符 (*):

results = ET.fromstring(xml).findall(".//*imagedata") 

注意.// 部分,这意味着搜索整个文档(所有后代)。

【讨论】:

这也适用于.iter()(可能还有所有与搜索相关的方法)。此外,.// 位是特定于问题的详细信息。不需要使用新的* 通配符。 通配符真的可以与iter() 一起使用吗?链接的 3.8 发行说明仅提及“.find*() 方法”。 是的,我已确认您的行为。 Documention on 'Supported XPath syntax' 现在还指定:*spam selects tags named spam in any (or no) namespaceChanged in version 3.8: Support for star-wildcards was added. @Aaron:“这也适用于.iter()”是什么意思?命名空间通配符iter() 一起使用。它适用于find()findall()findtext() 来自我的本地测试;新的通配符在iter() 中有效。简要回顾; source-code commit 似乎修改了用于确定标签是否匹配的比较逻辑,所以我假设即使没有记录,也可以说这适用于 .iter()。然而,在有人添加适当的单元测试并更新文档之前,这只是一个假设。【参考方案2】:

ET.findall()BS4.find_all():

ElementTree's findall() 不是递归的默认情况下*。它只会找到所提供节点的直接子节点。因此,在您的情况下,它仅在根元素下直接搜索图像节点。 * 根据下面的mzjn's comment,在match 参数(标签或路径)前加上".//" 将搜索该节点在树中的任何位置,因为它是@ 987654323@. BeautifulSoup's find_all() 搜索所有后代。因此它会在树中的任何位置搜索“imagedata”节点。

但是,ElementTree.iter() 确实搜索所有后代。使用'working with namespaces' example in the docs:

>>> for char in root.iter('http://characters.example.comcharacter'):
...     print(' |-->', char.text)
...
 |--> Lancelot
 |--> Archie Leach
 |--> Sir Robin
 |--> Gunther
 |--> Commander Clement
遗憾的是,ET.iterfind() 将命名空间用作 dict(如 ET.findall),也不搜索后代,仅直接子代默认情况下*。就像 ET.findall。除了标签中的空字符串'' 在命名空间中如何被处理,一个返回一个列表而另一个返回一个迭代器之外,我不能说ET.findallET.iterfind 之间存在有意义的区别。 * 如上ET.findall(),前缀".//" 使其搜索整个树(与任何节点匹配)。

当您使用带有 ET 的命名空间时,您仍然需要带有标签的 命名空间名称。结果行应该是:

namespace = 'v': "urn:schemas-microsoft-com:vml"
results = ET.fromstring(xml).findall("v:imagedata", namespace)  # note the 'v:'

另外,'v' 不必是 'v',如果需要,您可以将其更改为更有意义的内容:

namespace = 'image': "urn:schemas-microsoft-com:vml"
results = ET.fromstring(xml).findall("image:imagedata", namespace)

当然,如果它们不是根的直接子元素,这仍然不一定会得到所有的 imagedata 元素。为此,您需要创建一个递归函数来为您执行此操作。请参阅this answer on SO 了解如何操作。请注意,虽然该答案会进行递归搜索,但如果后代深度太高,您可能会达到 Python 的递归限制...deep

要获取树中任意位置的所有 imagedata 元素,请使用 ".//" 前缀:

results = ET.fromstring(xml).findall(".//v:imagedata", namespace)

【讨论】:

findall 可以找到所有imagedata 节点。只需使用findall(".//v:imagedata", namespace) 谢谢!我已经编辑并澄清了我的答案 wrt ET.findall(),以及 ET.iterfind()【参考方案3】:

我将保留这个问题,但我目前使用的解决方法是使用 BeautifulSoup,它很乐意接受 v: 语法。

soup = BeautifulSoup(xml, "lxml")

results = soup.find_all("v:imagedata")

【讨论】:

以上是关于如何使用 Python XML findall 查找 '<v:imagedata r:id="rId7" o:title="1-REN"/>'的主要内容,如果未能解决你的问题,请参考以下文章

替换Groovy XML节点会导致后续findAll调用出现问题

idea的spring整合基于xml文件配置的mybatis报Invalid bound statement (not found): com.music.dao.MusicDao.findAll的问

使用 Python Etree 解析 XML 并返回指定的标签而不考虑命名空间

Python正则表达式之findall疑点

Python re.findall 打印所有模式

python RE findall() 返回值是一个完整的字符串