2 package: XML2

Reference: http://xml2.r-lib.org/index.html

library(xml2); library(dplyr); library(stringr); library(purrr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
## Warning: package 'purrr' was built under R version 3.5.2

3 XML document

3.1 Typical structure

Source: XML Tree. (2019). W3schools.com. Retrieved 5 February 2019, from https://www.w3schools.com/xml/xml_tree.asp

Source: XML Tree. (2019). W3schools.com. Retrieved 5 February 2019, from https://www.w3schools.com/xml/xml_tree.asp

3.1.1 Element

An XML element is everything from (including) the element’s start tag to (including) the element’s end tag.

  • 帶有內容的element: <tag> 內容文字 </tag>

  • 不帶有內容的element: <sunnyday/>

3.1.2 Attribute

< >內「非element名稱」的其他設定:如 <sunnyday temperature="25C" />

3.2 Example

單純文字檔,以<?xml...>開頭,附檔名通常為.xml

範例:取自 https://www.dgbas.gov.tw/public/data/open/Stat/price/PR0103A1M.xml

<?xml version="1.0" encoding="utf-8"?>
<DataSet Tab_NAME="消費者物價特殊分類指數-月" Sender_NAME="行政院主計總處">
  <Obs1>
    <Item>總指數(不含食物)(民國105年=100)</Item>
    <TIME_PERIOD>1981M01</TIME_PERIOD>
    <FREQ>M</FREQ>
    <TYPE>原始值</TYPE>
    <Item_VALUE>62.05</Item_VALUE>
  </Obs1>
  <Obs2>
    <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>
    <TIME_PERIOD>1981M01</TIME_PERIOD>
    <FREQ>M</FREQ>
    <TYPE>原始值</TYPE>
    <Item_VALUE>60.35</Item_VALUE>
  </Obs2>  
</DataSet>
'<?xml version="1.0" encoding="utf-8"?>
<DataSet Tab_NAME="消費者物價特殊分類指數-月" Sender_NAME="行政院主計總處">
  <Obs1>
    <Item>總指數(不含食物)(民國105年=100)</Item>
    <TIME_PERIOD>1981M01</TIME_PERIOD>
    <FREQ>M</FREQ>
    <TYPE>原始值</TYPE>
    <Item_VALUE>62.05</Item_VALUE>
  </Obs1>
  <Obs2>
    <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>
    <TIME_PERIOD>1981M01</TIME_PERIOD>
    <FREQ>M</FREQ>
    <TYPE>原始值</TYPE>
    <Item_VALUE>60.35</Item_VALUE>
  </Obs2>  
</DataSet>' %>%
  read_xml -> example

4 XML Tree

XML documents are formed as element trees.

An XML tree starts at a root element and branches from the root to child elements.

All elements can have sub elements (child elements).

root只會有一個樹結。

4.1 root tree

Obtain root tree:

example %>%
  xml_root -> rootTree 

4.1.1 xml_document

檢視

rootTree %>% print
## {xml_document}
## <DataSet Tab_NAME="消費者物價特殊分類指數-月" Sender_NAME="行政院主計總處">
## [1] <Obs1>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M01 ...
## [2] <Obs2>\n  <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...

由上述的結果,我們得知rootTree是個特別的xml_document class, 其文件內容記錄在{xml_document}的下方。

4.1.2 two classes

class(rootTree)
## [1] "xml_document" "xml_node"

rootTree帶有兩個classes: xml_documentxml_node;前者是以文字字串角度來看,後者是以樹狀結構來看。

4.2 sub trees

xml_node class的物件可以進行子樹(subtree)的裁剪。

4.2.1 裁剪子樹

xml_child(n)可裁出rootTree第一層的子樹中的第n株子樹,在範例裡有Obs1及Obs2兩株子樹可裁。

xml_child不指定n時會裁出第一顆子樹。

rootTree %>%
  xml_child -> subTree1; print(subTree1)
## {xml_node}
## <Obs1>
## [1] <Item>總指數(不含食物)(民國105年=100)</Item>
## [2] <TIME_PERIOD>1981M01</TIME_PERIOD>
## [3] <FREQ>M</FREQ>
## [4] <TYPE>原始值</TYPE>
## [5] <Item_VALUE>62.05</Item_VALUE>
rootTree %>%
  xml_child(2) -> subTree2; print(subTree2)
## {xml_node}
## <Obs2>
## [1] <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>
## [2] <TIME_PERIOD>1981M01</TIME_PERIOD>
## [3] <FREQ>M</FREQ>
## [4] <TYPE>原始值</TYPE>
## [5] <Item_VALUE>60.35</Item_VALUE>

注意:裁剪出的子樹依然保有樹狀結構class,即保有xml_node class。(print時有{xml_node}標示。 )

xml_node在print時會有[1],[2],…,[n],顯示下一層子樹會有幾株。要查詢有幾株子樹也可以用xml_length()

subTree2 %>% xml_length
## [1] 5

範例:剪出Obs2下的Item_Value子樹

rootTree %>%
  xml_child(2) %>% # 裁出第2株子樹
  xml_child(5) # 再裁出接下來的第5株子樹
## {xml_node}
## <Item_VALUE>

4.3 其他常用裁剪函數

4.3.1 xml_parent()

xml_parent()由子樹找回上層母樹

subTree1 %>%
  xml_parent() -> parentOfsubTree1; parentOfsubTree1
## {xml_node}
## <DataSet Tab_NAME="消費者物價特殊分類指數-月" Sender_NAME="行政院主計總處">
## [1] <Obs1>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M01 ...
## [2] <Obs2>\n  <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...

parentOfsubTree1透過xml_child()自然可裁出subTree1

4.3.2 xml_siblings()

xml_siblings()會找出對應的兄弟姐妹樹

subTree1 %>%
  xml_siblings -> siblingsOfsubTree1; siblingsOfsubTree1
## {xml_nodeset (1)}
## [1] <Obs2>\n  <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...

siblingsOfsubTree1subTree2有點不同。siblingsOfsubTree1是一個list集合,subTree2是其中一個元素。雖然subTree2是唯一的sibling tree,但要siblingsOfsubTree1[[1]]才會是subTree2

siblingsOfsubTree1[[1]] 
## {xml_node}
## <Obs2>
## [1] <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>
## [2] <TIME_PERIOD>1981M01</TIME_PERIOD>
## [3] <FREQ>M</FREQ>
## [4] <TYPE>原始值</TYPE>
## [5] <Item_VALUE>60.35</Item_VALUE>
subTree2
## {xml_node}
## <Obs2>
## [1] <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>
## [2] <TIME_PERIOD>1981M01</TIME_PERIOD>
## [3] <FREQ>M</FREQ>
## [4] <TYPE>原始值</TYPE>
## [5] <Item_VALUE>60.35</Item_VALUE>

5 Contents

xml_contents(): xml_node tree依第一層子樹列出各別子樹原始xml文字內容

rootTree %>%
  xml_contents %>% print
## {xml_nodeset (2)}
## [1] <Obs1>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M01 ...
## [2] <Obs2>\n  <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
subTree1 %>%
  xml_contents %>% print
## {xml_nodeset (5)}
## [1] <Item>總指數(不含食物)(民國105年=100)</Item>
## [2] <TIME_PERIOD>1981M01</TIME_PERIOD>
## [3] <FREQ>M</FREQ>
## [4] <TYPE>原始值</TYPE>
## [5] <Item_VALUE>62.05</Item_VALUE>

5.1 text

xml_text()會取出xml_contents中各element的值(即去除element tag<tag></tag>),並以字串方式保留

subTree1 %>%
  xml_contents %>%
  xml_text
## [1] "總指數(不含食物)(民國105年=100)" "1981M01"                        
## [3] "M"                               "原始值"                         
## [5] "62.05"

5.2 number

subTree1 %>%
  xml_contents %>%
  {.[[5]]} %>% # 取出第5個content
  xml_double # 拿出其內容並以數值儲存
## [1] 62.05

6 XPath

Reference: https://www.w3schools.com/xml/xpath_intro.asp

XPath是用來描述取得子樹的一行文字

先前的子樹裁剪是由rootTree開始,透過xml_child(),xml_children()等,一層一層剪下去。若知道某一顆子樹的XPath,我們可以用xml_find_all(rootTree,Xpath)一行指示就取到那顆樹,較有效率。

6.1 查詢

xml_path(): 查詢xml_node樹的XPath

subTree1 %>%
  xml_path -> xpathOfsubTree1; xpathOfsubTree1
## [1] "/DataSet/Obs1"

6.2 練習

"https://www.dgbas.gov.tw/public/data/open/Stat/price/PR0103A1M.xml" %>%
  read_xml -> cpiXML

6.2.1 取出rootTree

cpiXML %>%
  xml_root -> cpiRoot; cpiRoot
## {xml_document}
## <DataSet Tab_NAME="消費者物價特殊分類指數-月" Sender_NAME="行政院主計總處">
##  [1] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M01 ...
##  [2] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M01 ...
##  [3] <Obs>\n  <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
##  [4] <Obs>\n  <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
##  [5] <Obs>\n  <Item>總指數(不含蔬果及能源)【即核心物價】(民國105年=100)</Item>\n  <TIME_PERI ...
##  [6] <Obs>\n  <Item>總指數(不含蔬果及能源)【即核心物價】(民國105年=100)</Item>\n  <TIME_PERI ...
##  [7] <Obs>\n  <Item>總指數(不含食物及能源)(民國105年=100)</Item>\n  <TIME_PERIOD>1981 ...
##  [8] <Obs>\n  <Item>總指數(不含食物及能源)(民國105年=100)</Item>\n  <TIME_PERIOD>1981 ...
##  [9] <Obs>\n  <Item>總指數(不含蔬果水產及能源)(民國105年=100)</Item>\n  <TIME_PERIOD>19 ...
## [10] <Obs>\n  <Item>總指數(不含蔬果水產及能源)(民國105年=100)</Item>\n  <TIME_PERIOD>19 ...
## [11] <Obs>\n  <Item>總指數(不含設算租金)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
## [12] <Obs>\n  <Item>總指數(不含設算租金)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
## [13] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M02 ...
## [14] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M02 ...
## [15] <Obs>\n  <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
## [16] <Obs>\n  <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
## [17] <Obs>\n  <Item>總指數(不含蔬果及能源)【即核心物價】(民國105年=100)</Item>\n  <TIME_PERI ...
## [18] <Obs>\n  <Item>總指數(不含蔬果及能源)【即核心物價】(民國105年=100)</Item>\n  <TIME_PERI ...
## [19] <Obs>\n  <Item>總指數(不含食物及能源)(民國105年=100)</Item>\n  <TIME_PERIOD>1981 ...
## [20] <Obs>\n  <Item>總指數(不含食物及能源)(民國105年=100)</Item>\n  <TIME_PERIOD>1981 ...
## ...

6.2.2 有多少Obs

cpiRoot %>%
  xml_length()
## [1] 5472

6.2.3 裁出第一個Obs子樹

cpiRoot %>%
  xml_child ->
  firstObsTree; firstObsTree
## {xml_node}
## <Obs>
## [1] <Item>總指數(不含食物)(民國105年=100)</Item>
## [2] <TIME_PERIOD>1981M01</TIME_PERIOD>
## [3] <FREQ>M</FREQ>
## [4] <TYPE>原始值</TYPE>
## [5] <Item_VALUE>62.05</Item_VALUE>

6.2.4 裁出firstObsTree的Item子樹

firstObsTree %>%
  xml_child -> firstObsItemTree; firstObsItemTree
## {xml_node}
## <Item>

6.2.5 找出firstObsItemTree的XPath

firstObsItemTree %>%
  xml_path
## [1] "/DataSet/Obs[1]/Item"

6.3 “/”

XPath裡/後方名字代表node element name:

/DataSet: 走到DataSet node

/DataSet/Obs: 走到DataSet node之後再走到Obs node

6.3.1 練習

/DataSet/Obs可以走到那些子樹並裁出來

"/DataSet/Obs" %>%
  xml_find_all(cpiRoot,.) 
## {xml_nodeset (5472)}
##  [1] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M01 ...
##  [2] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M01 ...
##  [3] <Obs>\n  <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
##  [4] <Obs>\n  <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
##  [5] <Obs>\n  <Item>總指數(不含蔬果及能源)【即核心物價】(民國105年=100)</Item>\n  <TIME_PERI ...
##  [6] <Obs>\n  <Item>總指數(不含蔬果及能源)【即核心物價】(民國105年=100)</Item>\n  <TIME_PERI ...
##  [7] <Obs>\n  <Item>總指數(不含食物及能源)(民國105年=100)</Item>\n  <TIME_PERIOD>1981 ...
##  [8] <Obs>\n  <Item>總指數(不含食物及能源)(民國105年=100)</Item>\n  <TIME_PERIOD>1981 ...
##  [9] <Obs>\n  <Item>總指數(不含蔬果水產及能源)(民國105年=100)</Item>\n  <TIME_PERIOD>19 ...
## [10] <Obs>\n  <Item>總指數(不含蔬果水產及能源)(民國105年=100)</Item>\n  <TIME_PERIOD>19 ...
## [11] <Obs>\n  <Item>總指數(不含設算租金)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
## [12] <Obs>\n  <Item>總指數(不含設算租金)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
## [13] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M02 ...
## [14] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M02 ...
## [15] <Obs>\n  <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
## [16] <Obs>\n  <Item>總指數(不含蔬菜水果)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M ...
## [17] <Obs>\n  <Item>總指數(不含蔬果及能源)【即核心物價】(民國105年=100)</Item>\n  <TIME_PERI ...
## [18] <Obs>\n  <Item>總指數(不含蔬果及能源)【即核心物價】(民國105年=100)</Item>\n  <TIME_PERI ...
## [19] <Obs>\n  <Item>總指數(不含食物及能源)(民國105年=100)</Item>\n  <TIME_PERIOD>1981 ...
## [20] <Obs>\n  <Item>總指數(不含食物及能源)(民國105年=100)</Item>\n  <TIME_PERIOD>1981 ...
## ...

符合此XPath子樹會有5472株。

6.4 “[ ]”

/DataSet/Obs若要再走下去會有5472種走法,有時要選出符合某種條件的路就使用/DataSet/Obs[條件描述]

條件描述為predicate。

條件描述會以每一株的contents來看

使用xml_find_first()裁出第一株子樹,並用xml_contents()看一下contents

"/DataSet/Obs" %>%
  xml_find_first(cpiRoot,.) %>%
  xml_contents()
## {xml_nodeset (5)}
## [1] <Item>總指數(不含食物)(民國105年=100)</Item>
## [2] <TIME_PERIOD>1981M01</TIME_PERIOD>
## [3] <FREQ>M</FREQ>
## [4] <TYPE>原始值</TYPE>
## [5] <Item_VALUE>62.05</Item_VALUE>

[...] 裡…必需是contents條件。

選出/DataSet/Obs中contents符合TYPE='原始值'Item='總指數(不含食物)(民國105年=100)'

"/DataSet/Obs[TYPE='原始值' and Item='總指數(不含食物)(民國105年=100)']" %>%
  xml_find_all(cpiRoot,.) -> cpiSelect; cpiSelect
## {xml_nodeset (456)}
##  [1] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M01 ...
##  [2] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M02 ...
##  [3] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M03 ...
##  [4] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M04 ...
##  [5] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M05 ...
##  [6] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M06 ...
##  [7] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M07 ...
##  [8] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M08 ...
##  [9] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M09 ...
## [10] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M10 ...
## [11] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M11 ...
## [12] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1981M12 ...
## [13] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1982M01 ...
## [14] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1982M02 ...
## [15] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1982M03 ...
## [16] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1982M04 ...
## [17] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1982M05 ...
## [18] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1982M06 ...
## [19] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1982M07 ...
## [20] <Obs>\n  <Item>總指數(不含食物)(民國105年=100)</Item>\n  <TIME_PERIOD>1982M08 ...
## ...

原本XPath/DataSet/Obs子樹會有5472株,符合contents描述的只剩下456株。

更多可用的operators: https://www.w3schools.com/xml/xpath_operators.asp

7 轉成Data frame

7.1 Terminal nodes

Terminal nodes in XMLs are nodes that do no have any “children”. These nodes contain the information we generally want to extract into a tidy data frame.

cpiSelect 1st child的結構:只包含terminal nodes

cpiSelect[[1]] %>%
  xml_structure()
## <Obs>
##   <Item>
##     {text}
##   <TIME_PERIOD>
##     {text}
##   <FREQ>
##     {text}
##   <TYPE>
##     {text}
##   <Item_VALUE>
##     {text}

7.2 as_list()

把cpiSelect的456株子樹轉成帶有456個elements的List。

cpiSelect %>% 
  as_list -> cpiSelectList

試著把第一株樹存成data frame

.x<-cpiSelectList[[1]]
data.frame(
  項目=.x$Item[[1]],
  期間=.x$TIME_PERIOD[[1]],
  頻率=.x$FREQ[[1]],
  格式=.x$TYPE[[1]],
  CPI指數=as.numeric(.x$Item_VALUE[[1]]),
  stringsAsFactors = F
)
##                              項目    期間 頻率   格式 CPI指數
## 1 總指數(不含食物)(民國105年=100) 1981M01    M 原始值   62.05

寫一個函數,讓cpiSelectList中的任一個element(即cpiSelectList[[i]], i為element位置)輸入後會輸出一個一筆資料的data frame.

treeList2df<-function(.x){
  data.frame(
  項目=.x$Item[[1]],
  期間=.x$TIME_PERIOD[[1]],
  頻率=.x$FREQ[[1]],
  格式=.x$TYPE[[1]],
  CPI指數=as.numeric(.x$Item_VALUE[[1]]),
  stringsAsFactors = F
)
}

測試一下函數

cpiSelectList[[2]] %>%
  treeList2df()
##                              項目    期間 頻率   格式 CPI指數
## 1 總指數(不含食物)(民國105年=100) 1981M02    M 原始值   62.63

使用迴圈將cpiSelectList疊成完整資料data frame

cpiSelectList %>%
  purrr::map_dfr(treeList2df) -> cpiData

7.3 xmltools

devtools::install_github('dantonnoriega/xmltools')
library(xmltools)

xmltools::xml_dig_df()可將只有terminal nodes的nodeset轉成list of dataframes.

cpiSelect %>%
  xmltools::xml_dig_df(.) -> cpiSelectList

xml_dig_df()是個list,在應用上每個元素可以是不同結構的dataframe.

若每個dataframe有相同變數項目可推疊成一個dataframe,可透過如下的步驟完成

cpiSelectList %>% 
  map_dfr(~.x) -> cpiSelectDF

8 綜合練習

8.1 練習1

8.1.1 由Item node找出資料裡有那幾種消費者物價指數

"/DataSet/Obs/Item" %>% # 從Root的DataSet選出所有Obs子樹下的Item節點
  xml_find_all(cpiRoot,.) %>% 
  xml_contents %>% xml_text %>% # 取出contents, 再粹出內容字串
  as.factor %>% levels %>% print
## [1] "總指數(不含蔬果及能源)【即核心物價】(民國105年=100)"
## [2] "總指數(不含蔬果水產及能源)(民國105年=100)"          
## [3] "總指數(不含蔬菜水果)(民國105年=100)"                
## [4] "總指數(不含設算租金)(民國105年=100)"                
## [5] "總指數(不含食物)(民國105年=100)"                    
## [6] "總指數(不含食物及能源)(民國105年=100)"

8.1.2 選出「核心物價」指數「原始值」的所有Obs子樹

"/DataSet/Obs[Item='總指數(不含蔬果及能源)【即核心物價】(民國105年=100)' and TYPE='原始值']" %>%
  xml_find_all(cpiRoot,.) -> coreCPI

8.1.3 存出核心物價指數

coreCPI %>%
  as_list %>%
  map_dfr(treeList2df) -> 
  coreCPIdf