iT邦幫忙

DAY 20
4

現代環境下的 Linux 裡的新工具系列 第 17

從命令模式解析XML的工具:xmlstarlet

看 chou 大的 讀取文化部資料開放服務網資料 (1) 蠻有趣,也想試試在 Linux 下的指令模式,如何把該 XML 裡的值抓出。

觀察XML之格式
先下載該XML檔案

$ wget http://cloud.culture.tw/frontsite/trans/emapOpenDataAction.do?method=exportEmapXML\&typeId=F -O o.xml
--2013-10-05 07:10:46--  http://cloud.culture.tw/frontsite/trans/emapOpenDataAction.do?method=exportEmapXML&typeId=F
Resolving cloud.culture.tw (cloud.culture.tw)... 210.61.8.85
Connecting to cloud.culture.tw (cloud.culture.tw)|210.61.8.85|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/xml]
Saving to: ‘o.xml’

    [    <=>                                                    ] 1,933,955   3.03MB/s   in 0.6s

2013-10-05 07:10:47 (3.03 MB/s) - ‘o.xml’ saved [1933955]

該檔案輸出排列的不易觀察,利用以下指令來簡單地排列,好觀察要取哪些的值:

xmllint --format o.xml | less
<?xml version="1.0" encoding="UTF-8"?>
<XML_Head version="1.0" status="success" total="2247">
  <emapItem>
    <emap>
      <Info groupTypeName="公共藝術" mainTypeName="公共藝術" mainTypePk="2828" name="聚沙成塔 Accumulating much from a little" representImage="http://cloud.culture.tw/e_upload_ccacloud/ccacloud/image/A0/B0/C-1/D-267/E-728/F-534/1364531871547.jpg" intro="此件作品設置於單位之大門廣場上,以具有代表錢幣之貝殼造型意像,運用鋼材層疊的創作手法,表現出「積少成多,聚沙成塔」之財稅集中的精神意涵,而其旋轉上身之造刑動勢,更營造出單位的朝氣與活力,讓往來之民眾更能體現場域之精神。" areaCode="407" cityName="
臺中市  西屯區" address="稅捐稽徵處" longitude="120.647237" latitude="24.1589" buildingYearName="P130-407-05" author="林舜龍、楊仁明、張明風 lin Shuen-Long, Yang Jen-Ming, Chang Ming-Feng" size="435*315*600cm" material="熱浸鍍鋅鋼板"/>
...
    </emap>
  </emapItem>
</XML_Head>

此 xmllint 的指令是包在 libxml2 的套件裡。
但後來發現 tidy 的指令排得更利於觀察些:

$ tidy -xml -utf8 -i -q o.xml |less
<?xml version="1.0" encoding="utf-8"?>
<XML_Head version="1.0" status="success" total="2247">
  <emapItem>
    <emap>
      <Info groupTypeName="公共藝術" mainTypeName="公共藝術"
      mainTypePk="2828" name="聚沙成塔 Accumulating much from a little"
      representImage="http://cloud.culture.tw/e_upload_ccacloud/ccacloud/image/A0/B0/C-1/D-267/E-728/F-534/1364531871547.jpg"
      intro="此件作品設置於單位之大門廣場上,以具有代表錢幣之貝殼造型意像,運用鋼材層疊的創作手法,
表現出「積少成多,聚沙成塔」之財稅集中的精神意涵,而其旋轉上身之造刑動勢,更營造出單位的朝氣與活力,讓往來之民眾更能體現場域之精神。"
      areaCode="407" cityName="臺中市 西屯區" address="稅捐稽徵處"
      longitude="120.647237" latitude="24.1589"
      buildingYearName="P130-407-05"
      author="林舜龍、楊仁明、張明風 lin Shuen-Long, Yang Jen-Ming, Chang Ming-Feng"
      size="435*315*600cm" material="熱浸鍍鋅鋼板" />
      <Info groupTypeName="公共藝術" mainTypeName="公共藝術"
      mainTypePk="2829" name="所拾 Key"
      representImage="http://cloud.culture.tw/e_upload_ccacloud/ccacloud/image/A0/B0/C-1/D-267/E-726/F-362/1364531873719.jpg"
      intro="透過低頭側身的穿越,彷彿由內而外的淨化與蛻變。窄門是成功的代言,而低頭思苦的當時,澈悟
出成功所拾的所在。"
      areaCode="406" cityName="臺中市 北屯區" address="中清路70-1號 大德樓穿堂後方"
      longitude="120.669158" latitude="24.180345"
      buildingYearName="P127-406-05" author="廖秀玲 Liao Xiu-Ling"
      size="310*90*200" material="大理石、蛇岩" />
..
    </emap>
  </emapItem>
</XML_Head>

如果要抓裡面的 name,需要有工具做真正的解析XML的工作。這時就需要 xmlstarlet 這工具來進行。

解析XML的內容
先看此檔有什階層:

xml el o.xml
XML_Head
XML_Head/emapItem
XML_Head/emapItem/emap
XML_Head/emapItem/emap/Info
XML_Head/emapItem/emap/Info
XML_Head/emapItem/emap/Info
...

可能就是要抓每個 Info 標籤裡的東西。
若要更詳細可以:

$ xml el -a o.xml
XML_Head
XML_Head/@version
XML_Head/@status
XML_Head/@total
XML_Head/emapItem
XML_Head/emapItem/emap
XML_Head/emapItem/emap/Info
XML_Head/emapItem/emap/Info/@groupTypeName
XML_Head/emapItem/emap/Info/@mainTypeName
XML_Head/emapItem/emap/Info/@mainTypePk
XML_Head/emapItem/emap/Info/@name
XML_Head/emapItem/emap/Info/@representImage
XML_Head/emapItem/emap/Info/@intro
XML_Head/emapItem/emap/Info/@areaCode
XML_Head/emapItem/emap/Info/@cityName
XML_Head/emapItem/emap/Info/@address
XML_Head/emapItem/emap/Info/@longitude
XML_Head/emapItem/emap/Info/@latitude
XML_Head/emapItem/emap/Info/@buildingYearName
XML_Head/emapItem/emap/Info/@author
XML_Head/emapItem/emap/Info/@size
XML_Head/emapItem/emap/Info/@material
...

連想要抓的 @name 都辨識得到,要把該值抓出應就在眼前了…
接下來就應是怎麼query所要的內容。

XML的 select
查詢有幾個要抓的 Info 的元素?

$ xml sel -t -v "count(XML_Head/emapItem/emap/Info)" o.xml
2247

再用這個指令,就把所有name的值抓到了:

$ xml sel  -t -m /XML_Head/emapItem/emap/Info -v "concat(@name,'')" -n o.xml|less
聚沙成塔 Accumulating much from a little
所拾 Key
深耕.生根 Plowing and rooting
生命的禮讚 The praise of life
大橢圓
飛行船
珍愛一生(1)
珍愛一生(2)
..

如果要把其他值一起抓出,變成csv格式:

$ xml sel  -t -m /XML_Head/emapItem/emap/Info -v "concat(@name,'|',@author)" -n o.xml|less
聚沙成塔 Accumulating much from a little|林舜龍、楊仁明、張明風 lin Shuen-Long, Yang Jen-Ming, Chang Ming-Feng
所拾 Key|廖秀玲 Liao Xiu-Ling
深耕.生根 Plowing and rooting|邱泰洋 Ciou Tai-Yang
生命的禮讚 The praise of life|王振瑋 Wang Chen-Wei
大橢圓|赫穌斯•拉斐爾•索托(Jesus Rafael Soto)
飛行船|王為河
珍愛一生(1)|盧明德、郭挹芬
珍愛一生(2)|盧明德、郭挹芬
林間上翔|盧明德、郭挹芬

這樣就完工了,若要其他的值,就在 contact 裡繼續加所要的欄位。

參考資料:
XMLStarlet Command Line XML Toolkit
XMLStarlet 使用入门
XmlStarlet Command Line XML Toolkit User's Guide
How to indent html with xmllint?
XML formatting Indentation Tags Matching - Linux


上一篇
代轉信件的 Nullmailer
下一篇
快速指到常用的目錄及檔案的工具:fasd
系列文
現代環境下的 Linux 裡的新工具27
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言