2012-01-17 8 views
12

मैं एक एक्सएमएल फ़ाइल को पार्स करने की कोशिश कर रहा हूं जो कि 2 जीबी से अधिक है जो पाइथन की एलएक्सएमएल लाइब्रेरी के साथ है। दुर्भाग्यवश, एक्सएमएल फ़ाइल में वर्ण एन्कोडिंग को एक पंक्ति नहीं है, इसलिए मुझे इसे मैन्युअल रूप से सेट करना होगा। हालांकि फ़ाइल के माध्यम से पुनरावृत्ति करते हुए, अभी भी कुछ अजीब पात्र हैं जो थोड़ी देर में आते हैं।एक बड़ी एक्सएमएल फ़ाइल को पार्स करते समय मैं Python के lxml में XMLSyntaxError से कैसे निपटूं?

मुझे यकीन नहीं है कि लाइन के चरित्र एन्कोडिंग को कैसे निर्धारित किया जाए, लेकिन इसके अलावा, lxml लूप के दायरे से XMLSyntaxError को बढ़ाएगा। मैं इस त्रुटि को सही तरीके से कैसे पकड़ सकता हूं, और इसके साथ सही तरीके से निपट सकता हूं?

from lxml import etree 
etparse = etree.iterparse(file("my_file.xml", 'r'), events=("start",), encoding="CP1252") 
for event, elem in etparse: 
    if elem.tag == "product": 
     print "Found the product!" 
     elem.clear() 

यह अंततः त्रुटि पैदा करता है: यहाँ एक साधारण कोड का टुकड़ा है

XMLSyntaxError: PCDATA invalid Char value 31, line 1565367, column 50

फ़ाइल का यह लाइन इस तरह दिखता है:

% sed -n "1565367 p" my_file.xml 
<romance_copy>Ravioli Florentine. Tender Ravioli Filled With Creamy Ricotta Cheese And 

भरा के 'एफ' वास्तव में मेरे टर्मिनल में ऐसा लगता है:

xml line causing the error

+0

क्या आपने पहले ही एन्कोडिंग के लिए "utf-8" की कोशिश की है? – jsbueno

+1

@jsbueno: समस्या "F" में "F" से ठीक पहले वर्ण है, जिसमें 31 (दशमलव) या 0x1F का मान है। यह एक्सएमएल विनिर्देश के प्रति एक अमान्य चरित्र है, इसलिए यूटीएफ -8 एन्कोडिंग का उपयोग करने के लिए यह एक फर्क नहीं पड़ता है। प्रश्न यह है कि खराब वर्णों से अधिक सावधानीपूर्वक सामना करने के लिए lxml कैसे प्राप्त करें (यानी अपवाद नहीं फेंकें)। मुझे lxml दस्तावेज़ में ऐसा करने का विकल्प नहीं मिला। –

उत्तर

7

यहाँ करने के लिए सही बात यकीन है कि एक्सएमएल फ़ाइल के निर्माता करता है कि सुनिश्चित करें कि है: ए) कि फ़ाइल की एन्कोडिंग घोषित किया जाता है बी) कि एक्सएमएल फ़ाइल अच्छी तरह से बनाई है (कोई अमान्य वर्ण वर्णों को नियंत्रित करते हैं, कोई अमान्य वर्ण जो एन्कोडिंग योजना में नहीं आ रहे हैं, सभी तत्व ठीक से बंद हैं आदि) सी) यदि आप यह सुनिश्चित करना चाहते हैं कि कुछ विशेषताओं/तत्व मौजूद हैं, तो निश्चित रूप से एक डीटीडी या एक्सएमएल स्कीमा का उपयोग करें मान या किसी निश्चित प्रारूप से मेल खाते हैं (नोट: यह एक प्रदर्शन हिट लेगा)

तो, अब आपके प्रश्न पर। जब आप एक्सएमएल का विश्लेषण करने के लिए इसका इस्तेमाल करते हैं तो एलएक्सएमएल तर्कों के पूरे समूह का समर्थन करता है। Check out the documentation। आप इन दो तर्कों को देखना चाहेंगे:

-> पुनर्प्राप्त करें -> टूटी हुई एक्सएमएल
-> huge_tree -> सुरक्षा प्रतिबंधों को अक्षम करें और बहुत गहरे पेड़ और बहुत लंबी टेक्स्ट सामग्री का समर्थन करें (केवल libxml2 2.7+ को प्रभावित करता है)

वे आपकी डिग्री में मदद करेंगे, लेकिन कुछ अमान्य वर्णों को अभी से पुनर्प्राप्त नहीं किया जा सकता है, फिर से, यह सुनिश्चित करना कि फाइल सही ढंग से लिखी गई है, यह साफ/अच्छी तरह से काम करने वाला कोड है ।

आह हाँ और एक और बात। 2 जीबी बड़ा है। मुझे लगता है कि आपके पास इस फ़ाइल में समान तत्वों की एक सूची है (पुस्तकों की उदाहरण सूची)। ओएस पर एक रेगेक्स अभिव्यक्ति के साथ फ़ाइल को विभाजित करने का प्रयास करें, फिर टुकड़ों को विभाजित करने के लिए कई प्रक्रियाएं शुरू करें। इस तरह आप अपने बॉक्स पर अपने अधिक कोर का उपयोग करने में सक्षम होंगे और प्रसंस्करण का समय नीचे जाएगा। बेशक आपको परिणामों को एक साथ वापस विलय करने की जटिलता से निपटना होगा। मैं तुम्हारे लिए इस व्यापार बंद नहीं कर सकते हैं, लेकिन यह आप के लिए के रूप में "खाद्य विचार के लिए"

अलावा पोस्ट करने के लिए देना चाहता था: आप इनपुट फ़ाइल पर कोई नियंत्रण नहीं है और यह बुरा वर्ण है, तो , मैं फ़ाइल के रूप में इसे पार्स करने से पहले स्ट्रिंग पर पुनरावृत्ति करके इन खराब वर्णों को प्रतिस्थापित/निकालने का प्रयास करूंगा।यहाँ एक कोड नमूना कि Unicode control characters that you wont need निकालता है:

#all unicode characters from 0x0000 - 0x0020 (33 total) are bad and will be replaced by "" (empty string) 
for line in fileinput.input(xmlInputFileLocation, inplace=1): 
    for pos in range(0,len(line)): 
     if unichr(line[pos]) < 32: 
      line[pos] = None 
    print u''.join([c for c in line if c]) 
+0

+1, लेकिन 'iterparse' एक ईवेंट-आधारित पार्सर है, इसलिए यह बड़ी फ़ाइलों को ठीक से संभाल सकता है। –

+1

दुर्भाग्यवश, एक्सएमएल फ़ाइल किसी तीसरे पक्ष से रात के पेलोड में आती है। मेरे पास सामग्री पर कोई नियंत्रण नहीं है। ऐसा कहा जा रहा है कि, फ़ाइल एन्कोडिंग की घोषणा पर मेरा कोई नियंत्रण नहीं है, जिसमें फ़ाइल नहीं है। एक्सएमएल फ़ाइल अच्छी तरह से गठित नहीं है, इसमें कुछ अजीब पात्र हैं। और, फ़ाइल किसी भी डीटीडी या एक्सएमएल स्कीमा की सदस्यता नहीं लेती है, और विक्रेता को यह समझ में नहीं आता है कि क्या है ... दुर्भाग्यवश, मैं यहां पर हूं। – blackrobot

+0

अपने कोड में, जहां आप 'unichr' का उपयोग करते हैं, तो आपका मतलब 'ord' है। – maurits

0

codecs पायथन मॉड्यूल की आपूर्ति एक EncodedFile वर्ग है कि एक फाइल करने के लिए एक आवरण के रूप में काम करता है - आप इस वर्ग, lxml करने के लिए सेट का एक उद्देश्य के साथ अज्ञात पात्रों को बदलने के लिए पास करना चाहिए एक्सएमएल चार संस्थाओं -

कोशिश यह कर:

from lxml import etree 
import codecs 

enc_file = codecs.EncodedFile(file("my_file.xml"), "ASCII", "ASCII", "xmlcharrefreplace") 

etparse = etree.iterparse(enc_file, events=("start",), encoding="CP1252") 
... 

"xmlcharrefreplace" निरंतर पारित कर दिया "त्रुटियों" पैरामीटर है, और करने के लिए क्या निर्दिष्ट करता है अज्ञात पात्रों के साथ करो। यह "सख्त" हो सकता है (एक त्रुटि उठाता है), "अनदेखा करें" (जैसा छोड़ें), "प्रतिस्थापित करें" ("xmlrefreplace" के साथ char को प्रतिस्थापित करता है) ("& #xxxx;" xml संदर्भ) या " बैकस्लाह्रेलेस "(एक पायथन वैध बैकस्लैश संदर्भ बनाता है)। अधिक जानकारी के लिए देखें: http://docs.python.org/library/codecs.html

+1

दुर्भाग्य से, यह वही त्रुटि देता है, भले ही मैं "अनदेखा" या "प्रतिस्थापन" का उपयोग करता हूं। 'XMLSyntaxError: पीसीडीएटीए अमान्य चार मान 31, लाइन 1565367, कॉलम 50' – blackrobot

3

मैं भी इस में भाग गया, डेटा में \x16 हो रही (यूनिकोड 'तुल्यकालिक निष्क्रिय' या 'SYN' चरित्र, ^V के रूप में xml में दिखाया गया है), जो जब पार्स करने में त्रुटि की ओर जाता है xml: XMLSyntaxError: PCDATA invalid Char value 22. 22 क्योंकि ord('\x16') 22 है।

@ माइकल का जवाब मुझे सही रास्ते पर रखता है। लेकिन 32 से नीचे कुछ नियंत्रण वर्ण ठीक हैं, जैसे वापसी या टैब, और कुछ उच्च वर्ण अभी भी खराब हैं। तो:

# Get list of bad characters that would lead to XMLSyntaxError. 
# Calculated manually like this: 
from lxml import etree 
from StringIO import StringIO 
BAD = [] 
for i in range(0, 10000): 
    try: 
     x = etree.parse(StringIO('<p>%s</p>' % unichr(i))) 
    except etree.XMLSyntaxError: 
     BAD.append(i) 

यह 31 वर्णों की एक सूची है कि बजाय कोड में उपरोक्त गणना करने का hardcoded किया जा सकता है की ओर जाता है:

def remove_bad_chars(value): 
    # Remove bad control characters. 
    if isinstance(value, unicode): 
     for char in BAD_UNICODE_CHARS: 
      value = value.replace(char, u'') 
    elif isinstance(value, basestring): 
     for char in BAD_BASESTRING_CHARS: 
      value = value.replace(char, '') 
    return value 

:

BAD = [ 
    0, 1, 2, 3, 4, 5, 6, 7, 8, 
    11, 12, 
    14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 
    # Two are perfectly valid characters but go wrong for different reasons. 
    # 38 is '&' which gives: xmlParseEntityRef: no name. 
    # 60 is '<' which gives: StartTag: invalid element namea different error. 
] 
BAD_BASESTRING_CHARS = [chr(b) for b in BAD] 
BAD_UNICODE_CHARS = [unichr(b) for b in BAD] 

तो इस तरह इसका इस्तेमाल यदि value 2 गीगाबाइट है तो आपको इसे अधिक कुशल तरीके से करने की आवश्यकता हो सकती है, लेकिन मैं इसे यहां अनदेखा कर रहा हूं, हालांकि सवाल यह उल्लेख करता है। मेरे मामले में, मैं xml फ़ाइल बनाने वाला हूं, लेकिन मुझे मूल डेटा में इन वर्णों से निपटने की ज़रूरत है, इसलिए मैं XML में डेटा डालने से पहले इस फ़ंक्शन का उपयोग करूंगा।

संबंधित मुद्दे