2016-04-08 55 views
0

我想將多個類似格式的XML文件解析爲CSV文件。如何使用Nokogiri將多個類似格式的XML文件合併爲CSV

我在谷歌搜索,nokogiri.org和搜索,但我一直沒能找到答案。

根據節點/元素結構,我有十個XML文件,它們位於當前目錄中。

將XML文件合併到單個XML文件後,我需要提取advisory節點的特定元素。我想將link,title,location,os -> language -> namereference -> name數據輸出到CSV文件。

我的代碼是唯一能夠解析一個XML文檔,我想它要考慮到1:許多:

# Parse the XML file into a Nokogiri::XML::Document object 
@doc = Nokogiri::XML(File.open("file.xml")) 

# Gather the 5 specific XML elements out of the 'advisory' top-level node 
data = @doc.search('advisory').map { |adv| 
    [ 
    adv.at('link').content, 
    adv.at('title').content, 
    adv.at('location').content, 
    adv.at('os > language > name').content, 
    adv.at('reference > name').content 
    ] 
} 

# Loop through each array element in the object and write out as CSV row 
CSV.open('output_file.csv', 'wb') do |csv| 
    # Explicitly set headers until you figure out how to get them programatically 
    csv << ['Link', 'Title', 'Location', 'OS Name', 'Reference Name'] 
    data.each do |row| 
    csv << row 
    end 
end 

我試圖改變的代碼來支持多個XML文件,並讓他們進入引入nokogiri :: XML :: Document對象:

xml_docs = [] 

Dir.glob("*.xml").each do |file| 
    xml = Nokogiri::XML(File.new(file)) 
    xml_docs << Nokogiri::XML::Document.new(xml) 
end 

這成功地創建了正確的對象的數組xml_docs它,但我不知道怎麼這六個對象轉換爲一個單一的對象。

這是示例XML。所有的XML文件使用相同的節點/元結構:

<advisories> 
    <title> Not relevant </title> 
    <customer> N/A </customer> 
    <advisory id="12345"> 
    <link> https://www.google.com </link> 
    <release_date>2016-04-07</release_date> 
    <title> The Short Description Would Go Here </title> 
    <location> Location Name Here </location> 
    <os> 
     <product> 
     <id>98765</id> 
     <name>Product Name</name> 
     </product> 
     <language> 
     <id>123</id> 
     <name>en</name> 
     </language> 
    </os> 
    <reference> 
     <id>00029</id> 
     <name>Full</name> 
     <area>Not Defined</area> 
    </reference> 
    </advisory> 
    <advisory id="98765"> 
    <link> https://www.msn.com </link> 
    <release_date>2016-04-08</release_date> 
    <title> The Short Description Would Go Here </title> 
    <location> Location Name Here </location> 
    <os> 
     <product> 
     <id>12654</id> 
     <name>Product Name</name> 
     </product> 
     <language> 
     <id>126</id> 
     <name>fr</name> 
     </language> 
    </os> 
    <reference> 
     <id>00052</id> 
     <name>Partial</name> 
     <area>Defined</area> 
    </reference> 
    </advisory> 
</advisories> 

代碼利用引入nokogiri :: XML ::文檔,但如果引入nokogiri :: XML :: Builder將努力爲這個更好的,我更願意調整更多我的代碼相應。

+0

歡迎來到Stack Overflow。雖然你很高興轉身到這裏,不幸的是你錯過了SO的這一點;我們幫助*您*修正*您的*代碼中的錯誤/問題。請閱讀「[問]」,包括底部的鏈接和「[mcve]」。我們希望看到你的努力的證據:你嘗試了什麼?爲什麼它不工作?如果你沒有嘗試,你在哪裏搜索,爲什麼這些地方沒有你需要的信息?把XML傳給我們,並告訴我們你想做什麼,希望我們編寫代碼來解決問題,而不是問我們如何解決你在編寫時遇到的問題。 –

+0

我會盡快修改我的問題,我不想讓這個問題時間過長,但我會看看我能做些什麼。謝謝你的提示! –

+0

不客氣。 SO有一個雄心勃勃的目標,即成爲編程問題的在線參考,這是一本問題和解決方案的食譜。他們在這方面做得很好,正如搜索引擎的頂級結果所證明的那樣,但是確保問題和答案的質量仍然很高是一項持續的工作,這就是爲什麼我們需要我們所做的事情。長期的問題並不意味着高質量,所以他們很難寫作,並採取預先考慮和努力,但最終的結果是偉大的;你將會得到答案,其他人也會得到答案。歡迎來到解決大家問題的戰鬥! :-) –

回答

0

我會處理的第一部分,解析一個XML文件,像這樣的:

require 'nokogiri' 

doc = Nokogiri::XML(<<EOT) 
<advisories> 
    <advisory id="12345"> 
    <link> https://www.google.com </link> 
    <title> The Short Description Would Go Here </title> 
    <location> Location Name Here </location> 
    <os> 
     <language> 
     <name>en</name> 
     </language> 
    </os> 
    <reference> 
     <name>Full</name> 
    </reference> 
    </advisory> 
    <advisory id="98765"> 
    <link> https://www.msn.com </link> 
    <release_date>2016-04-08</release_date> 
    <title> The Short Description Would Go Here </title> 
    <location> Location Name Here </location> 
    <os> 
     <language> 
     <name>fr</name> 
     </language> 
    </os> 
    <reference> 
     <name>Partial</name> 
    </reference> 
    </advisory> 
</advisories> 
EOT 

注:這有刪除,因爲他們沒有到問題的重要節點。詢問時請刪除絨毛,因爲它會讓人分心。

有了這個作爲代碼的核心部分:

doc.search('advisory').map{ |advisory| 
    link = advisory.at('link').text 
    title = advisory.at('title').text 
    location = advisory.at('location').text 
    os_language_name = advisory.at('os > language > name').text 
    reference_name = advisory.at('reference > name').text 

    { 
    link: link, 
    title: title, 
    location: location, 
    os_language_name: os_language_name, 
    reference_name: reference_name 
    } 
} 

,可能是DRY'd卻被寫成什麼就做什麼的例子。

運行的結果在哈希的數組,這將是很容易地通過CSV輸出:

# => [ 
     {:link=>" https://www.google.com ", :title=>" The Short Description Would Go Here ", :location=>" Location Name Here ", :os_language_name=>"en", :reference_name=>"Full"}, 
     {:link=>" https://www.msn.com ", :title=>" The Short Description Would Go Here ", :location=>" Location Name Here ", :os_language_name=>"fr", :reference_name=>"Partial"} 
    ] 

一旦你得到了工作,然後將它列入你的循環的修改版本輸出CSV和閱讀XML文件。這是未經測試,但看起來八九不離十:您正在使用的'wb'文件模式

CSV.open('output_file.csv', 'w', 
    headers: ['Link', 'Title', 'Location', 'OS Name', 'Reference Name'], 
    write_headers: true 
) do |csv| 
    Dir.glob("*.xml").each do |file| 
    xml = Nokogiri::XML(File.read(file)) 
    # parse a file and get the array of hashes 
    end 

    # pass the array of hashes to CSV for output 
end 

注意。由於CSV應該是文本格式,因此您很少需要使用CSV作爲b。如果你是當然你會遇到二進制數據,然後使用'b'也可以導致包含龍的路徑。

另請注意,這是使用readread是不可擴展的,這意味着它不關心文件有多大,它會嘗試將它讀入內存,不管它是否真的適合。有很多原因可以避免,但最好的是它會讓你的程序屈服。如果您的XML文件可能超過系統的可用內存,那麼您將需要使用Nokogiri支持的SAX解析器進行重寫。如何做到這一點是一個不同的問題。


它實際上是散列陣列的陣列。我不知道我是如何結束了那裏,但我很容易就在這個array.flatten

冥想使用:

foo = [] # => [] 
foo << [{}] # => [[{}]] 
foo.flatten # => [{}] 

你可能想這樣做:

foo = [] # => [] 
foo += [{}] # => [{}] 

任何時候我必須使用flatten我期待看看我是否可以創建數組而不是一個數組的數組。這並不是說它們本質上是不好的,因爲它們有時非常有用,但是你真的想要一些散列,所以你知道某些東西是錯誤的,flatten是一個便宜的出路,但使用它也會花費更多的CPU時間。最好找出問題並加以解決,最終得到更快/更高效的代碼。 (有些人會說這是浪費精力或是過早優化,但編寫高效代碼是一個非常好的特質和目標。)

+0

「一條含有龍的路徑」......大聲笑。我會盡快嘗試。感謝您的深入解釋! –

+0

謝謝,這個作品很好。我爲每個'xml.search'分配了一個變量,並將它們寫入了一個新的Array。一旦我擁有6個對象的數組,它實際上是一個哈希數組的數組。我不知道我是如何在那裏結束的,但是我很容易使用'array.flatten'並迭代將散列值分配給CSV的散列。我應該發佈完整的答案嗎?順便說一句,我以前見過你的名字很多次 - 我很高興我有機會感謝你。你幫助過我和很多其他人 - 謝謝你! –

+0

如果你有一個散列數組數組,你可能沒有正確連接。我會添加一個解釋。除非找到修復,否則不要添加完整的答案;張貼您的代碼並不能幫助其他人。我的目標是回報。我已經做了很長時間了,並且有很多優秀的導師。我喜歡教導,指導和支付。 –

相關問題