分享

Python爬虫从入门到精通(四)提取网页中的信息

 zhulin1028 2021-12-29

一、数据的类型

网页中数据的类型简单来说可以分成以下三类:

1、结构化数据

可以用统一的结构加以表示的数据。可以使用关系型数据库表示和存储,表现为二维形式的数据。一般特点是:数据以行为单位,一行数据表示一个实体的信息,每一行数据的属性是相同的。

比如MySQL数据库表中的数据:

id          name       age      gender

aid1        马化腾      46       male

aid2        马云        53       male

aid3        李彦宏      49       male

2、半结构化数据

是结构化数据的一种形式,并不符合关系型数据库或其他数据表的形式关联起来的数据模型结构,但包含相关标记,用来分隔语义元素以及对记录和字段进行分层。因此,它也被称为自描述的结构。常见的半结构数据有HTML,XML和JSON等,实际上是以树或者图的结构来存储的。

比如,一个简单的XML表示:

<person>

    <name>A</name>

<age>13</age>

<class>aid1710</class>

    <gender>female</gender>

</person>

或者

<person>

    <name>B</name>

    <gender>male</gender>

</person>

结点中属性的顺序是不重要的,不同的半结构化数据的属性的个数是不一定一样的。这样的数据格式,可以自由地表达很多有用的信息,包括自描述信息(元数据)。所以,半结构化数据的扩展性很好,特别适合于在互联网中大规模传播。

3、非结构化数据

就是没有固定结构的数据。各种文档、图片、视频/音频等都属于非结构化数据。对于这类数据,我们一般直接整体进行存储,而且一般存储为二进制的数据格式;

除了结构化和半结构数据之外的数据都是非结构化数据。

二、关于XML,HTML,DOM和JSON文件

1、XML, HTML, DOM

XML即Extentsible Markup Language(可扩展标记语言),是用来定义其它语言的一种元语言,其前身是SGML(标准通用标记语言)。它没有标签集(tagset),也没有语法规则(grammatical rule),但是它有句法规则(syntax rule)。任何XML文档对任何类型的应用以及正确的解析都必须是良构的(well-formed),即每一个打开的标签都必须有匹配的结束标签,不得含有次序颠倒的标签,并且在语句构成上应符合技术规范的要求。XML文档可以是有效的(valid),但并非一定要求有效。所谓有效文档是指其符合其文档类型定义(DTD)的文档。如果一个文档符合一个模式(schema)的规定,那么这个文档是模式有效的(schema valid)。

HTML(Hyper Text Mark-up Language)即超文本标记语言,是WWW的描述语言。HTML与XML的区别与联系:

  XML和HTML都是用于操作数据或数据结构,在结构上大致是相同的,但它们在本质上却存在着明显的区别。综合网上的各种资料总结如下。

(一)语法要求不同:

1. 在HTML中不区分大小写,在XML中严格区分。

2. 在HTML中,有时不严格,如果上下文清楚地显示出段落或者列表键在何处结尾,那么你可以省略</p>或者</li>之类的结束标记。在XML中,是严格的树状结构,绝对不能省略掉结束标记。

3. 在XML中,拥有单个标记而没有匹配的结束标记的元素必须用一个/ 字符作为结尾。这样分析器就知道不用查找结束标记了。

4. 在XML中,属性值必须分装在引号中。在HTML中,引号是可用可不用的。

5. 在HTML中,可以拥有不带值的属性名。在XML中,所有的属性都必须带有相应的值。

6. 在XML文档中,空白部分不会被解析器自动删除; 但是html是过滤掉空格的。

XML的语法要求比HTML严格。

(二)标记不同:

1. HTML使用固有的标记; 而XML没有固有的标记。

2. HTML标签是预定义的; XML标签是免费的、自定义的、可扩展的。

(三)作用不同:

1. HTML是用来显示数据的; XML是用来描述数据、存放数据的,所以可以作为持久化的介质。HTML将数据和显示结合在一起,在页面中把这数据显示出来;xml则将数据和显示分开。 XML被设计用来描述数据,其焦点是数据的内容。HTML被设计用来显示数据,其焦点是数据的外观。

2. XML不是HTML的替代品,XML和HTML是两种不同用途的语言。 XML 不是要替换 HTML;实际上XML 可以视作对 HTML 的补充。XML 和HTML 的目标不同HTML 的设计目标是显示数据并集中于数据外观,而XML的设计目标是描述数据并集中于数据的内容。

3. 没有任何行为的XML, 与HTML 相似, XML不进行任何操作(共同点)。

4. 对于XML最好的形容可能是: XML是一种跨平台的,与软、硬件无关的,处理与传输信息的工具。

5. XML未来将会无所不在,XML将成为最普遍的数据处理和数据传输的工具。

关于DOM:

文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标志语言的标准编程接口。在网页上,组织页面(或文档)的对象被组织在一个树形结构中,用来表示文档中对象的标准模型就称为DOM。Document Object Model的历史可以追溯至1990年代后期微软与Netscape的“浏览器大战”,双方为了在JavaScript与JScript一决生死,于是大规模的赋予浏览器强大的功能。微软在网页技术上加入了不少专属事物,既有VBScript、ActiveX、以及微软自家的DHTML格式等,使不少网页使用非微软平台及浏览器无法正常显示。DOM即是当时蕴酿出来的杰作。

DOM= Document Object Model,文档对象模型,DOM可以以一种独立于平台和语言的方式访问和修改一个文档的内容和结构。换句话说,这是表示和处理一个HTML或XML文档的常用方法。DOM很重要,DOM的设计是以对象管理组织(OMG)的规约为基础的,因此可以用于任何编程语言。最初人们把它认为是一种让JavaScript在浏览器间可移植的方法,不过DOM的应用已经远远超出这个范围。DOM技术使得用户页面可以动态地变化,如可以动态地显示或隐藏一个元素,改变它们的属性,增加一个元素等,DOM技术使得页面的交互性大大地增强。

DOM实际上是以面向对象方式描述的文档模型。DOM定义了表示和修改文档所需的对象、这些对象的行为和属性以及这些对象之间的关系。可以把DOM认为是页面上数据和结构的一个树形表示,不过页面当然可能并不是以这种树的方式具体实现。

通过 JavaScript,您可以重构整个 HTML 文档。您可以添加、移除、改变或重排页面上的项目。要改变页面的某个东西,JavaScript 就需要获得对 HTML 文档中所有元素进行访问的入口。这个入口,连同对 HTML 元素进行添加、移动、改变或移除的方法和属性,都是通过文档对象模型来获得的(DOM)。

2、JSON文件

JSON(JavaScript Object Notation, JS对象标记) 是一种轻量级的数据交换格式。它基于 ECMAScript (w3c制定的JS规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

JSON 语法规则:

在JS 语言中,一切都是对象。因此,任何支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。

但是对象和数组是比较特殊且常用的两种类型:

                        1.对象表示为键值对

                       2.数据由逗号分隔

                       3.花括号保存对象

                       4.方括号保存数组

JSON键值对是用来保存 JS 对象的一种方式,和 JS 对象的写法也大同小异,

键/值对组合中的键名写在前面并用双引号 "" 包裹,使用冒号 : 分隔,然后紧接着值

{"firstName": "Json","class":"aid1710"}

这很容易理解,等价于这条 JavaScript 语句:

{firstName : "Json","class":"aid1710"}

JSON与JS对象的关系:

很多人搞不清楚 JSON 和JS对象的关系,甚至连谁是谁都不清楚。其实,可以这么理解:JSON是 JS对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。

如:

var obj = {a: 'Hello', b: 'World'}; //这是一个对象,注意键名也是可以使用引号包裹的

var json = '{"a": "Hello", "b": "World"}'; //这是一个 JSON 字符串,本质是一个字符串。

Python中关于JSON的操作简单演示:

代码示例见 josnTest.py

JSON和XML的比较:

1.可读性:

JSON和XML的可读性可谓不相上下,一边是简易的语法,一边是规范的标签形式,很难分出胜负。

2.可扩展性:

XML天生有很好的扩展性,JSON当然也有,没有什么是XML可以扩展而JSON却不能扩展的。不过JSON在Javascript主场作战,可以存储Javascript复合对象,有着xml不可比拟的优势。

3.编码难度:

XML有丰富的编码工具,比如Dom4j、JDom等,JSON也有提供的工具。无工具的情况下,相信熟练的开发人员一样能很快的写出想要的xml文档和JSON字符串,不过,xml文档要多很多结构上的字符。

4.解码难度

XML的解析方式有两种:

一是通过文档模型解析,也就是通过父标签索引出一组标记。例如:xmlData.getElementsByTagName("tagName"),但是这样是要在预先知道文档结构的情况下使用,无法进行通用的封装。

       另外一种方法是遍历节点(document 以及 childNodes)。这个可以通过递归来实现,不过解析出来的数据仍旧是形式各异,往往也不能满足预先的要求。凡是这样可扩展的结构数据解析起来一定都很困难。JSON也同样如此。如果预先知道JSON结构的情况下,使用JSON进行数据传递简直是太美妙了,可以写出很实用美观可读性强的代码。

如果你是纯粹的前台开发人员,一定会非常喜欢JSON。但是如果你是一个应用开发人员,就不是那么喜欢了,毕竟xml才是真正的结构化标记语言,用于进行数据传递。而如果不知道JSON的结构而去解析JSON的话,那简直是噩梦。费时费力不说,代码也会变得冗余拖沓,得到的结果也不尽人意。

但是这样也不影响众多前台开发人员选择JSON。因为json.js中的toJSONString()就可以看到JSON的字符串结构。当然不是使用这个字符串,这样仍旧是噩梦。常用JSON的人看到这个字符串之后,就对JSON的结构很明了了,就更容易的操作JSON。以上是在Javascript中仅对于数据传递的xml与JSON的解析。

在Javascript地盘内,JSON毕竟是主场作战,其优势当然要远远优越于xml。

如果JSON中存储Javascript复合对象,而且不知道其结构的话,相信很多程序员也一样是哭着解析JSON的。除了上述之外,JSON和XML还有另外一个很大的区别在于有效数据率。JSON作为数据包格式传输的时候具有更高的效率,这是因为JSON不像XML那样需要有严格的闭合标签,这就让有效数据量与总数据包比大大提升,从而减少同等数据流量的情况下,网络的传输压力。

实例比较:

XML和JSON都使用结构化方法来标记数据,下面来做一个简单的比较。

用XML表示中国部分省市数据如下:

<?xml version="1.0" encoding="utf-8"?>

<country>

    <name>中国</name>

    <province>

        <name>黑龙江</name>

        <cities>

            <city>哈尔滨</city>

            <city>大庆</city>

        </cities>

    </province>

    <province>

        <name>广东</name>

        <cities>

            <city>广州</city>

            <city>深圳</city>

            <city>珠海</city>

        </cities>

    </province>

    <province>

        <name>台湾</name>

        <cities>

            <city>台北</city>

            <city>高雄</city>

        </cities>

    </province>

    <province>

        <name>新疆</name>

        <cities>

            <city>乌鲁木齐</city>

        </cities>

    </province>

</country>

用JSON表示如下:

{

    "name": "中国",

    "province": [{

        "name": "黑龙江",

        "cities": {

            "city": ["哈尔滨", "大庆"]

        }

    }, {

        "name": "广东",

        "cities": {

            "city": ["广州", "深圳", "珠海"]

        }

    }, {

        "name": "台湾",

        "cities": {

            "city": ["台北", "高雄"]

        }

    }, {

        "name": "新疆",

        "cities": {

            "city": ["乌鲁木齐"]

        }

    }]

}

可以看到:JSON简单的语法格式和清晰的层次结构明显要比XML容易阅读,并且在数据交换方面,由于JSON所使用的字符要比XML少得多,可以大大得节约传输数据所占用得带宽。

三、 怎么提取网页中的信息

1、 XPath与lxml

XPath是一门在XML文档中查找信息的语言,对XPath的理解是很多高级XML应用的基础,XPath在XML中通过元素和属性进行导航。

lxml是一个用来处理XML的第三方 Python 库,它在底层封装了用 C 语言编写的 libxml2和libxslt,并以简单强大的Python API,兼容并加强了著名的Element Tree API。

安装:pip install lxml

使用:from lxml import etree

1.      XPath术语:

在XPath语境中,XML 文档被视作节点树,节点树的根节点也被称作文档节点。 XPath 将节点树中的节点(Node)分为七类:元素(Element),属性(Attribute),文本(Text),命名空间(Namespace),处理指令(Processing-instruction),注释(Comment)和文档节点(Document nodes)。

看一下 XML 文档例子:

<?xml version="1.0" encoding="ISO-8859-1"?>

<bookstore>

<book>

  <title lang="en">Harry Potter</title>

  <author>J K. Rowling</author>

  <year>2005</year>

  <price>29.99</price>

</book>

</bookstore>

  以上的XML文档中:

                            <bookstore> (这是一个“根”)

                            <author>J K. Rowling</author> (这是一个“元素”)

                            lang="en" (这是一个“属性”)

  从另一个视角来看它:

bookstore                      (根)

book                         (元素)

title                        (元素)

           lang  = en                   (属性)

           text = Harry Potter          (文本)

           author                       (元素)

           text = J K. Rowling          (文本)

           year                         (元素)

           text = 2005                  (文本)

           price                        (元素)

       text = 29.99                 (文本)

2. 节点之间的关系

父(Parent):每个元素都肯定有一个父节点,最顶层的元素父亲是根节点。同理每个属性必然有一个父,它们的父是元素。 上例XML文档中,根bookstore是元素 book 的父节点,book是元素title, author, year, price 的父节点,title是lang 的父节点。

子(Children):元素可以有零或多个子。上例XML文档中,title, author, year, price是book的子节点。

同胞(Sibling):父节点相同的节点之间互为同胞,也称彼此的兄弟节点。上例XM文档中,title, author, year, price 彼此互为同胞。

先辈(Ancestor):某节点的父节点、父的父,以此类推一直追溯至根节点之间所有节点。上例XM文档中,title, author, year, price 的先辈就是 book, bookstore。

后代(Descendant):某节点的子节点、子的子,以此类推至最后一个子节点之间所有节点。上例XM文档中,bookstore 的后代就是 title, author, year, price 。

3.选取节点

以下为基本路径的表达方式,记住XPath的路径表达式都是基于某个节点之上的,例如最初的当前节点一般是根节点,这与Linux下路径切换原理是一样的。

表达式描述:

nodename       选取已匹配节点下名为 nodename 的子元素节点

/             如果以 / 开头,表示从根节点作为选取起点。

//            在已匹配节点后代中选取节点,不考虑目标节点的位置。

.              选取当前节点。

..            选取当前节点的父元素节点。

@           选取属性。

4.通配符

*          匹配任何元素。

@*        匹配任何属性。

node()      匹配任何类型的节点。

5.预判(Predicates)或 条件选取

预判是用来查找某个特定的节点或者符合某种条件的节点,预判表达式位于方括号中。使用 “|” 运算符,你可以选取符合“或”条件的若干路径。

具体例子见下面代码lxmlTest.py。

6.坐标轴

XPath 坐标轴:坐标轴用于定义当对当前节点的节点集合。

坐标轴名称          含义

ancestor                    选取当前节点的所有先辈元素及根节点。

ancestor-or-self            选取当前节点的所有先辈以及当前节点本身。

attibute              选取当前节点的所有属性。

child                      选取当前节点的所有子元素。

descendant               选取当前节点的所有后代元素。

descendant-or-self             选取当前节点的所有后代元素以及当前节点本身。

following                    选取文档中当前节点的结束标签之后的所有节点。

following-sibling         选取当前节点之后的所有同级节点。

namespace               选取当前节点的所有命名空间节点。

parent                  选取当前节点的父节点。

preceding                  选取当前节点的开始标签之前的所有节点。

preceding-sibling       选取当前节点之前的所有同级节点。

self                            选取当前节点。

7.位置路径的表达式

位置路径可以是绝对路径,也可以是相对路径。绝对路径以 “/” 开头。每条路径包括一个或多个步,每步之间以“/”分隔。

    绝对路径:/step/step/…

    相对路径:step/step/…

每步根据当前节点集合中的节点计算。

步(step)包括三部分:

    坐标轴(axis):      定义所选节点与当前节点之间的关系。

    节点测试(node-test):识别某个坐标轴内部的节点。

    预判(predicate):    提出预判条件对节点集合进行筛选。

步的语法:坐标轴::节点测试[预判]

2、 BeautifulSoup4

Beautiful Soup是用Python写的一个HTML/XML的解析器,它可以很好的处理不规范标记并生成剖析树(parse tree)。 它提供简单又常用的导航(navigating),搜索以及修改剖析树的操作。它可以大大节省你的编程时间。

安装:(sudo) pip install beautifuilsoup4

使用:

在程序中导入 Beautiful Soup库:

from BeautifulSoup import BeautifulSoup          # For processing HTML

from BeautifulSoup import BeautifulStoneSoup     # For processing XML

import BeautifulSoup                             # To get everything
# 代码例子

from bs4 import BeautifulSoup

import re

doc = ['<html><head><title>Page title</title></head>',

       '<body><p id="firstpara" align="center">This is paragraph <b>one</b>.',

       '<p id="secondpara" align="blah">This is paragraph <b>two</b>.',

       '</html>']

soup = BeautifulSoup(''.join(doc))

print soup.prettify()

定位某些 soup元素很简单,比如上例:

soup.contents[0].name

# u'html'



soup.contents[0].contents[0].name

# u'head'



head = soup.contents[0].contents[0]

head.parent.name

# u'html'



head.next

# <title>Page title</title>



head.nextSibling.name

# u'body'



head.nextSibling.contents[0]

# <p id="firstpara" align="center">This is paragraph <b>one</b>.</p>



head.nextSibling.contents[0].nextSibling

# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>

    也可以利用soup,获得特定标签或有着特定属性的标签,修改soup也很简单;

BS4 与 lxml的比较:

lxml    C实现,只会局部遍历,快;        复杂,语法不太友好;

BS4     Python实现,会加载整个文档,慢; 简单,API人性化;

3、正则表达式re

被用来检索\替换那些符合某个模式(规则)的文本,对于文本过滤或规则匹配,最强大的就是正则表达式,是python爬虫里必不可少的神兵利器。

基本匹配规则:

[0-9] 任意一个数字,等价\d

[a-z] 任意一个小写字母

[A-Z]任意一个大写字母

[^0-9] 匹配非数字,等价\D

\w 等价[a-z0-9_],字母数字下划线

\W 等价对\w取非

. 任意字符

[] 匹配内部任意字符或子表达式

[^] 对字符集合取非

* 匹配前面的字符或者子表达式0次或多次

+ 匹配前一个字符至少1次

? 匹配前一个字符0次或多次

^ 匹配字符串开头

$ 匹配字符串结束

Python使用正则表达式

Python的re模块

pattern 编译好的正则表达式

几个重要的方法:

match: 匹配一次从开头;

search: 匹配一次,从某位置;

findall: 匹配所有;

split: 分隔;

sub: 替换;

需要注意的两种模式:

贪婪模式:(.*)

懒惰模式:(.*?)

3. 用正则表达式实现下面的效果:

把 i=d%0A&from=AUTO&to=AUTO&smartresult=dict

转换成下面的形式:

i:d%0A

from:AUTO

to:AUTO

smartresult:dict

总结:正则,BS,lxml的比较

 

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多