如何使用 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"/>'

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

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 答案,因为它完全按照我需要的方式在两行中完成了整个工作。如果您实际上并不关心名称空间,那似乎更容易。

我打算让这个问题悬而未决,但我目前使用的解决方法是使用 BeautifulSoup,它很乐意接受 v: 语法。

soup = BeautifulSoup(xml, "lxml")

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

ET.findall() 对比 BS4.find_all():

  • ElementTree's findall() 不是递归的 默认情况下 *。它只会找到所提供节点的直接子节点。所以在你的例子中,它只搜索直接在根元素下的图像节点。
    • * 根据 below, prefixing the match argument (tag or path) with ".//" will search for that node anywhere in the tree, since it's supports XPath's.
  • BeautifulSoup's find_all() 搜索所有后代。所以它搜索树中任何地方的 'imagedata' 个节点。
  • 但是,ElementTree.iter() does search all descendants. Using the 'working with namespaces' example in the docs:

    >>> for char in root.iter('{http://characters.example.com}character'):
    ...     print(' |-->', char.text)
    ...
     |--> Lancelot
     |--> Archie Leach
     |--> Sir Robin
     |--> Gunther
     |--> Commander Clement
    
  • 可悲的是,ET.iterfind() 将名称空间用作字典(如 ET.findall),而且 不搜索后代 ,仅搜索直接子代 默认*。就像 ET.findall。除了标签中的空字符串 '' 如何处理命名空间,一个 returns 一个列表而另一个 returns 一个迭代器,我不能说 returns 之间有什么有意义的区别=23=] 和 ET.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)

当然,如果它们不是根的直接子元素,这仍然不一定能为您提供所有图像数据元素。为此,您需要创建一个递归函数来为您完成。请参阅 了解具体方法。请注意,虽然该答案进行递归搜索,但如果后代深度太...deep.,您可能会达到 Python 的递归限制

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

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

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

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

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