如何从python的xml文件中获取文本?

时间:2019-07-31 10:01:23

标签: python xml beautifulsoup elementtree

我目前正在使用此代码来计算text文件中存在的xml个元素的数量。

from bs4 import BeautifulSoup

soup = BeautifulSoup(open('wiki.xml'), 'lxml')

count = 0

for text in soup.find_all('text', recursive=False):
    count += 1

print(count)

由于其大小,我无法显示完整的xml文件,但这是它的快速摘要...

<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
  <siteinfo>
    <sitename>Wikipedia</sitename>
    <dbname>simplewiki</dbname>
    <base>https://simple.wikipedia.org/wiki/Main_Page</base>
    <generator>MediaWiki 1.30.0-wmf.14</generator>
    <case>first-letter</case>
    <namespaces>
      <namespace key="-2" case="first-letter">Media</namespace>
      <namespace key="-1" case="first-letter">Special</namespace>
      <namespace key="0" case="first-letter" />
      <namespace key="1" case="first-letter">Talk</namespace>
      <namespace key="2" case="first-letter">User</namespace>
      <namespace key="3" case="first-letter">User talk</namespace>
      <namespace key="4" case="first-letter">Wikipedia</namespace>
      <namespace key="5" case="first-letter">Wikipedia talk</namespace>
      <namespace key="6" case="first-letter">File</namespace>
      <namespace key="7" case="first-letter">File talk</namespace>
      <namespace key="8" case="first-letter">MediaWiki</namespace>
      <namespace key="9" case="first-letter">MediaWiki talk</namespace>
      <namespace key="10" case="first-letter">Template</namespace>
      <namespace key="11" case="first-letter">Template talk</namespace>
      <namespace key="12" case="first-letter">Help</namespace>
      <namespace key="13" case="first-letter">Help talk</namespace>
      <namespace key="14" case="first-letter">Category</namespace>
      <namespace key="15" case="first-letter">Category talk</namespace>
      <namespace key="828" case="first-letter">Module</namespace>
      <namespace key="829" case="first-letter">Module talk</namespace>
      <namespace key="2300" case="first-letter">Gadget</namespace>
      <namespace key="2301" case="first-letter">Gadget talk</namespace>
      <namespace key="2302" case="case-sensitive">Gadget definition</namespace>
      <namespace key="2303" case="case-sensitive">Gadget definition talk</namespace>
      <namespace key="2600" case="first-letter">Topic</namespace>
    </namespaces>
  </siteinfo>
  <page>
    <title>April</title>
    <ns>0</ns>
    <id>1</id>
    <revision>
      <id>5753795</id>
      <parentid>5732421</parentid>
      <timestamp>2017-08-11T21:06:32Z</timestamp>
      <contributor>
        <ip>2602:306:3433:C7F0:188F:FDE3:9FBE:D0B0</ip>
      </contributor>
      <model>wikitext</model>
      <format>text/x-wiki</format>
      <text xml:space="preserve">{{monththisyear|4}}
'''April''' is the fourth [[month]] of the [[year]], and comes between     [[March]] and [[May]]. It is one of four months to have 30 [[day]]s.

April always begins on the same day of week as [[July]], and additionally, [[January]] in leap years. April always ends on the same day of the week as [[December]].

April's [[flower]]s are the [[Sweet Pea]] and [[Asteraceae|Daisy]]. Its [[birthstone]] is the [[diamond]]. The meaning of the diamond is innocence.

简而言之,对于最终产品,我希望它能够搜索page元素以找到titles,它将在其中搜索我输入的特定短语,然后返回该页面内的text元素,如果找不到结果,则返回最相似的前三个。这可能吗,有人可以帮助吗?我对所使用的库很灵活,这意味着它不必为bs4。谢谢。

编辑:

我刚刚发现,如果从上述代码中删除recursive=False,它将返回1而不是0。不知道为什么吗?

编辑:

我也尝试了下面的代码,但是它也返回0。贝娄也是我想要最终产品的例子,全部都放在字典中。

import xml.etree.ElementTree as ET

def get_data():
    tree = ET.parse(open("wiki.xml"))
    root = tree.getroot()
    results = {}
    for title in root.findall('./page/title') and text in root.findall('./page/revision/text'):
        results[title] = text
    return results

r = get_data()
print(len(r))

编辑:

我刚刚在xml文件下面尝试了一些代码...

<vehicles>
  <car name="BMW">
    <model>850 CSI</model>
    <speed>1000</speed>
  </car>
  <car name="Mercedes">
    <model>SL65</model>
    <speed>900</speed>
  </car>
  <car name="Jaguar">
    <model>EV400</model>
    <speed>850</speed>
  </car>
  <car name="Ferrari">
    <model>Enzo</model>
    <speed>2</speed>
  </car>
</vehicles>

这是我使用的代码...

from bs4 import BeautifulSoup

def get_data():
    soup = BeautifulSoup(open('test.xml'), 'lxml')
    count = 0
    for text in soup.select("vehicles car model"):
        count += 1
    return count

r = get_data()
print(r)

该脚本返回了正确的数字4。但是,当我将vehicles car model更改为page revision text并在wiki.xml文件上尝试时,它不起作用,仍然返回1。注意:在wiki文件中,文本元素更多,所以我有时间算一下自己,所以1绝对是错误的。

编辑:

这是我一直试图用来解析文件的代码...

def parser(file_name="wiki.xml",save_to="weboffline.csv",url='http://www.mediawiki.org/xml/export-0.10/'):
    doc = tree.parse(file_name)
    titles = []
    texts = []
    for title in doc.findall('.//mediawiki{'+url+'}//page//title'):
        titles.append(title)
    for text in doc.findall('.//mediawiki{'+url+'}//page//revision//text'):
        texts.append(text)
    with open(save_to, mode='w') as file:
        writer = csv.writer(file)
        writer.writerow(['TITLES', 'TEXT'])
        for items in zip(titles,texts):
            writer.writerow(items)

但是,此CSV文件仅返回TITLES,TEXT。有人有解决方案吗?

2 个答案:

答案 0 :(得分:0)

recursive=False将仅找到顶级元素的直接子级。在您显示的示例中,<mediawiki>的唯一子项是<siteinfo><page>,没有<text>,因此0是正确的。通过递归到结构,我们发现单个<text>作为子元素,同时递归到<page>然后是<revision>。所以1是正确的!

如果要查找这样的子代子女(等),则必须使用recursive=True(这意味着省略了recursive选项)。

答案 1 :(得分:0)

import xml.etree.ElementTree as etree
import codecs
import csv
import time
import os

def hms_string(sec_elapsed):
    h = int(sec_elapsed / (60 * 60))
    m = int((sec_elapsed % (60 * 60)) / 60)
    s = sec_elapsed % 60
    return "{}:{:>02}:{:>05.2f}".format(h, m, s)

def strip_tag_name(t,elem):
    t = elem.tag
    idx = k = t.rfind("}")
    if idx != -1:
        t = t[idx + 1:]
    return t

def parseWikiFile():
    PATH_WIKI_XML = ''
    FILENAME_WIKI = 'resources/wiki.xml'
    FILENAME_PAGES = 'resources/weboffline.csv'
    ENCODING = "utf-8"

    pathWikiXML = os.path.join(PATH_WIKI_XML, FILENAME_WIKI)
    pathPages = os.path.join(PATH_WIKI_XML, FILENAME_PAGES)

    pageCount = 0
    totalCount = 0
    title = None
    start_time = time.time()

    with codecs.open(pathPages, "w", ENCODING) as pagesFH:
        pageWriter = csv.writer(pagesFH, quoting=csv.QUOTE_MINIMAL)
        pageWriter.writerow(['title', 'text'])

        for event, elem in etree.iterparse(pathWikiXML, events=('start', 'end')):
            tname = strip_tag_name(elem.tag,elem)

            if event == 'start':
                if tname == 'page':
                    title = ''
                    text = ''
                else:
                    continue
            else:
                if tname == 'title':
                    title = elem.text
                elif tname == 'text':
                    text = elem.text
                elif tname == 'page':
                    totalCount += 1
                    pageCount += 1
                    pageWriter.writerow([title, text])

                    if totalCount > 1 and (totalCount % 100000) == 0:
                        print("{:,}".format(totalCount))

                elem.clear()

    elapsed_time = time.time() - start_time

    print("Total pages: {:,}".format(totalCount))
    print("Text pages: {:,}".format(pageCount))
    print("Elapsed time: {}".format(hms_string(elapsed_time)))

这是我在this网站上修改的代码,效果非常好。我认为我最初试图做的只是将元素查找为位置而不是标签名称。无论如何,这行得通。