'এলিমেট্রি'র মাধ্যমে পাইথনের নেমস্পেসের সাথে এক্সএমএল পার্স করা হচ্ছে


163

পাইথনের ব্যবহার করে আমি নীচের এক্সএমএলটি বিশ্লেষণ করতে চাই ElementTree:

<rdf:RDF xml:base="http://dbpedia.org/ontology/"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:owl="http://www.w3.org/2002/07/owl#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
    xmlns="http://dbpedia.org/ontology/">

    <owl:Class rdf:about="http://dbpedia.org/ontology/BasketballLeague">
        <rdfs:label xml:lang="en">basketball league</rdfs:label>
        <rdfs:comment xml:lang="en">
          a group of sports teams that compete against each other
          in Basketball
        </rdfs:comment>
    </owl:Class>

</rdf:RDF>

আমি সমস্ত owl:Classট্যাগ খুঁজে পেতে চাই এবং তারপরে সেগুলির মধ্যে সমস্ত rdfs:labelদৃষ্টান্তের মান বের করতে চাই। আমি নিম্নলিখিত কোড ব্যবহার করছি:

tree = ET.parse("filename")
root = tree.getroot()
root.findall('owl:Class')

নেমস্পেসের কারণে আমি নিম্নলিখিত ত্রুটিটি পাচ্ছি।

SyntaxError: prefix 'owl' not found in prefix map

আমি http://effbot.org/zone/element-namespaces.htm এ দস্তাবেজটি পড়ার চেষ্টা করেছি তবে উপরের এক্সএমএলটির একাধিক নেস্টেড নেমস্পেস রয়েছে বলে আমি এখনও এই কাজটি করতে সক্ষম নই।

দয়া করে আমাকে সমস্ত owl:Classট্যাগ সন্ধানের জন্য কোডটি কীভাবে পরিবর্তন করবেন তা জানান ।

উত্তর:


226

এলিমেন্ট্রি নেমস্পেসগুলি সম্পর্কে খুব স্মার্ট নয়। আপনি দিতে হবে .find(), findall()এবং iterfind()পদ্ধতি একটি সুনির্দিষ্ট নামস্থান অভিধান। এটি খুব ভাল নথিভুক্ত করা হয় না:

namespaces = {'owl': 'http://www.w3.org/2002/07/owl#'} # add more as needed

root.findall('owl:Class', namespaces)

আপনি যে প্যারামিটারে প্রবেশ করেন কেবল সেগুলি কেবল পূর্বরূপগুলিতেই দেখা হয় namespacesThis এর অর্থ আপনি আপনার পছন্দ মতো কোনও নেমস্পেস উপসর্গ ব্যবহার করতে পারেন; এপিআই owl:অংশটি বিভক্ত করে , namespacesঅভিধানে সংশ্লিষ্ট নেমস্পেস ইউআরএল সন্ধান করে , তারপরে {http://www.w3.org/2002/07/owl}Classপরিবর্তে এক্সপথ এক্সপ্রেশনটি অনুসন্ধানের জন্য অনুসন্ধানটি পরিবর্তন করে । আপনি নিজেও একই সিনট্যাক্সটি অবশ্যই ব্যবহার করতে পারেন:

root.findall('{http://www.w3.org/2002/07/owl#}Class')

আপনি যদি lxmlলাইব্রেরিতে স্যুইচ করতে পারেন তবে জিনিসগুলি আরও ভাল; সেই লাইব্রেরি একই এলিমেন্টটি ট্রি এপিআই সমর্থন করে তবে .nsmapউপাদানগুলির একটি বৈশিষ্ট্যে আপনার জন্য নেমস্পেসগুলি সংগ্রহ করে।


7
ধন্যবাদ. কোনও ধারণা কীভাবে আমি এক্সএমএল থেকে হার্ড-কোডিং ছাড়াই সরাসরি নামস্থানটি পেতে পারি? বা আমি কীভাবে এড়িয়ে যাব? আমি ফাইন্ডল ('{*} ক্লাস') চেষ্টা করেছি কিন্তু এটি আমার ক্ষেত্রে কাজ করবে না।
Kostanos

7
আপনার xmlnsনিজের বৈশিষ্ট্যের জন্য আপনাকে গাছটি স্ক্যান করতে হবে ; উত্তরে বর্ণিত হিসাবে lxml, এটি আপনার জন্য করে, xml.etree.ElementTreeমডিউলটি দেয় না। তবে আপনি যদি একটি নির্দিষ্ট (ইতিমধ্যে হার্ডকোডযুক্ত) উপাদানটির সাথে মেলে চেষ্টা করছেন, তবে আপনি একটি নির্দিষ্ট নেমস্পেসেও একটি নির্দিষ্ট উপাদানটির সাথে মেলানোর চেষ্টা করছেন। উপাদানটির নামের চেয়ে আর নামের দস্তাবেজগুলির মধ্যে আর পরিবর্তন হবে না। আপনি সেই উপাদানটির নাম সহ হার্ডকোডও করতে পারেন।
মার্টিজন পিটারস

14
@ জন: register_namespaceকেবল সিরিয়ালাইজেশন প্রভাবিত করে, অনুসন্ধান নয় search
মার্টিজান পিটারস

5
ছোট উপরন্তু যে দরকারী হতে পারে: যখন ব্যবহার cElementTreeপরিবর্তে ElementTree, findallএকটি শব্দ আর্গুমেন্ট হিসাবে নামব্যবধান না নিতে, কিন্তু বরং কেবল একটি স্বাভাবিক যুক্তি, অর্থাত ব্যবহার ctree.findall('owl:Class', namespaces)
egpbos

2
@ ব্লুওয়ার্ফ: দস্তাবেজগুলি এটি উল্লেখ করেছে (এখন, আপনি যখন এটি লিখেছিলেন তা না হলে) তবে আপনাকে সেগুলি খুব সাবধানে পড়তে হবে। নেমস্পেস বিভাগের সাথে পার্সিং এক্সএমএল দেখুন : একটি উদাহরণ রয়েছে যার ব্যবহার findallছাড়াই এবং তারপরে namespaceআর্গুমেন্টের সাথে বিপরীত , কিন্তু যুক্তিটি এলিমেন্ট অবজেক্ট বিভাগে পদ্ধতি পদ্ধতির আর্গুমেন্টগুলির মধ্যে একটি হিসাবে উল্লেখ করা হয়নি ।
উইলসন এফ

57

নেমস্পেসগুলি হার্ড-কোড না করে বা তাদের জন্য পাঠ্যটি স্ক্যান না করে কীভাবে এলএক্সএমএল দিয়ে এটি করবেন তা এখানে রয়েছে (মার্টিজান পিটারস উল্লেখ হিসাবে):

from lxml import etree
tree = etree.parse("filename")
root = tree.getroot()
root.findall('owl:Class', root.nsmap)

আপডেট :

5 বছর পরে আমি এখনও এই সমস্যাটির বিভিন্নতায় চলেছি। lxML আমি উপরে প্রদর্শিত হিসাবে সাহায্য করে, কিন্তু প্রতিটি ক্ষেত্রে না। ডকুমেন্টগুলি মার্জ করার সময় কমেন্টারদের এই কৌশলটি সম্পর্কে একটি বৈধ পয়েন্ট থাকতে পারে তবে আমি মনে করি বেশিরভাগ লোককে কেবল নথিপত্র অনুসন্ধান করতে সমস্যা হচ্ছে।

এখানে অন্য একটি কেস এবং আমি কীভাবে এটি পরিচালনা করেছি:

<?xml version="1.0" ?><Tag1 xmlns="http://www.mynamespace.com/prefix">
<Tag2>content</Tag2></Tag1>

প্রিফিক্স ব্যতীত xMLns এর অর্থ হল যে প্রাক-পূর্বনির্ধারিত ট্যাগগুলি এই ডিফল্ট নেমস্পেসটি পায়। এর অর্থ আপনি যখন ট্যাগ 2 অনুসন্ধান করবেন তখন এটি সন্ধানের জন্য আপনাকে নামের স্থানটি অন্তর্ভুক্ত করতে হবে। যাইহোক, lxML কী হিসাবে কোনওটিই নয় এমন এনএসএম্যাপ এন্ট্রি তৈরি করে এবং আমি এটির সন্ধানের কোনও উপায় খুঁজে পাই না। সুতরাং, আমি এর মতো একটি নতুন নেমস্পেস অভিধান তৈরি করেছি

namespaces = {}
# response uses a default namespace, and tags don't mention it
# create a new ns map using an identifier of our choice
for k,v in root.nsmap.iteritems():
    if not k:
        namespaces['myprefix'] = v
e = root.find('myprefix:Tag2', namespaces)

3
সম্পূর্ণ নেমস্পেস ইউআরএল হ'ল নেমস্পেস শনাক্তকারী যা আপনার হার্ড-কোড করার কথা। স্থানীয় উপসর্গ ( owl) ফাইল থেকে ফাইলে পরিবর্তন করতে পারে। সুতরাং এই উত্তরটি যা বোঝায় তা করা সত্যিই খারাপ ধারণা।
ম্যাটি ভির্ককুনেন

1
@ মাট্টিভির্ককুনেন ঠিক যদি পেঁচার সংজ্ঞা ফাইল থেকে ফাইলের মধ্যে পরিবর্তন করতে পারে তবে আমরা কি প্রতিটি ফাইলের হার্ডকোডিংয়ের পরিবর্তে সংজ্ঞায়িত সংজ্ঞাটি ব্যবহার করব না?
Loïc Faure-Lacroix

@ LoïcFaure-Lacroix: সাধারণত এক্সএমএল লাইব্রেরি আপনাকে সেই অংশটি বিমূর্ত করতে দেয়। আপনার নিজেরাই ফাইলটিতে ব্যবহৃত উপসর্গ সম্পর্কে জানা বা যত্ন নেওয়ার দরকার নেই, আপনি কেবল আপনার নিজের উপসর্গটি পার্সিংয়ের উদ্দেশ্যে সংজ্ঞায়িত করেন বা পুরো নেমস্পেসের নামটি ব্যবহার করেন।
ম্যাটি ভির্ককুনেন

এই উত্তরটি আমার কমপক্ষে অনুসন্ধানের ফাংশনটি ব্যবহার করতে সক্ষম হতে সহায়তা করেছে। আপনার নিজের উপসর্গ তৈরি করার দরকার নেই। আমি সবেমাত্র কী = তালিকা (root.nsmap.keys ()) [0] করেছি এবং তারপরে কীটি উপসর্গ হিসাবে যুক্ত করেছি: root.find (f '{key}: Tag2', root.nsmap)
Eelco van Vliet

30

দ্রষ্টব্য : পাইথনের এলিমেট্রি স্ট্যান্ডার্ড লাইব্রেরির জন্য হার্ডকডযুক্ত নেমস্পেসগুলি ব্যবহার না করেই এটি উত্তর।

এক্সএমএল ডেটা থেকে নেমস্পেসের উপসর্গ এবং ইউআরআই বের করতে আপনি ElementTree.iterparseফাংশনটি ব্যবহার করতে পারেন , কেবলমাত্র নেমস্পেসের শুরু ইভেন্টগুলি ( স্টার্ট-এনএস ) পার্স করে :

>>> from io import StringIO
>>> from xml.etree import ElementTree
>>> my_schema = u'''<rdf:RDF xml:base="http://dbpedia.org/ontology/"
...     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
...     xmlns:owl="http://www.w3.org/2002/07/owl#"
...     xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
...     xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
...     xmlns="http://dbpedia.org/ontology/">
... 
...     <owl:Class rdf:about="http://dbpedia.org/ontology/BasketballLeague">
...         <rdfs:label xml:lang="en">basketball league</rdfs:label>
...         <rdfs:comment xml:lang="en">
...           a group of sports teams that compete against each other
...           in Basketball
...         </rdfs:comment>
...     </owl:Class>
... 
... </rdf:RDF>'''
>>> my_namespaces = dict([
...     node for _, node in ElementTree.iterparse(
...         StringIO(my_schema), events=['start-ns']
...     )
... ])
>>> from pprint import pprint
>>> pprint(my_namespaces)
{'': 'http://dbpedia.org/ontology/',
 'owl': 'http://www.w3.org/2002/07/owl#',
 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#',
 'xsd': 'http://www.w3.org/2001/XMLSchema#'}

তারপরে অভিধানটি অনুসন্ধান কার্যকারণের পক্ষে যুক্তি হিসাবে পাস করা যেতে পারে:

root.findall('owl:Class', my_namespaces)

1
এটি lxML অ্যাক্সেস ছাড়াই এবং হার্ডকোড নেমস্পেস না করে আমাদের জন্য এটি দরকারী।
ডেল্রাকো

1
আমি ত্রুটি পেয়েছি: ValueError: write to closedএই লাইনের জন্য filemy_namespaces = dict([node for _, node in ET.iterparse(StringIO(my_schema), events=['start-ns'])])। কোন ধারণা ভুল চায়?
ইউলি

সম্ভবত ত্রুটিটি ক্লাস io.StringIO এর সাথে সম্পর্কিত, যা ASCII স্ট্রিংগুলিকে অস্বীকার করে। আমি পাইথন 3 দিয়ে আমার রেসিপিটি পরীক্ষা করেছিলাম। নমুনা স্ট্রিংটিতে ইউনিকোড স্ট্রিং উপসর্গ 'u' যুক্ত করা এটি পাইথন 2 (2.7) এর সাথেও কাজ করে।
ডেভিড ব্রুনাটো

পরিবর্তে dict([...])আপনি ডিক বোধগম্যতা ব্যবহার করতে পারেন।
আর্মিনিয়াস

পরিবর্তে StringIO(my_schema)আপনি এক্সএমএল ফাইলের ফাইলের নামও রাখতে পারেন।
JustAC0der

6

আমি এটির সাথে একই জাতীয় কোড ব্যবহার করছি এবং এটি ডকুমেন্টেশন পড়ার পক্ষে যথারীতি খুঁজে পেয়েছি ... যথারীতি!

Findall () কেবলমাত্র সেই উপাদানগুলি সন্ধান করবে যা বর্তমান ট্যাগের প্রত্যক্ষ শিশু । সুতরাং, সত্যিই সব না।

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

root.iter()

রেফ: https://docs.python.org/3/library/xML.etree.elementtree.html#finding-interesting-eitions "এলিমেন্ট.ফিন্ডাল () কেবলমাত্র একটি ট্যাগযুক্ত উপাদানগুলি সন্ধান করে যা বর্তমান উপাদানটির প্রত্যক্ষ শিশু। এলিমেন্ট.ফাইন্ড () নির্দিষ্ট ট্যাগ সহ প্রথম শিশুকে খুঁজে পায় এবং এলিমেন্ট.টেক্সট উপাদানটির পাঠ্য সামগ্রীটি অ্যাক্সেস করে E


6

নেমস্পেসটি এর নেমস্পেস বিন্যাসে পেতে, উদাহরণস্বরূপ {myNameSpace}, আপনি নিম্নলিখিতটি করতে পারেন:

root = tree.getroot()
ns = re.match(r'{.*}', root.tag).group(0)

এভাবে আপনি নোডগুলি খুঁজতে আপনার কোডটিতে পরে এটি ব্যবহার করতে পারেন, যেমন স্ট্রিং ইন্টারপোলেশন (পাইথন 3) ব্যবহার করে।

link = root.find(f"{ns}link")

0

আমার সমাধান @ মার্তিজন পিটারসের মন্তব্যের ভিত্তিতে:

register_namespace কেবল সিরিয়ালাইজেশন প্রভাবিত করে, অনুসন্ধান নয়।

সুতরাং এখানে কৌশলটি ক্রমিককরণ এবং অনুসন্ধানের জন্য বিভিন্ন অভিধান ব্যবহার করা different

namespaces = {
    '': 'http://www.example.com/default-schema',
    'spec': 'http://www.example.com/specialized-schema',
}

এখন, পার্সিং এবং লেখার জন্য সমস্ত নেমস্পেস নিবন্ধন করুন:

for name, value in namespaces.iteritems():
    ET.register_namespace(name, value)

অনুসন্ধানের জন্য ( find(), findall(), iterfind()) আমরা একটি খালি প্রেফিক্স প্রয়োজন। এই ফাংশনগুলিকে একটি পরিবর্তিত অভিধানটি পাস করুন (এখানে আমি মূল অভিধানটি সংশোধন করি, তবে এটি অবশ্যই নেমস্পেসগুলি নিবন্ধিত হওয়ার পরে তৈরি করা উচিত)।

self.namespaces['default'] = self.namespaces['']

এখন, find()পরিবারের ফাংশনগুলি defaultউপসর্গের সাথে ব্যবহার করা যেতে পারে :

print root.find('default:myelem', namespaces)

কিন্তু

tree.write(destination)

ডিফল্ট নেমস্পেসের উপাদানগুলির জন্য কোনও উপসর্গ ব্যবহার করে না।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.