1. 程式人生 > >Python 網路抓取和文字挖掘-2 XML 和 JSON

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()