一.讀寫文本格式的數據
因為其簡單的文件交互語法、直觀的數據結構,以及諸如元組打包解包之類的便利功能。Python在文本和文件處理方面已經成為一門招人喜歡的語言。
pandas提供了一些用於將表格型數據讀取為DataFrame對象的函數。下表對它們進行了總結,其中read_csv和read_table可能是我們今后用的最多的。
函數 說明
read_excel 將excel表的數據讀取為DataFrame
read_csv 從文件、URL、文件型對象中加載帶分隔符的數據。默認分隔符為逗號
read_table 從文件、URL、文件型對象中加載帶有分隔符的數據。默認分隔符為制表符("\t")
read_fwf 讀取定寬列格式數據(也就是說,沒有分隔符)
read_clipboard 讀取剪貼板中的數據,可以看做read_table的剪貼板版,在將網頁轉換為表格時很有用
下面大致介紹一下這些函數在將文本數據轉換為DataFrame時所用到的一些技術。這些函數的選項可以划分為一下幾個大類:
索引:講一個或多個列當做返回的DataFrame處理,以及是否從文件、用戶獲取列名
類型推斷和數據轉換:包括用戶定義值的轉換、缺失值標記列表等。
日期解析:包括組合功能,比如將分散在多個列中的日期時間信息組合成結果中的單個列。
迭代:支持對大文件進行逐塊迭代。
不規整數據問題:跳過一些行、頁腳、注釋或其他一些不重要的東西(比如由成千上萬個逗號隔開的數值數據)。
類型推斷(type inference)是這些函數中最重要的功能之一,也就是說,我們不需要指定列的類型到底是數值、整數、布爾值,還是字符串。日期和其他自定義類型的處理需要多花點工夫才行。首先我們來看一個以逗號分隔的csv文本文件:
In [17]: !type test.csv
a,b,c,d,kobe
1,3,5,7,calvin
2,4,6,8,hello
9,10,11,12 ,world
由於該文件以逗號分隔,所以我們可以使用read_csv將其讀入一個DataFrame:
In [18]: import pandas as pd
In [21]: df=pd.read_csv('test.csv')
In [22]: df
Out[22]:
a b c d kobe
0 1 3 5 7 calvin
1 2 4 6 8 hello
2 9 10 11 12 world
我們也可以用read_table。只不過需要制定分隔符而已:
In [23]: pd.read_table('test.csv',sep=',')
Out[23]:
a b c d kobe
0 1 3 5 7 calvin
1 2 4 6 8 hello
2 9 10 11 12 world
並不是所有文件都有標題行。看看下面這個文件:
In [24]: !type test02.csv
1,3,5,7,calvin
2,4,6,8,hello
9,10,11,12,world
讀入該文件的辦法有兩個。我們可以讓pandas為其分配默認的列名,也可以自己定義列名:
In [25]: pd.read_csv('test02.csv',header=None)
Out[25]:
0 1 2 3 4
0 1 3 5 7 calvin
1 2 4 6 8 hello
2 9 10 11 12 world
In [27]: pd.read_csv('test02.csv',names=['a','b','c','d','name'])
Out[27]:
a b c d name
0 1 3 5 7 calvin
1 2 4 6 8 hello
2 9 10 11 12 world
假如我們希望將name列做成DataFrame的索引,我們可以明確表示要將該列放到索引4的位置上,也可以通過index_col參數指定"name":
In [28]: pd.read_csv("test02.csv",names=['a','b','c','d','name'],index_col='nam
...: e')
Out[28]:
a b c d
name
calvin 1 3 5 7
hello 2 4 6 8
world 9 10 11 12
如果希望將多個列做成一個層次化索引,只需傳入由列編號或列名組成的列表即可:
In [35]: !type test03.csv
key1,key2,value1,value2
one,a,1,2
one,b,3,4
one,c,5,6
one,d,7,8
two,a,9,10
two,b,11,12
two,c,13,14
two,d,15,16
In [36]: parsed=pd.read_csv('test03.csv',index_col=['key1','key2'])
In [37]: parsed
Out[37]:
value1 value2
key1 key2
one a 1 2
b 3 4
c 5 6
d 7 8
two a 9 10
b 11 12
c 13 14
d 15 16
有些表格可能不是用固定的分隔符去分隔字段的(比如空白符或其他模式)。對於這種情況,可以編寫一個正則表達式來作為read_table的分隔符。看下面這個文本文件:
In [3]: import pandas as pd
In [4]: from pandas import Series,DataFrame
In [5]: import numpy as np
In [15]: list(open('test04.txt'))
Out[15]:
[' A B C\n',
'aaa -123 -2.4 -0.88\n',
'bbb -0.66 -2.88 -0.83\n',
'ccc -6.5 -2.6 -0.89\n',
'ddd -3.5 -2.9 -0.99']
該文件各個字段由數量不定的空白符分隔,雖然我們可以對其做一些手工調整,但這個情況還是處理比較好。本例的這個情況可以用正則表達式\s+表示,於是我們就有了:
In [16]: result=pd.read_table('test04.txt',sep='\s+')
In [17]: result
Out[17]:
A B C
aaa -123.00 -2.40 -0.88
bbb -0.66 -2.88 -0.83
ccc -6.50 -2.60 -0.89
ddd -3.50 -2.90 -0.99
這里,由於列名比數據行的數量少,所以read_table推斷第一列應該是DataFrame的索引。
這些解析器函數還有許多參數可以幫助我們處理各種各樣的異形文件格式(見下表)。比如說,我們可以用skiprows跳過文件的第一行、第三行和第四行:
In [1]: import numpy as np
In [2]: import pandas as pd
In [3]: from pandas import Series,DataFrame
In [5]: !type test05.csv
#hello !
a,b,c,d,info
#just wanted to learning something difficult
#who reads CSV files,anyway?
1,2,3,4,calvin
5,6,7,8,kobe
9,10,11,12,michale
In [6]: pd.read_csv('test05.csv',skiprows=[0,2,3])
Out[6]:
a b c d info
0 1 2 3 4 calvin
1 5 6 7 8 kobe
2 9 10 11 12 michale
缺失值處理是文件解析任務中的一個重要組成部分。缺失數據經常是要么沒有(空字符串),要么用某個標記值表示。默認情況下,pandas會用一組經常出現的標記值進行識別,如NA、-1.#IND以及NULL等:
In [14]: !type test06.csv
numbers,a,b,c,d,info
one,1,2,3,4,NA
two,5,6,,8,calvin
three,9,10,11,12,kobe
In [15]: result=pd.read_csv('test06.csv')
In [16]: result
Out[16]:
numbers a b c d info
0 one 1 2 3.0 4 NaN
1 two 5 6 NaN 8 calvin
2 three 9 10 11.0 12 kobe
In [17]: pd.isnull(result)
Out[17]:
numbers a b c d info
0 False False False False False True
1 False False False True False False
2 False False False False False False
na_values可以接受一組用於表示缺失值的字符串:
In [25]: result=pd.read_csv('test06.csv',na_values=['NULL'])
In [26]: result
Out[26]:
numbers a b c d info
0 one 1 2 3.0 4 NaN
1 two 5 6 NaN 8 calvin
2 three 9 10 11.0 12 kobe
可以用一個字典為各列指定不同的NA標記值:
In [33]: sentinels={'info':['kobe','NA'],'numbers':['one','NA']}
In [34]: result=pd.read_csv('test06.csv',na_values=sentinels)
In [35]: result
Out[35]:
numbers a b c d info
0 NaN 1 2 3.0 4 NaN
1 two 5 6 NaN 8 calvin
2 three 9 10 11.0 12 NaN
下表為read_csv/read_table函數的參數:
參數 說明
path 表示文件系統位置、URL/文件型對象的字符串
sep或delimiter 用於對行中各字段進行拆分的字符序列或正則表達式
header 用作列名的行號。默認為0(第一行),如果沒有header行就應該設置為None
index_col 用作行索引的列編號或列名。可以是單個名稱/數字或由多個名稱/數字組成的列表(層次化索引)
names 用於結果的列名列表,結合header=None
skiprows 需要忽略的行數(從文件開始處算起),或需要跳過的行號列表(從0開始)
na_values 一組用於替換NA的值
comment 用於將注釋信息從行尾拆分出去的字符(一個或多個)
parse_dates 嘗試將數據解析為日期,默認為False.如果為True,則嘗試解析所有列。此外,還可以指定需要解析的一組列號或列名。如果列表的元素為列表或元組,就會將多個列組合到一起再進行日期解析工作(例如,日期/時間分別位於兩個列中)
keep_date_col 如果連接多列解析日期,則保持參與連接的列。默認為False
converters 由列號/列名跟函數之間的映射關系組成的字典。例如,{‘foo’:f}會對foo列的所有值應用函數f
dayfirst 當解析有歧義的日期時,將其看做國際格式(例如,11/27/2018->Nov 27,2018.默認為False
date_parser 用於解析日期的函數
nrows 需要讀取的行數(從文件開始處算起)
iterator 返回一個TextParser以便逐塊讀取文件
chunksize 文件塊的大小(用於迭代)
skip_footer 需要忽略的行數(從文件末尾處算起)
verbose 打印各種解析器輸出信息,比如“非數值列中缺失值的數量”等
encoding 用於unicode的文本編碼格式。例如,“utf-8”表示用UTF-8編碼的文本
squeeze 如果數據經解析后僅含一列,則返回Series
thousands 千分位分隔符,如“,”或“.”
二.逐塊讀取文本文件
在處理很大的文件時,或找出大文件中的參數集以便於后續處理時,我們可能只想讀取文件的一小部分或逐塊對文件進行迭代。
In [38]: result=pd.read_csv('test07.csv')
In [39]: result
Out[39]:
numbers a b c d info
0 one 1 2 3 4 NaN
1 two 5 6 NaN 8 calvin
2 three 9 10 11 12 kobe
3 numbers a b c d info
4 one 1 2 3 4 NaN
5 two 5 6 NaN 8 calvin
6 three 9 10 11 12 kobe
7 numbers a b c d info
8 one 1 2 3 4 NaN
9 two 5 6 NaN 8 calvin
10 three 9 10 11 12 kobe
11 numbers a b c d info
12 one 1 2 3 4 NaN
13 two 5 6 NaN 8 calvin
14 three 9 10 11 12 kobe
如果只想讀取幾行(避免讀取整個文件),通過nrows進行指定即可:
In [40]: pd.read_csv('test07.csv',nrows=5)
Out[40]:
numbers a b c d info
0 one 1 2 3 4 NaN
1 two 5 6 NaN 8 calvin
2 three 9 10 11 12 kobe
3 numbers a b c d info
4 one 1 2 3 4 NaN
要逐塊讀取文件,需要設置chunksize(行數):
In [41]: chunker=pd.read_csv('test07.csv',chunksize=10)
In [42]: chunker
Out[42]: <pandas.io.parsers.TextFileReader at 0x6905278>
read_csv所返回的這個TextParser對象使我們可以根據chunksize對文件進行逐塊迭代:
In [46]: for i in chunker:
...: print(i)
...:
numbers a b c d info
10 three 9 10 11 12 kobe
11 numbers a b c d info
12 one 1 2 3 4 NaN
13 two 5 6 NaN 8 calvin
14 three 9 10 11 12 kobe
TextParser還有一個get_chunk方法,它使我們可以讀取任意大小的快。
In [47]: reader=pd.read_csv('test07.csv',iterator=True)
In [48]: reader.get_chunk(5)
Out[48]:
numbers a b c d info
0 one 1 2 3 4 NaN
1 two 5 6 NaN 8 calvin
2 three 9 10 11 12 kobe
3 numbers a b c d info
4 one 1 2 3 4 NaN
三.將數據寫出到文本格式
數據也可以被輸出為分隔符格式的文本。我們再來看看之前讀過的一個CSV文件:
In [51]: data=pd.read_csv('test06.csv')
In [52]: data
Out[52]:
numbers a b c d info
0 one 1 2 3.0 4 NaN
1 two 5 6 NaN 8 calvin
2 three 9 10 11.0 12 kobe
利用DataFrame的to_csv方法,我們可以將數據寫到一個以逗號分隔的文件中:
In [53]: data.to_csv('out.csv')
In [54]: !type out.csv
,numbers,a,b,c,d,info
0,one,1,2,3.0,4,
1,two,5,6,,8,calvin
2,three,9,10,11.0,12,kobe
In [58]: data.to_csv('out.csv',sep='|')
In [59]: !type out.csv
|numbers|a|b|c|d|info
0|one|1|2|3.0|4|
1|two|5|6||8|calvin
2|three|9|10|11.0|12|kobe
缺失值在輸出結果中會被表示為空字符串。我們可能希望將其表示為別的標記值:
In [60]: data.to_csv('out.csv',na_rep='NULL')
In [61]: !type out.csv
,numbers,a,b,c,d,info
0,one,1,2,3.0,4,NULL
1,two,5,6,NULL,8,calvin
2,three,9,10,11.0,12,kobe
如果沒有設置其他選項,則會寫出行和列的標簽。當然,它們也都可以被禁用:
In [62]: data.to_csv('out.csv',index=False,header=False)
In [63]: !type out.csv
one,1,2,3.0,4,
two,5,6,,8,calvin
three,9,10,11.0,12,kobe
此外,我們可以只寫出一部分的列,並以我們指定的順序排列:
In [72]: data.to_csv('out.csv',index=False,columns=['a','b'])
In [73]: !type out.csv
a,b
1,2
5,6
9,10
Series也有一個to_csv方法:
In [74]: dates=pd.date_range('11/27/2018',periods=7)
In [75]: ts=Series(np.arange(7),index=dates)
In [76]: ts.to_csv('tseries.csv')
In [77]: !type tseries.csv
2018-11-27,0
2018-11-28,1
2018-11-29,2
2018-11-30,3
2018-12-01,4
2018-12-02,5
2018-12-03,6
只需一點整理工作(無header行,第一列作索引)就能用read_csv將csv文件讀取為Series,
In [81]: pd.read_csv('tseries.csv',parse_dates=True)
Out[81]:
2018-11-27 0
0 2018-11-28 1
1 2018-11-29 2
2 2018-11-30 3
3 2018-12-01 4
4 2018-12-02 5
5 2018-12-03 6
四.手工處理分隔符格式
大部分存儲在磁盤上的表格型數據都能用pandas.read_table進行加載。然而,有時還是需要做一些手工處理。由於接收到含有畸形行的文件而使read_table出毛病的情況並不少見。為了說明這些基本工具,看看下面這個簡單的csv文件:
In [82]: !type test08.csv
"a","b","c"
"3","4","5"
"6","7","8","9"
對於任何單字符分隔符文件,可以直接使用Python內置的csv模塊。將任意已打開的文件或文件型的對象傳給csv.reader:
In [83]: import csv
In [84]: f=open('test08.csv')
In [85]: reader=csv.reader(f)
對這個reader進行迭代將會為每行產生一個列表(並移除了所有的引號):
In [87]: for line in reader:
...: print (line)
...:
['a', 'b', 'c']
['3', '4', '5']
['6', '7', '8', '9']
現在,為了使數據格式合乎要求,我們需要對其做一些整理工作:
In [88]: lines=list(csv.reader(open('test08.csv')))
In [89]: header,values=lines[0],lines[1:]
In [90]: data_dict={h:v for h,v in zip(header,zip(*values))}
In [91]: data_dict
Out[91]: {'a': ('3', '6'), 'b': ('4', '7'), 'c': ('5', '8')}
CSV文件的形式有很多。只需定義csv.Dialect的一個子類即可定義出新格式(如專門的分隔符、字符串引用約定、行結束符等):
In [10]: class my_dialect(csv.Dialect):
...: lineterminator='\n'
...: delimiter=';'
...: quotechar='"'
...: quoting=csv.QUOTE_MINIMAL
reader=csv.reader(f,dialect=my_dialect)
各個CSV語支的參數也可以關鍵字的形式提供給csv.reader,兒無需定義子類
reader=csv.reader(f,delimiter='|')
可用的選項(csv.Dialect的屬性)及其功能如下表所示
下表為CSV語支選項
參數 說明
delimiter 用於分隔字段的單字符字符串。默認為“,”
lineterminator 用於寫操作的行結束符,默認為“\r\n”.讀操作將忽略此選項,它能認出跨平台行結束符
quotechar 用於帶有特殊字符(如分隔符)的字段的引用符號。默認為““””
quoting 引用約定。可選值包括csv.QUOTE_ALL(引用所有字段)、csv.QUOTE_MINIMAL(只引用帶有諸如分隔符之類特殊字符的字段)、csv.QUOTE_NONNUMBERIC以及csv.QUOTE_NON(不引用)。默認為QUOTE_MINIMAL
skipinitialspace 忽略分隔符后面的空白符。默認為False
doublequote 如何處理字段內的引用符號。如果為True,則雙寫。完整信息及行為可以參見在線文檔
escaperchar 用於對分隔符進行轉義的字符串(如果quoting被設置為csv.QUOTE_NONE的話)。默認禁用
要手工輸出分隔符文件,我們可以使用csv.writer。它接收一個一打開且可寫的文件對象以及跟csv.reader相同的那些語支盒格式化選項:
In [11]: with open ('test001.csv','w') as f:
...: writer=csv.writer(f,dialect=my_dialect)
...: writer.writerow(('one','two','three'))
...: writer.writerow(('1','2','3'))
...: writer.writerow(('4','5','6'))
...: writer.writerow(('7','8','9'))
五.JSON數據
JSON(JavaScript Object Notation的簡稱)已經成為通過HTTP請求在Web瀏覽器和其他應用程序之間發送數據的標准格式之一。它是一種比表格型文本格式(如CSV)靈活得多的數據格式。下面是一個例子:
In [12]: obj="""
...: {"country":"China",
...: "places_lived":["Xiantao","Shenzhen","Wuhan"],
...: "pet":null,
...: "siblings":[{"name":"Scott","age":25,"pet":"Zuko"},
...: {"name":"Kate","age":20,"pet":"Jackie"}]
...: }
...: """
除其空值null和一些其他的細微差別(如列表末尾不允許存在多余的逗號)之外,JSON非常接近於有效的Python代碼。基本類型有對象(字典)、數組(列表)、字符串、數值、布爾值以及null。對象中所有的鍵都必須是字符串。許多Python庫都可以讀寫JSON數據。我們將使用json,因為它是構建於Python標准庫中的,通過json.loads即可將json字符串轉換成Python形式:
In [13]: import json
In [17]: result=json.loads(obj)
In [18]: result
Out[18]:
{'country': 'China',
'places_lived': ['Xiantao', 'Shenzhen', 'Wuhan'],
'pet': None,
'siblings': [{'name': 'Scott', 'age': 25, 'pet': 'Zuko'},
{'name': 'Kate', 'age': 20, 'pet': 'Jackie'}]}
相反,json.dumps則將Python對象轉換成JSON格式:
In [19]: asjson=json.dumps(result)
In [20]: asjson
Out[20]: '{"country": "China", "places_lived": ["Xiantao", "Shenzhen", "Wuhan"],
"pet": null, "siblings": [{"name": "Scott", "age": 25, "pet": "Zuko"}, {"name":
"Kate", "age": 20, "pet": "Jackie"}]}'
如何將(一個或一組)JSON對象轉換為DataFrame或其他便於分析的數據結構就由我們決定了,最簡便的方式是:向DataFrame構造器傳入一組JSON對象,並選取數據字段的子集。
In [22]: from pandas import DataFrame
In [23]: siblings=DataFrame(result['siblings'],columns=['name','age','pet'])
In [24]: siblings
Out[24]:
name age pet
0 Scott 25 Zuko
1 Kate 20 Jackie
下面看下DataFrame數據結構轉換為JSON數據格式和以及轉換之后讀取JSON數據
In [29]: import numpy as np
In [30]: si = DataFrame(np.zeros((4, 4)), columns=list(range(4)),index=[str(i)
...: for i in range(4)])
In [31]: si
Out[31]:
0 1 2 3
0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0
3 0.0 0.0 0.0 0.0
In [32]: si.index
Out[32]: Index(['0', '1', '2', '3'], dtype='object')
In [33]: si.columns
Out[33]: Int64Index([0, 1, 2, 3], dtype='int64')
In [32]: si.index
Out[32]: Index(['0', '1', '2', '3'], dtype='object')
In [33]: si.columns
Out[33]: Int64Index([0, 1, 2, 3], dtype='int64')
In [34]: si_json=si.to_json()
In [35]: import pandas as pd
In [36]: sij=pd.read_json(si_json,convert_axes=False) #不須將軸的類型轉換為合適的類型
In [37]: sij
Out[37]:
0 1 2 3
0 0 0 0 0
1 0 0 0 0
2 0 0 0 0
3 0 0 0 0
In [38]: sij.index
Out[38]: Index(['0', '1', '2', '3'], dtype='object')
In [39]: sij.columns
Out[39]: Index(['0', '1', '2', '3'], dtype='object')
六.XML和HTML:Web信息收集
Python有許多可以讀寫HTML和XML格式數據的庫。lxml(python3需要單獨安裝)就是其中之一,它能夠高效且可靠地解析大文件。lxml有多個編程接口。首先我要用lxml.html處理HTML,然后再用lxml.objectify做一些XML處理。
許多網站都將數據放到HTML表格中以便在瀏覽器中查看,但不能以一種更易於機器閱讀的格式(如JSON、HTML或XML)進行下載。
首先,找到你希望獲取數據的URL,利用urllib.request將其打開,然后用lxml解析得到的數據流,如下所示:
In [24]: from urllib import request
In [25]: url="http://www.hao123.com/nba"
In [26]: wp=request.urlopen(url) #打開鏈接
In [27]: content=wp.read() #獲取頁面內容
#利用lxml.objectify解析XML
XML(Extensible Markup Language)是另一種常見的支持分層、嵌套數據以及元數據的結構化數據格式。本書所使用的這些文件實際上來自於一個很大的XML文檔。下面介紹另一個用於操作XML數據的接口,即lxml.objectify。
我們先用lxml.objectify解析文件,然后通過getroot得到該XML文件的根節點的引用:
test002.xml的內容如下:
<INDICATOR>
<INDICATOR_SEQ>373889</INDICATOR_SEQ>
<PARENT_SEQ></PARENT_SEQ>
<AGENCY_NAME>Metro-nORTH Railroad</AGENCY_NAME>
<DESCRIPTION>Percent of the time that escalator are operational systemwide.The availability rate is based on physical observations performed the morning of regular business days only,This is a new indicator the agency began reporting in 2009.</DESCRIPTION>
<PERIOD_YEAR>2018</PERIOD_YEAR>
<PERIOD_MONTH>12</PERIOD_MONTH>
<CATEGORY>Service Indicator</CATEGORY>
<FREQUENCY>M</FREQUENCY>
<DESIRED_CHANGE>U</DESIRED_CHANGE>
<INDICATOR_UNIT>%</INDICATOR_UNIT>
<DECIMAL_PLACES>1</DECIMAL_PLACES>
<YTD_TARGET>97.00</YTD_TARGET>
<YTD_ACTUAL></YTD_ACTUAL>
<MONTHLY_TARGET>97.00</MONTHLY_TARGET>
<MONTHLY_ACTUAL></MONTHLY_ACTUAL>
</INDICATOR>
In [6]: from lxml import objectify
In [7]: path='test002.xml'
In [8]: parsed=objectify.parse(open(path))
In [9]: root=parsed.getroot()
對於每條記錄,我們可以用標記嗎(如YTD_ACTUAL)和數據值填充一個字典(排除幾個標記):
In [17]:data=[]
In [18]:skip_fields=['PARENT_SEQ','INDICATOR_SEQ','DESIRED_CHANGE','DECIMAL_PL
ACES']
In [19]:for elt in root:
el_data={}
for cd in elt.getchildren():
if cd.tag in skip_fields:
continue
el_data[cd.tag]=cd.pyval
data.append(el_data)
最后將這組字典轉換為一個DataFrame:
In [23]: from pandas import DataFrame
In [24]: perf=DataFrame(data)
In [25]: perf
Out[25]:
AGENCY_NAME CATEGORY ... YTD_ACTUAL YTD_TARGET
0 Metro-nORTH Railroad Service Indicator ................... 97.0
1 Metro-nORTH Railroad Service Indicator ... ............... 97.0
[2 rows x 11 columns]
XML數據可以比本例復雜得多。每個標記都可以有元數據。看看下面的這個HTML的鏈接標記(它也算是一段有效的XML):
In [2]: from io import StringIO
In [3]: tag='<a href="http://www.baidu.com">Baidu</a>'
In [5]: from lxml import objectify
In [6]: root=objectify.parse(StringIO(tag)).getroot()
現在就可以訪問鏈接文本或標記中的任何字段了(如href):
In [7]: root
Out[7]: <Element a at 0x4c93348>
In [8]: root.get('href')
Out[8]: 'http://www.baidu.com'
In [9]: root.text
Out[9]: 'Baidu'