programing

파이썬에서 대용량 XML 문서를 구문 분석하는 가장 빠른 방법은 무엇입니까?

starjava 2023. 10. 19. 21:48
반응형

파이썬에서 대용량 XML 문서를 구문 분석하는 가장 빠른 방법은 무엇입니까?

저는 현재 파이썬 쿡북의 12.5장을 기반으로 다음과 같은 코드를 실행하고 있습니다.

from xml.parsers import expat

class Element(object):
    def __init__(self, name, attributes):
        self.name = name
        self.attributes = attributes
        self.cdata = ''
        self.children = []
    def addChild(self, element):
        self.children.append(element)
    def getAttribute(self,key):
        return self.attributes.get(key)
    def getData(self):
        return self.cdata
    def getElements(self, name=''):
        if name:
            return [c for c in self.children if c.name == name]
        else:
            return list(self.children)

class Xml2Obj(object):
    def __init__(self):
        self.root = None
        self.nodeStack = []
    def StartElement(self, name, attributes):
        element = Element(name.encode(), attributes)
        if self.nodeStack:
            parent = self.nodeStack[-1]
            parent.addChild(element)
        else:
            self.root = element
        self.nodeStack.append(element)
    def EndElement(self, name):
        self.nodeStack.pop()
    def CharacterData(self,data):
        if data.strip():
            data = data.encode()
            element = self.nodeStack[-1]
            element.cdata += data
    def Parse(self, filename):
        Parser = expat.ParserCreate()
        Parser.StartElementHandler = self.StartElement
        Parser.EndElementHandler = self.EndElement
        Parser.CharacterDataHandler = self.CharacterData
        ParserStatus = Parser.Parse(open(filename).read(),1)
        return self.root

1GB 정도 크기의 XML 문서로 작업하고 있습니다.이것들을 파싱하는 더 빠른 방법을 아는 사람?

나는 당신이 당신의 프로그램에서 DOM 기능을 필요로 하지 않는 것처럼 나를 바라봅니다.나는 (c)Element의 사용에 찬성합니다.트리 라이브러리.cElement의 iterparse 함수를 사용하는 경우트리 모듈, xml을 통해 이벤트가 발생할 때 처리할 수 있습니다.

참고로, 프레드릭은 cElement를 사용하는 것에 대한 조언을 합니다.트리이터 구문 분석 기능:

대용량 파일을 구문 분석하려면 요소를 처리하는 즉시 제거할 수 있습니다.

for event, elem in iterparse(source):
    if elem.tag == "record":
        ... process record elements ...
        elem.clear()

위 패턴은 루트 요소를 제거하지 못하기 때문에 빈 자식 요소가 많은 단일 요소로 귀결됩니다.파일 크기가 큰 것이 아니라 크기가 큰 경우 문제가 될 수 있습니다.이 문제를 해결하려면 뿌리 요소를 손에 넣어야 합니다.가장 쉬운 방법은 시작 이벤트를 활성화하고 변수에 첫 번째 요소에 대한 참조를 저장하는 것입니다.

# get an iterable
context = iterparse(source, events=("start", "end"))

# turn it into an iterator
context = iter(context)

# get the root element
event, root = context.next()

for event, elem in context:
    if event == "end" and elem.tag == "record":
        ... process record elements ...
        root.clear()

lxml.iterparse()에서는 이를 허용하지 않습니다.

이전 버전은 파이썬 3.7에서는 작동하지 않으므로 첫 번째 요소를 얻기 위해 다음과 같은 방법을 고려해 보십시오.

import xml.etree.ElementTree as ET

# Get an iterable.
context = ET.iterparse(source, events=("start", "end"))
    
for index, (event, elem) in enumerate(context):
    # Get the root element.
    if index == 0:
        root = elem
    if event == "end" and elem.tag == "record":
        # ... process record elements ...
        root.clear()

해보셨나요?cElementTree모듈?

cElementTree는 xml.etree로 Python 2.5 이상에 포함되어 있습니다.cElementTree.벤치마크를 참조합니다.

참고로 파이썬 3.3 이후로cElementTree는 기본 구현으로 사용되므로 Python 버전 3.3+에서는 이러한 변경이 필요하지 않습니다.

삭제된 ImageShack 링크

lxml을 사용하는 것을 추천합니다. libxml2 라이브러리를 위한 파이썬 바인딩으로 정말 빠릅니다.

제 경험상 libxml2와 expat은 성능이 매우 비슷합니다.하지만 저는 libxml2(그리고 python의 경우 lxml)를 더 선호합니다. 왜냐하면 더 활발하게 개발되고 테스트되고 있는 것 같기 때문입니다.또한 libxml2는 더 많은 기능을 가지고 있습니다.

lxml은 대부분 xml.etree와 API 호환이 가능합니다.요소 트리.그리고 웹사이트에 좋은 자료가 있습니다.

콜백을 등록하면 파싱 속도가 엄청나게 느려집니다.[편집]이것은 (빠른) C 코드가 C만큼 빠르지 않은 파이썬 인터프리터를 호출해야 하기 때문입니다.기본적으로 C 코드를 사용하여 파일을 읽고(빠름) 파이썬에서 DOM을 구축합니다(느림).[/EDIT]

xml.etree를 사용해 보십시오.요소C에서 100% 구현되며 파이썬 코드에 대한 콜백 없이 XML을 파싱할 수 있는 트리.

문서를 구문 분석한 후 원하는 내용을 얻기 위해 문서를 필터링할 수 있습니다.

그래도 너무 느리고 DOM이 필요 없는 경우 파일을 문자열로 읽고 간단한 문자열 작업을 사용하여 처리하는 방법도 있습니다.

응용프로그램이 성능에 민감하고 큰 파일(말씀하신 것처럼 1GB 이상)이 발생할 가능성이 있는 경우 전체 문서를 RAM에 로드한다는 간단한 이유로 질문에 표시된 코드를 사용하지 말 것을 강력히 권고합니다.전체 문서 트리를 한 번에 RAM에 보유하는 것을 방지하기 위해 가능하다면 설계를 다시 생각해 보는 것이 좋습니다.애플리케이션의 요구 사항이 무엇인지 모르기 때문에 "이벤트 기반" 설계를 사용하기 위한 일반적인 조언 외에 구체적인 접근 방법을 제대로 제안할 수 없습니다.

expat ParseFile은 트리 전체를 메모리에 저장할 필요가 없는 경우 잘 작동하며, 이로 인해 대용량 파일의 RAM이 조만간 손상될 것입니다.

import xml.parsers.expat
parser = xml.parsers.expat.ParserCreate()
parser.ParseFile(open('path.xml', 'r'))

파일을 청크로 읽어 램을 폭발시키지 않고 파서에 공급합니다.

문서: https://docs.python.org/2/library/pyexpat.html#xml.parsers.expat.xmlparser.ParseFile

나는 이것을 시도하는데 꽤 많은 시간을 소비했고 가장 빠르고 가장 적은 메모리 집약적인 접근법은 lxml과 iterparse를 사용하는 것이지만 불필요한 메모리를 확보하는 것입니다.예제에서 arXiv 덤프 구문 분석:

from lxml import etree

context = etree.iterparse('path/to/file', events=('end',), tag='Record')

for event, element in context:
    record_id = element.findtext('.//{http://arxiv.org/OAI/arXiv/}id')
    created = element.findtext('.//{http://arxiv.org/OAI/arXiv/}created')

    print(record_id, created)

    # Free memory.
    element.clear()
    while element.getprevious() is not None:
        del element.getparent()[0]

그렇게element.clear충분하지 않을 뿐만 아니라 이전 요소에 대한 링크도 제거합니다.

파이썬3에서는 구문을 변경해야 합니다.
이것 대신에

# get the root element
event, root = context.next()

이를 시도해 보십시오(Iterparse 개체에서 권장하는 것처럼 다음에는 속성이 없습니다).

# get the root element
event, root = next(context)

그리고 이 대사는 불필요합니다.

# turn it into an iterator
context = iter(context)

언급URL : https://stackoverflow.com/questions/324214/what-is-the-fastest-way-to-parse-large-xml-docs-in-python

반응형