পাইথন এলিমেন্ট্রি মডিউল: পদ্ধতিটি "ফাইন্ড", "ফোল্ডাল" ব্যবহার করার সময় মিলের উপাদান সনাক্ত করতে এক্সএমএল ফাইলের নাম স্থানটিকে কীভাবে উপেক্ষা করবেন?


136

আমি এলিমেন্ট্রি মডিউলটিতে উত্স এক্সএমএল ফাইলের কিছু উপাদান সনাক্ত করতে "Findall" পদ্ধতিটি ব্যবহার করতে চাই।

তবে উত্স এক্সএমএল ফাইলের (টেস্ট.এক্সএমএল) নাম স্থান রয়েছে। আমি নমুনা হিসাবে এক্সএমএল ফাইলের অংশ ছাঁটাই:

<?xml version="1.0" encoding="iso-8859-1"?>
<XML_HEADER xmlns="http://www.test.com">
    <TYPE>Updates</TYPE>
    <DATE>9/26/2012 10:30:34 AM</DATE>
    <COPYRIGHT_NOTICE>All Rights Reserved.</COPYRIGHT_NOTICE>
    <LICENSE>newlicense.htm</LICENSE>
    <DEAL_LEVEL>
        <PAID_OFF>N</PAID_OFF>
        </DEAL_LEVEL>
</XML_HEADER>

নমুনা পাইথন কোডটি নীচে রয়েছে:

from xml.etree import ElementTree as ET
tree = ET.parse(r"test.xml")
el1 = tree.findall("DEAL_LEVEL/PAID_OFF") # Return None
el2 = tree.findall("{http://www.test.com}DEAL_LEVEL/{http://www.test.com}PAID_OFF") # Return <Element '{http://www.test.com}DEAL_LEVEL/PAID_OFF' at 0xb78b90>

যদিও এটি কাজ করতে পারে, কারণ এখানে একটি নেমস্পেস রয়েছে "{http://www.test.com}", প্রতিটি ট্যাগের সামনে একটি নেমস্পেস যুক্ত করা খুব অসুবিধাজনক।

"ফাইন্ড", "ফান্ডাল" এবং এই জাতীয় পদ্ধতি ব্যবহার করার পরে আমি কীভাবে নামকরণটিকে উপেক্ষা করতে পারি?


18
কি tree.findall("xmlns:DEAL_LEVEL/xmlns:PAID_OFF", namespaces={'xmlns': 'http://www.test.com'})সুবিধাজনক কি যথেষ্ট?
iMom0

অনেক ধন্যবাদ. আমি আপনার পদ্ধতিটি চেষ্টা করি এবং এটি কাজ করতে পারে। এটি আমার চেয়ে বেশি সুবিধাজনক তবে এটি এখনও কিছুটা বিশ্রী। আপনি কি জানেন যে এই সমস্যা সমাধানের জন্য এলিমেন্ট্রি মডিউলে অন্য কোনও সঠিক পদ্ধতি নেই বা আদৌ এ জাতীয় কোনও পদ্ধতি নেই?
কেভিনলিং

বা চেষ্টা করুনtree.findall("{0}DEAL_LEVEL/{0}PAID_OFF".format('{http://www.test.com}'))
ওয়ার্ফ

পাইথন ৩.৮-তে, ওয়াইল্ডকার্ড নামের জায়গার জন্য ব্যবহার করা যেতে পারে। stackoverflow.com/a/62117710/407651
mzjn

উত্তর:


62

এক্সএমএল ডকুমেন্ট নিজেই পরিবর্তিত করার পরিবর্তে, এটি পার্স করা ভাল এবং তারপরে ফলাফলগুলিতে ট্যাগগুলি সংশোধন করা ভাল। আপনি একাধিক নেমস্পেস এবং নেমস্পেস এলিয়াসগুলি পরিচালনা করতে পারেন:

from io import StringIO  # for Python 2 import from StringIO instead
import xml.etree.ElementTree as ET

# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
    prefix, has_namespace, postfix = el.tag.partition('}')
    if has_namespace:
        el.tag = postfix  # strip all namespaces
root = it.root

এটি এখানে আলোচনার ভিত্তিতে: http://bugs.python.org/issue18304

আপডেট: rpartition পরিবর্তে কোনও নামস্থান partitionনা postfixথাকলেও আপনি ট্যাগ নামটি নিশ্চিত করে তা নিশ্চিত করে । সুতরাং আপনি এটি ঘনীভূত করতে পারে:

for _, el in it:
    _, _, el.tag = el.tag.rpartition('}') # strip ns

2
এই. এই এই এই. একাধিক নামের স্থান আমার মৃত্যু হতে চলেছিল।
জেস

8
ঠিক আছে, এটি দুর্দান্ত এবং আরও উন্নত তবে এটি এখনও তা নয় et.findall('{*}sometag')। এবং এটি নিজেই উপাদান গাছকে মংগল করছে, "কেবলমাত্র এই মুহুর্তে নেমস্পেসগুলি উপেক্ষা করে অনুসন্ধান করা নয়, দস্তাবেজকে পুনরায় পার্সিং না করে, নামের স্থানের তথ্য ধরে রাখা"। ঠিক আছে, সেই ক্ষেত্রে আপনার অবশ্যই অবশ্যই গাছটির মধ্য দিয়ে পুনরাবৃত্তি করতে হবে এবং নিজের অবস্থানটি দেখুন, যদি নোডটি নামস্থান সরিয়ে দেওয়ার পরে আপনার ইচ্ছার সাথে মেলে।
টমাসজ গ্যান্ডার

1
স্ট্রিং stripping কিন্তু দ্বারা এই কাজ যখন আমি ফাইলে লেখা ব্যবহার XML ফাইল (...) এক্সএমএল xmlns = "এর ভিক্ষা থেকে নামস্থান dissapears bla " dissapears। দয়া করে পরামর্শ দিন
ট্রেসকিরা

@ টমাসজান্ডার: আপনি সম্ভবত একটি পৃথক বৈশিষ্ট্যের সাথে নেমস্পেস যুক্ত করতে পারেন। সাধারণ ট্যাগ ধারক পরীক্ষার জন্য ( এই দস্তাবেজে এই ট্যাগটির নাম রয়েছে? ) এই সমাধানটি দুর্দান্ত এবং সংক্ষিপ্ত-প্রচারিত হতে পারে।
মার্টিজন পিটারস

@ ট্রেসকিরা: এই কৌশলটি পার্সড ডকুমেন্ট থেকে নেমস্পেসগুলি সরিয়ে দেয় এবং আপনি নামটি স্থানের সাহায্যে একটি নতুন এক্সএমএল স্ট্রিং তৈরি করতে এটি ব্যবহার করতে পারবেন না। হয় কোনও অতিরিক্ত বৈশিষ্ট্যে নেমস্পেসের মানগুলি সংরক্ষণ করুন (এবং এক্সএমএল গাছটিকে আবার স্ট্রিংয়ে ফেলার আগে নেমস্পেসটি আবার রেখে দিন) বা ফেলা গাছের উপর ভিত্তি করে পরিবর্তনগুলি প্রয়োগ করতে মূল উত্স থেকে পুনরায় পার্স করুন।
মার্টিজন পিটারস

48

আপনি যদি বিশ্লেষণের আগে xMLns বৈশিষ্ট্যটিকে xML থেকে মুছে ফেলে থাকেন তবে গাছের প্রতিটি ট্যাগে কোনও নামের স্থান যুক্ত করা হবে না।

import re

xmlstring = re.sub(' xmlns="[^"]+"', '', xmlstring, count=1)

5
এটি আমার পক্ষে অনেক ক্ষেত্রে কাজ করেছে, কিন্তু তারপরে আমি একাধিক নেমস্পেস এবং নেমস্পেসের উপাধিতে চলে এসেছি। এই মামলাগুলি পরিচালনা করে এমন অন্য পদ্ধতির জন্য আমার উত্তরটি দেখুন।
নোনাগন

47
-1 পার্সিংয়ের আগে নিয়মিত প্রকাশের মাধ্যমে এক্সএমএলকে ম্যানিপুলেট করা ঠিক ভুল। যদিও এটি কিছু ক্ষেত্রে কার্যকর হতে পারে, এটি সর্বোপরি ভোট দেওয়া উত্তর হওয়া উচিত নয় এবং পেশাদার প্রয়োগে ব্যবহার করা উচিত নয়।
মাইকে

1
এক্সএমএল পার্সিং কাজের জন্য একটি রেইগেক্স ব্যবহার সহজাতভাবে নিরবিচ্ছিন্ন, এ ছাড়াও এটি অনেক এক্সএমএল নথির জন্য কাজ করে না , কারণ এটি নামের স্থানের উপসর্গগুলি উপেক্ষা করে এবং এক্সএমএল সিনট্যাক্স বৈশিষ্ট্যের নামের পূর্বে স্বেচ্ছাসেবী স্থানের অনুমতি দেয় (কেবল নয়) স্পেস) এবং =সমান চিহ্নের চারপাশে ।
মার্টিজন পিটারস

হ্যাঁ, এটি দ্রুত এবং নোংরা, তবে এটি অবশ্যই সাধারণ ব্যবহারের ক্ষেত্রে সবচেয়ে মার্জিত সমাধান, ধন্যবাদ!
রিমকক্সক্স

18

উত্তরগুলি এখনও পর্যন্ত স্পষ্টতই স্ক্রিপ্টে নেমস্পেসের মান রাখে। আরও জেনেরিক সমাধানের জন্য, আমি বরং xML থেকে নেমস্পেসটি বের করতে চাই:

import re
def get_namespace(element):
  m = re.match('\{.*\}', element.tag)
  return m.group(0) if m else ''

এবং এটি ফাইন্ড পদ্ধতিতে ব্যবহার করুন:

namespace = get_namespace(tree.getroot())
print tree.find('./{0}parent/{0}version'.format(namespace)).text

15
অনুমান করার জন্য খুব একটা আছে যে কেবল একটিই আছেnamespace
কাশ্যপ

এটি আমলে নিবে না যে নেস্টেড ট্যাগগুলি বিভিন্ন নেমস্পেস ব্যবহার করতে পারে।
মার্টিজন পিটারস

15

নোনগনের উত্তরের জন্য এখানে একটি বর্ধিতাংশ রয়েছে যা নামের বৈশিষ্ট্যগুলিও বন্ধ করে দেয়:

from StringIO import StringIO
import xml.etree.ElementTree as ET

# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
    if '}' in el.tag:
        el.tag = el.tag.split('}', 1)[1]  # strip all namespaces
    for at in list(el.attrib.keys()): # strip namespaces of attributes too
        if '}' in at:
            newat = at.split('}', 1)[1]
            el.attrib[newat] = el.attrib[at]
            del el.attrib[at]
root = it.root

আপডেট: list()পুনরুক্তি কাজ করে তাই যুক্ত (পাইথন 3 জন্য প্রয়োজনীয়)


14

এরিকস্পড দ্বারা উত্তরের উন্নতি:

বিশ্বব্যাপী পার্স মোড পরিবর্তন করার পরিবর্তে আমরা এটিকে কনস্ট্রাক্ট সহ সমর্থনকারী কোনও অবজেক্টে গুটিয়ে রাখতে পারি।

from xml.parsers import expat

class DisableXmlNamespaces:
    def __enter__(self):
            self.oldcreate = expat.ParserCreate
            expat.ParserCreate = lambda encoding, sep: self.oldcreate(encoding, None)
    def __exit__(self, type, value, traceback):
            expat.ParserCreate = self.oldcreate

এটি নিম্নলিখিত হিসাবে ব্যবহার করা যেতে পারে

import xml.etree.ElementTree as ET
with DisableXmlNamespaces():
     tree = ET.parse("test.xml")

এই উপায়টির সৌন্দর্য হ'ল এটি ব্লকযুক্ত বাইরের সাথে সম্পর্কিত সম্পর্কযুক্ত কোডের জন্য কোনও আচরণ পরিবর্তন করে না। আমি এরিস্কোপড দ্বারা সংস্করণ ব্যবহার করার পরে সম্পর্কযুক্ত লাইব্রেরিতে ত্রুটি পাওয়ার পরে এটি তৈরি শেষ করেছি যা এক্সপেট ব্যবহারের ক্ষেত্রেও ঘটেছিল।


এটি মিষ্টি এবং স্বাস্থ্যকর! আমার দিন বাঁচা! +1
অ্যান্ড্রেসট

পাইথন ৩.৮ এ (অন্যান্য সংস্করণ দিয়ে পরীক্ষা করা হয়নি) এটি আমার পক্ষে কাজ করে না বলে মনে হয়। উত্সটির দিকে তাকালে এটি কাজ করা উচিত , তবে মনে হয় এর উত্স কোডটি xml.etree.ElementTree.XMLParserকোনওভাবেই অনুকূলিত হয়েছে এবং বানর-প্যাচিংয়ের expatপুরোপুরি কোনও প্রভাব নেই।
রিইনারিয়ান

ওহ হ্যাঁ. : @ Barny এর মন্তব্য দেখুন stackoverflow.com/questions/13412496/...
Reinderien

5

আপনি মার্জিত স্ট্রিং ফর্ম্যাটিং কনস্ট্রাক্টও ব্যবহার করতে পারেন:

ns='http://www.test.com'
el2 = tree.findall("{%s}DEAL_LEVEL/{%s}PAID_OFF" %(ns,ns))

বা, আপনি যদি নিশ্চিত হন যে PAID_OFF কেবলমাত্র গাছের এক স্তরে উপস্থিত হবে:

el2 = tree.findall(".//{%s}PAID_OFF" % ns)

2

আপনি যদি ব্যবহার করছেন ElementTreeএবং না cElementTreeরাখছেন তবে প্রতিস্থাপন করে এক্সপেটকে নামস্থান স্থান প্রক্রিয়াকরণ উপেক্ষা করতে বাধ্য করতে পারেন ParserCreate():

from xml.parsers import expat
oldcreate = expat.ParserCreate
expat.ParserCreate = lambda encoding, sep: oldcreate(encoding, None)

ElementTreeকল করে এক্সপেট ব্যবহার করার চেষ্টা করে ParserCreate()কিন্তু নামস্থান স্পেসিটার পৃথক স্ট্রিং সরবরাহ না করার কোনও বিকল্প সরবরাহ করে না, উপরের কোডটি এটিকে উপেক্ষা করার কারণ হতে পারে তবে সতর্ক করা হবে এটি অন্যান্য জিনিসগুলিকে ভেঙে ফেলতে পারে।


এটি অন্যান্য বর্তমান উত্তরের চেয়ে ভাল উপায় কারণ এটি স্ট্রিং প্রক্রিয়াজাতকরণের উপর নির্ভর করে না
lijat

3
অজগর ৩. 3..২ (এবং সম্ভবত কানের দুল) এএএএফসিটিতে সিলেটমেন্ট ব্যবহার করা এড়ানো আর সম্ভব নয়, সুতরাং এই
কাজটি

1
সিলেমেট্রি অবমূল্যায়ন করা হয়েছে তবে সি এক্সিলিটরগুলির সাথে ধরণের ছায়াছবি রয়েছে । সি কোড এক্সপেটে কল করছে না তাই হ্যাঁ এই সমাধানটি নষ্ট হয়ে গেছে।
এরিকস্পড

বার্নি এটি এখনও সম্ভব, ElementTree.fromstring(s, parser=None)আমি এটিতে পার্সার দেওয়ার চেষ্টা করছি।
এ 12

2

আমি এর জন্য দেরি করতে পারি তবে আমি মনে করি না re.subএটি একটি ভাল সমাধান।

তবে xml.parsers.expatপাইথন ৩.x সংস্করণগুলির জন্য পুনর্লিখনটি কাজ করে না,

মূল অপরাধী হ'ল xml/etree/ElementTree.pyসোর্স কোডের নীচে দেখুন

# Import the C accelerators
try:
    # Element is going to be shadowed by the C implementation. We need to keep
    # the Python version of it accessible for some "creative" by external code
    # (see tests)
    _Element_Py = Element

    # Element, SubElement, ParseError, TreeBuilder, XMLParser
    from _elementtree import *
except ImportError:
    pass

যা দুঃখজনক।

সমাধানটি প্রথমে এটি থেকে মুক্তি পাওয়া।

import _elementtree
try:
    del _elementtree.XMLParser
except AttributeError:
    # in case deleted twice
    pass
else:
    from xml.parsers import expat  # NOQA: F811
    oldcreate = expat.ParserCreate
    expat.ParserCreate = lambda encoding, sep: oldcreate(encoding, None)

পাইথন ৩.6-তে পরীক্ষা করা হয়েছে।

tryআপনার কোডের কোথাও আপনি পুনরায় লোড বা কোনও মডিউল আমদানি করার মতো ক্ষেত্রে কিছু অদ্ভুত ত্রুটি পেয়ে যাবার ক্ষেত্রে ট্রাই স্টেটমেন্টটি কার্যকর

  • সর্বাধিক পুনরাবৃত্তির গভীরতা অতিক্রম করেছে
  • অ্যাট্রিবিউটআরার: এক্সএমএল পার্সার

বিটিডব্লিউ এট্রি সোর্স কোডটি সত্যিই অগোছালো দেখাচ্ছে।


1

এর মেশা করা যাক নবভুজ জ্যামিতিক ক্ষেত্র এর উত্তর দিয়ে একটি সম্পর্কিত প্রশ্নের mzjn এর উত্তর :

def parse_xml(xml_path: Path) -> Tuple[ET.Element, Dict[str, str]]:
    xml_iter = ET.iterparse(xml_path, events=["start-ns"])
    xml_namespaces = dict(prefix_namespace_pair for _, prefix_namespace_pair in xml_iter)
    return xml_iter.root, xml_namespaces

এই ফাংশনটি ব্যবহার করে আমরা:

  1. নেমস্পেস এবং পার্স করা গাছের বস্তু উভয়ই পেতে একটি পুনরুক্তি তৈরি করুন ।

  2. নেমস্পেসস ডিক পেতে তৈরি করা পুনরুদ্ধারের উপরে আইট্রেট করুন যা আমরা পরে প্রত্যেকটিতে পাস করতে পারি find()বা iMom0 দ্বারা sugested হিসাবেfindall() কল করতে পারি

  3. পার্স করা গাছের মূল উপাদান উপাদান এবং নেমস্পেসগুলি ফিরিয়ে দিন।

আমি মনে করি এটি কোনও উত্স এক্সএমএল বা ফলস্বরূপ xml.etree.ElementTreeআউটপুট ফলে জড়িত কোনও ফলস্বরূপ কোনও হেরফের নেই কারণ এটি চারপাশের সেরা পদ্ধতির ।

আমি এই ধাঁধার একটি প্রয়োজনীয় টুকরো সরবরাহের সাথে বার্নির উত্তরও জমা দিতে চাই (যে আপনি পুনরাবৃত্তকারী থেকে পার্সড রুট পেতে পারেন)। ততক্ষণ পর্যন্ত আমি আমার অ্যাপ্লিকেশনটিতে দুবার এক্সএমএল গাছটি অতিক্রম করেছি (একবার নেমস্পেস পেতে, একটি মূলের জন্য দ্বিতীয়)।


এটি কীভাবে ব্যবহার করতে হয় তা খুঁজে পেয়েছি তবে এটি আমার পক্ষে কাজ করে না, আমি এখনও আউটপুটটিতে নামের স্থানগুলি দেখতে পাই
তাইকো

1
ওপি এর প্রশ্নের iMom0 এর মন্তব্য । এই ফাংশন ব্যবহার করে আপনি উভয় পার্স বস্তু এবং উপায় সঙ্গে এটি অনুসন্ধান পেতে find()এবং findall()। আপনি কেবল সেই পদ্ধতিগুলিকে নেমস্পেসের ডিকের সাহায্যে খাওয়াবেন এবং আপনার প্রশ্নের মধ্যে নেমস্পেসের উপসর্গটিparse_xml() ব্যবহার করুন। যেমন:et_element.findall(".//some_ns_prefix:some_xml_tag", namespaces=xml_namespaces)
z33k
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.