كيف يمكنني الحصول على Nokogiri لتحليل وإرجاع مستند XML؟
-
18-09-2019 - |
سؤال
إليك عينة من بعض الغرابة:
#!/usr/bin/ruby
require 'rubygems'
require 'open-uri'
require 'nokogiri'
print "without read: ", Nokogiri(open('http://weblog.rubyonrails.org/')).class, "\n"
print "with read: ", Nokogiri(open('http://weblog.rubyonrails.org/').read).class, "\n"
تشغيل هذه العائدات:
without read: Nokogiri::XML::Document
with read: Nokogiri::HTML::Document
بدون ال read
إرجاع XML، ومعها HTML؟ يتم تعريف صفحة الويب على أنها "XHTML الانتقالية"، لذلك في البداية اعتقدت أن Nokogiri يجب أن يقرأ "نوع المحتوى" OpenUri من الدفق، ولكن هذا يعود 'text/html'
:
(rdb:1) doc = open(('http://weblog.rubyonrails.org/'))
(rdb:1) doc.content_type
"text/html"
وهو ما يعود الخادم. لذلك، الآن أحاول معرفة سبب إرجاع Nokogiri قيمتين مختلفتين. لا يبدو أنه يقوم بتحليل النص واستخدام الاسلكية لتحديد ما إذا كان المحتوى هو HTML أو XML.
يحدث نفس الشيء مع موجز الذرة المدببة لهذه الصفحة:
(rdb:1) doc = Nokogiri.parse(open('http://feeds.feedburner.com/RidingRails'))
(rdb:1) doc.class
Nokogiri::XML::Document
(rdb:1) doc = Nokogiri.parse(open('http://feeds.feedburner.com/RidingRails').read)
(rdb:1) doc.class
Nokogiri::HTML::Document
أحتاج إلى أن أكون قادرا على تحليل صفحة دون معرفة ما هو مقدما، إما HTML أو موجز (RSS أو ATOM) وتحديد موثوق ذلك. طلبت من nokogiri لتحليل الجسم إما ملف تغذية HTML أو XML، لكنني أرى تلك النتائج غير المتسقة.
اعتقدت أنني أستطيع أن أكتب بعض الاختبارات لتحديد النوع ولكن ثم ركضت إلى xpath لا يجد عناصر، ولكن عمليات البحث العادية تعمل:
(rdb:1) doc = Nokogiri.parse(open('http://feeds.feedburner.com/RidingRails'))
(rdb:1) doc.class
Nokogiri::XML::Document
(rdb:1) doc.xpath('/feed/entry').length
0
(rdb:1) doc.search('feed entry').length
15
أنا أحسب xpaths ستعمل مع XML ولكن النتائج لا تبدو جديرة بالثقة أيضا.
تم إجراء جميع الاختبارات هذه على صندوق Ubuntu الخاص بي، لكنني رأيت نفس السلوك في MacBook Pro. أحب أن أعرف أنني أفعل شيئا خاطئا، لكنني لم أر مثالا على تحليل والبحث الذي أعطاني نتائج متسقة. هل يمكن لأي شخص أن يظهر لي خطأ طرقي؟
المحلول
يجب أن تفعل مع طريقة Nokogiri طريقة تحليل يعمل. إليك المصدر:
# File lib/nokogiri.rb, line 55
def parse string, url = nil, encoding = nil, options = nil
doc =
if string =~ /^\s*<[^Hh>]*html/i # Probably html
Nokogiri::HTML::Document.parse(string, url, encoding, options || XML::ParseOptions::DEFAULT_HTML)
else
Nokogiri::XML::Document.parse(string, url, encoding, options || XML::ParseOptions::DEFAULT_XML)
end
yield doc if block_given?
doc
end
المفتاح هو الخط if string =~ /^\s*<[^Hh>]*html/i # Probably html
. وبعد عندما تستخدم فقط open
, ، تقوم بإرجاع كائن لا يعمل مع Regex، وبالتالي فإنه يرجع دائما خطأ. من ناحية أخرى، read
إرجاع سلسلة، لذلك استطاع تعتبر HTML. في هذه الحالة، لأنه يطابق هذا Regex. ها هي بداية تلك السلسلة:
<!DOCTYPE html PUBLIC
يطابق Regex من "! doctype" [^Hh>]*
ثم يطابق "HTML"، وبالتالي افتراض أنه HTML. لماذا اختار شخص ما هذا Regex لتحديد ما إذا كان الملف هو HTML يتجاوزني. مع هذا Regex، ملف يبدأ علامة مثل <definitely-not-html>
يعتبر HTML، ولكن <this-is-still-not-html>
يعتبر XML. ربما تكون أفضل من البقاء بعيدا عن هذه الوظيفة البكمية والإحتسار Nokogiri::HTML::Document#parse
أو Nokogiri::XML::Document#parse
مباشرة.
نصائح أخرى
الاستجابة لهذا الجزء من سؤالك:
اعتقدت أنني أستطيع أن أكتب بعض الاختبارات لتحديد النوع ولكن ثم ركضت إلى xpath لا يجد عناصر، ولكن عمليات البحث العادية تعمل:
لقد صادفت هذه المشكلة باستخدام Nokogiri لتحليل علف الذرة. بدا المشكلة إلى إعلان المساحة المجهولية:
<feed xmlns="http://www.w3.org/2005/Atom">
إزالة إعلان XMLNS من مصدر XML سيمكن Nokogiri للبحث مع XPath حسب المعتاد. من الواضح أن إزالة هذا التصريح من الأعلاف لم يكن خيارا هنا، لذلك بدلا من ذلك، أزلت فقط أسماء الأسماء من المستند بعد التحليل. على سبيل المثال:
doc = Nokogiri.parse(open('http://feeds.feedburner.com/RidingRails'))
doc.remove_namespaces!
doc.xpath('/feed/entry').length
قبيح أعرف، لكنه فعلت الخدعة.