(1-1-01)数据采集:处理网络数据——解析HTML和XML数据

进行数据可视化处理的前提是有要处理的数据,这些数据是从哪儿来的呢?一些是个人、机构或组织提供的,一些需要大家从网络中收集归纳。在本章的内容中,将详细讲解使用Python语言采集数据的知识,为读者步入本书后面知识的学习打下基础。

1.1  处理网络数据

互联网改变了人们的生活方式,极大的方便了们生活中的方方面面,我们要分析的数据绝大多数来自网络。在本节的内容中,将详细讲解使用网络库处理网络数据的基本知识。

1.1.1  解析HTML和XML数据

1. 使用内置库解析XML数据

在Python应用程序中,有两种常见的XML编程接口,分别是SAX和DOM。所以与之对应的是,Python语言有两种解析XML文件的方法,分别是SAX和DOM方法。其中库xml由如下核心模块构成。

  1. xml.etree.ElementTree:提供了处理ElementTree的成员,是一个轻量级的XML处理器;
  2. xml.dom:用于定义DOM的API,提供了处理 DOM标记的成员。
  3. xml.dom.minidom:提供了处理最小DOM的成员;
  4. xml.dom.pulldom:提供了构建部分DOM树的成员;
  5. xml.sax:提供了处理SAX2的基类和方法成员;
  6. 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>&lt;i&gt;例子1&lt;/i&gt;</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>
&lt;span&gt;例子8&lt;/span&gt;
<b>&lt;span&gt;例子9&lt;/span&gt;</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()))

执行后会输出:

&lt;script&gt;alert(document.cookie);&lt;/script&gt;
<em>Peter &gt; Lustig</em>
<strong>Nice</strong>
<strong>Nice</strong>

  • 19
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值