파이썬에서 대용량 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
'programing' 카테고리의 다른 글
UI 검색바 검색 텍스트 색상을 변경하려면 어떻게 해야 합니까? (0) | 2023.10.19 |
---|---|
ui 부트스트랩 datepicker footer angularjs 제거방법 (0) | 2023.10.19 |
SQL Server에서 날짜 시간 필터링의 성능을 향상시키는 방법은 무엇입니까? (0) | 2023.10.19 |
PHP에서 MySQL 테이블 구조를 얻으려면 어떻게 해야 합니까?그리고 모든 테이블의 목록? (0) | 2023.10.19 |
C와 C++에서 (...)를 무엇이라고 합니까? (0) | 2023.10.14 |