进行数据可视化处理的前提是有要处理的数据,这些数据是从哪儿来的呢?一些是个人、机构或组织提供的,一些需要大家从网络中收集归纳。在本章的内容中,将详细讲解使用Python语言采集数据的知识,为读者步入本书后面知识的学习打下基础。
1.1 处理网络数据
互联网改变了人们的生活方式,极大的方便了们生活中的方方面面,我们要分析的数据绝大多数来自网络。在本节的内容中,将详细讲解使用网络库处理网络数据的基本知识。
1.1.1 解析HTML和XML数据
1. 使用内置库解析XML数据
在Python应用程序中,有两种常见的XML编程接口,分别是SAX和DOM。所以与之对应的是,Python语言有两种解析XML文件的方法,分别是SAX和DOM方法。其中库xml由如下核心模块构成。
- xml.etree.ElementTree:提供了处理ElementTree的成员,是一个轻量级的XML处理器;
- xml.dom:用于定义DOM的API,提供了处理 DOM标记的成员。
- xml.dom.minidom:提供了处理最小DOM的成员;
- xml.dom.pulldom:提供了构建部分DOM树的成员;
- xml.sax:提供了处理SAX2的基类和方法成员;
- xml.parsers.expat:绑定了Expat解析器的功能,能够使用注册处理器处理不同XML文档部分。
例如在下面的实例代码中,演示了使用库xml.etree.ElementTree读取XML文件的过程。其中XML文件test.xml的具体实现代码如下所示。
源码路径:codes\1\1-1\test.xml
<students>
<student name='赵敏' sex='男' age='35'/>
<student name='周芷若' sex='男' age='38'/>
<student name='小昭' sex='女' age='22'/>
</students>
例如在下面的实例代码中,演示了使用SAX方法解析XML文件的过程。其中实例文件movies.xml是一个基本的XML文件,在里面保存一些和电影有关的资料信息。文件movies.xml的具体实现代码如下所示。
源码路径:codes\1\1-1\movies.xml
<collection shelf="Root">
<movie title="深入敌后">
<type>War, Thriller</type>
<format>DVD</format>
<year>2003</year>
<rating>三星</rating>
<stars>10</stars>
<description>战争故事</description>
</movie>
<movie title="变形金刚">
<type>Anime, Science Fiction</type>
<format>DVD</format>
<year>1989</year>
<rating>五星</rating>
<stars>8</stars>
<description>科幻片</description>
</movie>
<movie title="枪神">
<type>Anime, Action</type>
<format>DVD</format>
<episodes>4</episodes>
<rating>四星</rating>
<stars>10</stars>
<description>警匪片</description>
</movie>
<movie title="伊师塔">
<type>Comedy</type>
<format>VHS</format>
<rating>五星</rating>
<stars>2</stars>
<description>希腊神话</description>
</movie>
</collection>
实例文件sax.py的功能是解析文件movies.xml的内容,具体实现代码如下所示。
源码路径:codes\1\1-1\sax.py
import xml.sax
class MovieHandler( xml.sax.ContentHandler ):
def __init__(self):
self.CurrentData = ""
self.type = ""
self.format = ""
self.year = ""
self.rating = ""
self.stars = ""
self.description = ""
# 元素开始调用
def startElement(self, tag, attributes):
self.CurrentData = tag
if tag == "movie":
print ("*****Movie*****")
title = attributes["title"]
print ("Title:", title)
# 元素结束调用
def endElement(self, tag):
if self.CurrentData == "type": #处理XML中的type元素
print ("Type:", self.type)
elif self.CurrentData == "format": #处理XML中的format元素
print ("Format:", self.format)
elif self.CurrentData == "year": #处理XML中的year元素
print ("Year:", self.year)
elif self.CurrentData == "rating": #处理XML中的rating元素
print ("Rating:", self.rating)
elif self.CurrentData == "stars": #处理XML中的stars元素
print ("Stars:", self.stars)
elif self.CurrentData == "description": #处理XML中的description元素
print ("Description:", self.description)
self.CurrentData = ""
# 读取字符时调用
def characters(self, content):
if self.CurrentData == "type":
self.type = content
elif self.CurrentData == "format":
self.format = content
elif self.CurrentData == "year":
self.year = content
elif self.CurrentData == "rating":
self.rating = content
elif self.CurrentData == "stars":
self.stars = content
elif self.CurrentData == "description":
self.description = content
if ( __name__ == "__main__"):
# 创建一个XMLReader
parser = xml.sax.make_parser()
# turn off namespaces
parser.setFeature(xml.sax.handler.feature_namespaces, 0)
# 重写ContentHandler
Handler = MovieHandler()
parser.setContentHandler( Handler )
parser.parse("movies.xml")
执行后的效果如图1-1所示。
图1-1 执行效果
例如在下面的实例文件dom.py中,演示了使用DOM方法解析XML文件的过程。实例文件dom.py的功能是解析文件movies.xml的内容,具体实现代码如下所示。
源码路径:codes\1\1-1\dom.py
from xml.dom.minidom import parse
import xml.dom.minidom
# 使用minidom.parse解析器XML 文件
DOMTree = xml.dom.minidom.parse("movies.xml")
collection = DOMTree.documentElement
if collection.hasAttribute("shelf"):
print ("根元素是 : %s" % collection.getAttribute("shelf"))
# 在集合中获取所有电影
movies = collection.getElementsByTagName("movie")
# 打印每部电影的详细信息
for movie in movies:
print ("*****Movie*****")
if movie.hasAttribute("title"):
print ("Title电影名: %s" % movie.getAttribute("title"))
type = movie.getElementsByTagName('type')[0]
print ("Type电影类型: %s" % type.childNodes[0].data)
format = movie.getElementsByTagName('format')[0]
print ("Format电影格式: %s" % format.childNodes[0].data)
rating = movie.getElementsByTagName('rating')[0]
print ("Rating电影评分: %s" % rating.childNodes[0].data)
description = movie.getElementsByTagName('description')[0]
print ("Description电影简介: %s" % description.childNodes[0].data)
执行后会输出:
根元素是 : Root
*****Movie*****
Title电影名: 深入敌后
Type电影类型: War, Thriller
Format电影格式: DVD
Rating电影评分: 三星
Description电影简介: 战争故事
*****Movie*****
Title电影名: 变形金刚
Type电影类型: Anime, Science Fiction
Format电影格式: DVD
Rating电影评分: 五星
Description电影简介: 科幻片
*****Movie*****
Title电影名: 枪神
Type电影类型: Anime, Action
Format电影格式: DVD
Rating电影评分: 四星
Description电影简介: 警匪片
*****Movie*****
Title电影名: 伊师塔
Type电影类型: Comedy
Format电影格式: VHS
Rating电影评分: 五星
Description电影简介: 希腊神话
2. 使用库Beautiful Soup解析网络数据
Beautiful Soup是一个重要的Python库,其功能是将HTML和XML文件的标签信息解析成树形结构,然后提取HTML或XML文件中指定标签属性对应的数据。库Beautiful Soup经常被用在爬虫项目中,通过使用库Beautiful Soup可以大大提高开发效率。
Beautiful Soup 3目前已经停止开发,其官方推荐使用Beautiful Soup 4,本书讲解的是Beautiful Soup 4。开发者可以使用如下两种命令安装库Beautiful Soup:
pip install beautifulsoup4
easy_install beautifulsoup4
在安装Beautiful Soup 4后还需要安装文件解析器,Beautiful Soup不但支持Python标准库中的HTML解析器,而且还支持第三方的解析器(例如lxml)。根据开发者所用操作系统的不同,可以使用如下命令来安装lxml:
$ apt-get install Python-lxml
$ easy_install lxml
$ pip install lxml
例如在下面的实例文件bs01.py中,演示了使用库Beautiful Soup解析HTML代码的过程。
源码路径:codes\1\1-1\bs01.py
from bs4 import BeautifulSoup
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>睡鼠的故事</b></p>
<p class="story">在很久以前有三个可爱的小熊宝宝,名字分别是
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>和
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
soup = BeautifulSoup(html_doc,"lxml")
print(soup)
通过上述代码,解析了html_doc中的HTML代码,执行后会输出解析结果:
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>睡鼠的故事</b></p>
<p class="story">在很久以前有三个可爱的小熊宝宝,名字分别是
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>和
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
</body></html>
在下面的实例文件bs02.py中,演示了使用库Beautiful Soup解析指定HTML标签的过程。
源码路径:codes\1\1-1\bs02.py
from bs4 import BeautifulSoup
html = '''
<html><head><title>睡鼠的故事</title></head>
<body>
<p class="title"><b>睡鼠的故事</b></p>
<p class="story">O在很久以前有三个可爱的小熊宝宝,名字分别是
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> 和
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
他们快乐的住在大森林里</p>
<p class="story">...</p>
'''
soup = BeautifulSoup(html,'lxml')
print(soup.title)
print(soup.title.name)
print(soup.title.string)
print(soup.title.parent.name)
print(soup.p)
print(soup.p["class"])
print(soup.a)
print(soup.find_all('a'))
print(soup.find(id='link3'))
执行后将输出指定标签的信息:
<title>睡鼠的故事</title>
title
睡鼠的故事
head
<p class="title"><b>睡鼠的故事</b></p>
['title']
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
3. 使用库bleach过滤数据
在使用Python开发Web程序时,开发者面临一个十分重要的安全性问题:跨站脚本注入攻击(黑客利用网站漏洞从用户一端盗取重要信息)。为了解决跨站脚本注入攻击漏洞,最常用的做法是设置一个访问白名单,设置只显示指定的HTML标签和属性。在现实应用中,最常用的HTML过滤库是Bleach,能够实现基于白名单的HTML清理和文本链接模块。
我们可以使用如下两种命令安装库bleach:
pip install bleach
easy_install bleach
例如在下面的实例文件guolv.py中,演示了使用方法bleach.clean()滤处理HTML标签的过程。
源码路径:codes\1\1-1\guolv.py
import bleach
# tag参数示例
print(bleach.clean(
u'<b><i>例子1</i></b>',
tags=['b'],
))
# attributes为list示例
print(bleach.clean(
u'<p class="foo" style="color: red; font-weight: bold;">例子2</p>',
tags=['p'],
attributes=['style'],
styles=['color'],
))
# attributes为dict示例
attrs = {
'*': ['class'],
'a': ['href', 'rel'],
'img': ['alt'],
}
print(bleach.clean(
u'<img alt="an example" width=500>例子3',
tags=['img'],
attributes=attrs
))
# attributes为function示例
def allow_h(tag, name, value):
return name[0] == 'h'
print(bleach.clean(
u'<a href="http://example.com" title="link">例子4</a>',
tags=['a'],
attributes=allow_h,
))
# style参数示例
tags = ['p', 'em', 'strong']
attrs = {
'*': ['style']
}
styles = ['color', 'font-weight']
print(bleach.clean(
u'<p style="font-weight: heavy;">例子5</p>',
tags=tags,
attributes=attrs,
styles=styles
))
# protocol参数示例
print(bleach.clean(
'<a href="smb://more_text">例子6</a>',
protocols=['http', 'https', 'smb']
))
print(bleach.clean(
'<a href="smb://more_text">例子7</a>',
protocols=bleach.ALLOWED_PROTOCOLS + ['smb']
))
#strip参数示例
print(bleach.clean('<span>例子8</span>'))
print(bleach.clean('<b><span>例子9</span></b>', tags=['b']))
print(bleach.clean('<span>例子10</span>', strip=True))
print(bleach.clean('<b><span>例子11</span></b>', tags=['b'], strip=True))
# strip_comments参数示例
html = 'my<!-- commented --> html'
print(bleach.clean(html))
print(bleach.clean(html, strip_comments=False))
执行后会输出:
<b><i>例子1</i></b>
<p style="color: red;">例子2</p>
<img alt="an example">例子3
<a href="http://example.com">例子4</a>
<p style="font-weight: heavy;">例子5</p>
<a href="smb://more_text">例子6</a>
<a href="smb://more_text">例子7</a>
<span>例子8</span>
<b><span>例子9</span></b>
例子10
<b>例子11</b>
my html
my<!-- commented --> html
4. 使用库html5lib解析网络数据
在Python程序中,可以使用库html5lib解析HTML文件。库html5lib是用纯Python语言编写实现的,其解析方式与浏览器相同。在本章前面讲解的库Beautiful Soup,使用的是lxml解析器,而库html5lib是Beautiful Soup支持的另一种解析器。
我们可以使用如下两种命令安装库html5lib:
pip install html5lib
easy_install html5lib
例如在下面的实例文件ht501.py中,演示了使用html5lib解析HTML文件的过程。
源码路径:codes\1\1-1\ht501.py
<html><head><title>睡鼠的故事</title></head>
<body>
<p class="title"><b>睡鼠的故事</b></p>
<p class="story">在很久以前有三个可爱的小熊宝宝,名字分别是
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>和
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
他们快乐的生活在大森林里.</p>
<p class="story">...</p>
</body></html>
5. 使用库MarkupSafe解析数据
在Python程序中,使用库MarkupSafe可以将具有特殊含义的字符将替换掉,这样可以减轻注入攻击(把用户输入、提交的数据当做代码来执行),能够将不受信任的用户输入的信息安全地显示在页面上。
我们可以使用如下两种命令安装库MarkupSafe:
pip install MarkupSafe
easy_install MarkupSafe
例如在下面的实例文件mark01.py中,演示了使用库MarkupSafe构建安全HTML的过程。
源码路径:codes\1\1-1\mark01.py
from markupsafe import Markup, escape
#实现支持HTML字符串的Unicode子类
print(escape("<script>alert(document.cookie);</script>"))
tmpl = Markup("<em>%s</em>")
print(tmpl % "Peter > Lustig")
#可以通过重写__html__功能自定义等效HTML标记
class Foo(object):
def __html__(self):
return '<strong>Nice</strong>'
print(escape(Foo()))
print(Markup(Foo()))
执行后会输出:
<script>alert(document.cookie);</script>
<em>Peter > Lustig</em>
<strong>Nice</strong>
<strong>Nice</strong>