Python 網路抓取和文字挖掘-2 XML 和 JSON
XML和JSON是兩個重要的網路資料交換標準。
1. XML (eXtensible Markup language, 可擴充套件標記語言)
以下是書中附帶的一個XML示例文件。
<?xml version="1.0" encoding="ISO-8859-1"?> <bond_movies> <movie id="1"> <name>Dr. No</name> <year>1962</year> <actors bond="Sean Connery" villain="Joseph Wiseman"/> <budget>1.1M</budget> <boxoffice>59.5M</boxoffice> </movie> <movie id="2"> <name>Live and Let Die</name> <year>1973</year> <actors bond="Roger Moore" villain="Yaphet Kotto"/> <budget>7M</budget> <boxoffice>126.4M</boxoffice> </movie> <movie id="3"> <name>Skyfall</name> <year>2012</year> <actors bond="Daniel Craig" villain="Javier Bardem"/> <budget>175M</budget> <boxoffice>1108.6M</boxoffice> </movie> </bond_movies>
XML是純文字格式,XML有一套語法規則和關機元素,一個XML文件永遠 以宣告XML文件的一行程式碼開頭。
XML檔案必須有且僅有一個根元素,它包裹整個文件,如例子裡的根元素<bond_movies> </bond_movies>。
一個XML元素包括標籤和內容,標籤包括起始標籤和終止標籤,起始標籤中和可以包含屬性,屬性和內容一樣可以用來存放資料(資訊),XML的屬性值必須加引號 ;起始標籤也可以用一個/,自己閉合,即不用終止標籤;XML可以自己選擇元素的名字,只要符合命名規則就可以(不能以數字或xml開頭 、不能包括空格、區分大小寫);內容中遇到特殊字元,須使用預先定義的轉義字串來代替,但如果資料中需要轉義的字元太多時 ,可放到<! [CDATA[ ]] > 部件中,這樣就不會被解析。。
一個XML檔案是一個有層次的樹形結構,元素裡還可以包含其它元素;元素可以巢狀,但必須是嚴格巢狀,不允許交叉巢狀。
XML文件結構可以由使用者自定義(標籤名和層次結構的深度),使用者可用文件型別定義(DTD)來描述;由於XML是可自定義的,就可能存在同樣的元素名用於表達不同內容的情況,處理這種情況需要用到名稱空間技術。
2. Python 解析XML檔案
1) DOM
定義一個類Movie存放電影資訊,與例子中xml檔案相對應。用xml.dom.minidom對文件進行解析,用到的方法包括:parse、hasChildNodes、getAttribute、getElementsBuTagName和data屬性。需要注意的是要用nodeType==Node.ELEMENT_NODE進行判斷過濾掉Text node。
class Movie(object):
def __init__(self, id=None, name=None, year=None, actors=None, budget=None, boxoffice=None):
self.__id__ = id
self.__name__ = name
self.__year__ = year
self.__actors__ = actors
self.__budget__ = budget
self.__boxoffice__ = boxoffice
def get_id(self):
return self.__id__
def get_name(self):
return self.__name__
def get_year(self):
return self.__year__
def get_actors(self):
return self.__actors__
def get_budget(self):
return self.__budget__
def get_boxoffice(self):
return self.__boxoffice__
def to_txt(self,separator=','):
bond = self.__actors__['bond']
villain = self.__actors__['villain']
line = str(self.__id__) + separator + \
self.__name__ + separator + \
str(self.__year__) + separator + \
self.__budget__ + separator + \
self.__boxoffice__ + separator + \
bond + separator + villain
return line
from xml.dom import minidom as mdom
from xml.dom.minidom import Node
def get_movies_by_dom(xmlfile):
movies = []
doc = mdom.parse(xmlfile)
root = None
if doc.hasChildNodes():
root = doc.childNodes[0]
else:
return movies
for mve in root.childNodes:
if mve.nodeType == Node.ELEMENT_NODE:
id = mve.getAttribute('id')
node = mve.getElementsByTagName("name")
name = node[0].childNodes[0].data
node = mve.getElementsByTagName("year")
year = node[0].childNodes[0].data
node = mve.getElementsByTagName("actors")
bond = node[0].getAttribute('bond')
villain = node[0].getAttribute('villain')
actors = {'bond':bond, 'villain':villain}
node = mve.getElementsByTagName("budget")
budget = node[0].childNodes[0].data
node = mve.getElementsByTagName("boxoffice")
boxoffice = node[0].childNodes[0].data
mv = Movie(id, name, year, actors, budget, boxoffice)
movies.append(mv)
return movies
if __name__ == '__main__':
movies = get_movies_by_dom('bond.xml')
for mv in movies:
print mv.to_txt()
2)SAX
dom解析xml是將整個文件樹載入到記憶體中,因此在解析大檔案時需要考慮使用sax進行解析。需要引入xml.sax中的parse函式和xml.sax.handler中的ContentHandler。MyContentHandler繼承ContentHandler,定義__movies__(列表)、__movie__和__tag_name__三個屬性,記錄解析過程的狀態資訊。當startElemet的節點標籤名為movie時建立一個movie物件,當endElemet的節點標籤名為movie時,表示<movie>...</movie>完成了,將__movie__新增到__movies__列表。
# -*- coding:utf-8 -*-
class Movie(object):
def __init__(self, id=None, name=None, year=None, actors=None, budget=None, boxoffice=None):
self.__id__ = id
self.__name__ = name
self.__year__ = year
self.__actors__ = actors
self.__budget__ = budget
self.__boxoffice__ = boxoffice
def get_id(self):
return self.__id__
def get_name(self):
return self.__name__
def get_year(self):
return self.__year__
def get_actors(self):
return self.__actors__
def get_budget(self):
return self.__budget__
def get_boxoffice(self):
return self.__boxoffice__
def set_id(self, id):
self.__id__ = id
def set_name(self, name):
self.__name__ = name
def set_year(self, year):
self.__year__ = year
def set_actors(self, actors):
self.__actors__ = actors
def set_budget(self, budget):
self.__budget__ = budget
def set_boxoffice(self, boxoffice):
self.__boxoffice__ = boxoffice
def to_txt(self,separator=','):
bond = self.__actors__['bond']
villain = self.__actors__['villain']
line = str(self.__id__) + separator + \
self.__name__ + separator + \
str(self.__year__) + separator + \
self.__budget__ + separator + \
self.__boxoffice__ + separator + \
bond + separator + villain
return line
from xml import sax
from xml.sax.handler import ContentHandler
class MovieContentHandler(ContentHandler):
def __init__(self):
ContentHandler.__init__(self)
self.__movies__ = []
self.__movie__ = None
self.__tag_name__ = None
def get_movies(self):
return self.__movies__
def startDocument(self):
self.__movies__ = []
def endDocument(self):
pass
def startElement(self, name, attrs):
if name == 'movie':
self.__movie__ = Movie()
self.__movie__.set_id(attrs['id'])
elif name == 'actors':
self.__movie__.set_actors({'bond': attrs['bond'], 'villain': attrs['villain']})
self.__tag_name__ = name
def endElement(self, name):
if name == 'movie':
self.__movies__.append(self.__movie__)
self.__movie__ = None
self.__tag_name__ = ''
def characters(self, content):
if self.__tag_name__ == 'name':
self.__movie__.set_name(content)
elif self.__tag_name__ == 'year':
self.__movie__.set_year(content)
elif self.__tag_name__ == 'budget':
self.__movie__.set_budget(content)
elif self.__tag_name__ == 'boxoffice':
self.__movie__.set_boxoffice(content)
if __name__ == '__main__':
mh = MovieContentHandler()
sax.parse('bond.xml', mh)
movies = mh.get_movies()
for movie in movies:
print movie.to_txt()
3)ElementTree
import xml.etree.ElementTree as ET
def get_moives_by_et(xmlfile):
movies = []
doc = ET.parse(xmlfile)
root = doc.getroot()
for child in root:
id = child.attrib['id']
name = child.find('name').text
year = child.find('year').text
actors = child.find('actors').attrib
budget = child.find('budget').text
boxoffice = child.find('boxoffice').text
movie = Movie(id, name, year, actors, budget, boxoffice)
movies.append(movie)
return movies
if __name__ == '__main__':
movies = get_moives_by_et('bond.xml')
for movie in movies:
print movie.to_txt()
3. JSON
JSON是一種源於JavaScript程式語言的資料格式,資料保持在鍵值對(Key/Value pair)裡,鍵和值用冒號(:)隔開,鍵值對用逗號(,)隔開,不同型別的括號(大括號和方括號)能夠描述層級結構。如果用Python的資料結構來描述的話,大括號({})對應字典,中括號([])對應列表。相比與XML,JSON不能添加註釋、不能區分缺失的值和空值、沒有名稱空間、沒有DTD。JSON是一種通用的資料交換標準。JSON在python 操作比較簡單,掌握load、loads、dump和dumps幾個方法就夠用了,要注意的是ensuer_ascii 引數的使用。
import json
if __name__ == '__main__':
f = open('indy.json')
data = json.load(f)
print json.dumps(data, ensure_ascii=False,)
f.close()