CH2 网络爬虫提取

2019-02-11  本文已影响0人  icey_J

网络爬虫提取

[TOC]

1. Beautiful Soup库入门

bs库的安装

win平台下,cmd输入

pip install beautifulsoup4

beautifulsoup的基本结构

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')

BeautifulSoup库的基本元素

对于bs库的理解

​ BeautifulSoup库是解析、遍历、维护”标签树“的功能库,什么是标签树呢,HTML语言是标记语言,通过不同的标签将内容分割为不同的结构,因此HTML的文本可以通过标签生成一颗标签树,bs库通过对这个树进行不同的操作。

​ 一个标签,例如<p>..</p>``<a>..</a>等是以一个标签:Tag的形式出现,p a是tag的名称Name,而后面跟的键值对是它的属性Attributes,一个HTML文档 = 一颗标签树 = 一个BeautifulSoup类实例 ,它对应的是HTML/XML文档的全部内容

bs库解析器

soup = BeautifulSoup('<html>data</html>','html.parser')

bs支持多种不同的解析器分别为

解析器 使用方法 条件
bs4的HTML解析器 BeautifulSoup('<html>data</html>','html.parser') 安装bs4库
lxml的HTML解析器 BeautifulSoup('<html>data</html>','lxml') pip install lxml
lxml的XML解析器 BeautifulSoup('<html>data</html>','xml') pip install lxml
html5lib的解析器 BeautifulSoup('<html>data</html>','html5lib') pip install html5lib

BeautifulSoup类的基本元素

基本元素 说明
Tag 标签,最基本信息组织单元<></>
Name 标签名字,比如p,a,格式<tag>.name
Attributes 标签属性,字典形式格式<tag>.attrs
NavigableString 标签内非属性的字符串<>和</>中间的字符串,格式:<tag>.string
Comment 标签内字符串的注释部分,特殊Comment类型

下面来说明一下这些都是怎么用的

Beautiful Soup库的3种遍历方式

​ 对于标签树,一共有3种遍历方式,分别是:上行遍历,下行遍历,平行遍历

下行遍历

下行遍历有三种属性

属性 说明
.contents 子结点的列表,将<tag>所有儿子节点存入列表
.children 子结点的迭代类型,与.contents类似,用于循环遍历儿子结点
.descendants 子孙结点的迭代类型,包含所有子孙结点,用于循环遍历

BeautifulSoup类是根节点,<head></head>标签单独作为一例,中间包含title标签,<body></body>中间包含<p><a>标签等。

注:在.contents属性中,'\n'也是作为单独的一个儿子节点被存入

下行遍历的结构
  1. 遍历儿子节点

    for child in soup.body.children:
        print(child)
    
  1. 遍历子孙节点

    for child in soup.body.descendants:
        print(child)
    

上行遍历

属性 说明
.parent 结点的父亲标签
.parents 结点先辈标签的迭代类型,用于循环遍历先辈结点

注:soup类本身的父结点为空,所以如果进行迭代,要进行判断

上行遍历的结构
for parent in soup.a.parents:
    if parent is None:    #判断是否为最顶结点
        print(parent)
    else:
        print(parent.name)

平行遍历

属性 说明
.next_sibling 返回按照HTML文本顺序的下一个平行结点标签
.previous_sibling 返回按照HTML文本顺序的上一个平行结点标签
.next_siblings 迭代类型(迭代器),返回按照HTML文本顺序的后续平行结点
.previous_siblings 迭代类型(迭代器),返回按照HTML文本顺序的前驱平行结点

注:平行遍历是发生在同一父结点下的各结点

平行遍历的结构
  1. 遍历后续结点

    for sibling in soup.a.next_siblings:
        print(sibling)
    
  1. 遍历前驱结点

    for sibling in soup.a.previous_siblings:
        print(sibling)
    

bs4的prettify方法

​ bs4库的prettify()方法为HTML文本<>及其内容增加换行符

prettify方法可用于标签或者BeautifulSoup类型,格式为

<tag>.prettify()    

2. 信息组织与提取方法

信息标记的三种形式

XML JSON YAML

XML

XML全称为eXtensible Markup Language,是一种标记语言

<img src="china.jpg" size="10">...</img>

带名称和属性的标签,中间还有非标签元素

<img src="china.jpg" size="10" />

空元素书写形式

<!-- This is a comment-->

注释

JSON

JavaScript Object Notation 有类型的键值对 key:value

比如:

"name":"哈尔滨工业大学"

而一个关键字对应多个值时

"name":["哈尔滨工业大学","西大直街"]

如果出现键值对的嵌套

"name":{
    "newName":"圣马家沟职业学院",
    "oldName":"哈尔滨工业大学"
}

YAML

YAML Ain`t Markup Language 无类型键值对, key:value仅用字符串就可以表达关系

name:哈尔滨工业大学

利用缩进表达所属关系

name:
    newName:圣马家沟职业学院
    oldName:哈尔滨工业大学

-表达并列关系

name:
-哈尔滨工业大学
-西大直街

| 表达整块数据,如果数据很多的话,# 表示注释

text: |  #introduction
balabalabala...

举个例子区分:

描述一个学生小明

XML:

<person>
    <firstname>Ming</firstname>
    <lastname>Xiao</lastname>
    <address>
        <street>西大直街</street>
        <city>哈尔滨</city>
    </address>
    <prof>CS</prof><prof>SE</prof>
</person>

JSON

"person":{
    "firstName":"Ming",
    "lastName":"Xiao",
    "address":{
                "street":"西大直街"
                "city":"哈尔滨"
              },
    "prof":["CS","SE"]
}

YAML

person:
    firstname:Ming
    lastName:Xiao
    address:
        street:西大直街
        city:哈尔滨
    prof:
    -CS
    -SE

三种信息标记比较:

XML 最早的标记语言,拓展性好,繁琐

JSON 信息有类型,适合程序处理,较XML简洁(什么叫有类型,JSON中每个关键字有""标记,”“叫做类型,”xxx“叫做键key

YAML 信息无类型,单纯为字符串,可读性好

信息提取的一般方法

方法一:完整解析信息的标记形式,在提取关键信息

​ 需要标记解析器,例如bs4库的标签树,信息提取准确但是提取过程很繁琐,速度慢

方法二:无视标记形式,直接搜索关键信息

​ 需要文本查找函数,提取过程简洁速度快,但是提取的结果准确性与信息内容有关

基于bs4库的HTML查找方法

<>.find_all方法

<>.find_all方法的实现为

<>.find_all(name, attrs, recursive, string, **kw)

函数返回的是一个列表LIST类型,存储查找的结果,注意:此时列表中的是Tag类型实例,其包含name、string等元素

对一个标签搜索

>>> soup.find_all('a')

对多个标签搜索

>>> soup.find_all(['a','b'])

也可以输入参数True和正则表达式,True返回的结果是所有的标签

>>> for tag in soup.find_all(True):
        print(tag.name)

>>> import re
>>> for tag in soup.find_all(re.complie('b')):
        print(tag.name)

body
b

<>.find_all()的等价写法

<tag>(...) <--> <tag>.find_all(...)

soup(...) <--> soup.find_all(...)

主要是因为这个方法使用频率很高

<>.find_all的扩展方法

方法 说明
<>.find() 搜索值返回一个结果
<>.find_parents() 在先辈节点中搜索,返回列表类型
<>.find_parent() 在先辈节点中返回一个结果
<>.find_next_siblings() 在后续平行节点中搜索,返回列表类型
<>.find_next_sibling() 在后续平行节点中返回一个结果
<>.find_previous_siblings() 在前序平行节点中搜索,返回列表
<>.find_previous_sibling() 在前序平行节点中返回一个结果

至于为什么没有对于子孙节点的搜索,是因为find_all() 和 find()函数就是这样的功能

3. 正则表达式库入门(re)

​ RE,regular expression, regex,正则表达式是用来简洁表达一组字符串的表达式,是一种通用的字符串表达框架。

​ 在re库中,首先使用正则表达式规则写出字符串,然后通过re库的编译是字符串转换为正则表达式特征,再将特征与字符串匹配。

正则表达式regex(string) => p = re.compile(regex)(编译) => 特征p

正则表达式的语法

正则表达式常用操作符

操作符 说明 实例
. 表示任何单个字符
[ ] 字符集,对单个字符给出取值范围 [abc]表示abc,[a-z]表示a到z单个字符
[^ ] 非字符集,对单个字符给出排除范围 [^abc]表示非a或b或c的单个字符
* 前一个字符0次或无限次扩展 abc*表示ab、abc、abcccc
+ 前一个字符1次或无限次扩展 abc+表示abc、abcc、abccc
前一个字符0次或1次扩展 abc?表示ab、abc
| 左右表达式任意一个 abc|edf表示abc、def
{m} 扩展前一个字符m次 ab{2}表示abb
{m,n} 扩展前一个字符m至n次(含n) ab{1,2}表示abc、abbc
^ 匹配字符串的开头 ^abc表示abc且在一个字符串的开头
$ 匹配字符串的结尾 abc$表示abc且在一个字符串的结尾
( ) 分组标记,内部只能使用|操作符 (abc)表示abc,(abc|def)表示abc、def 没有或
\d 数字,等价0-9
\w 单词字符,等价[A-Za-z0-9_]

正则表达式的经典实例

由26个字母组成的字符串 ^[A-Za-z]+$

由26个字母和数字组成的字符串 ^[A-Za-z0-9]+$

整数形式的字符串 ^-?\d+$

正整数形式的字符串 ^[0-9]*****[1-9][0-9]*****$

邮政编码 [1-9]\d{5}

匹配中文字符 [\u4e00-\u9fa5]

匹配IP地址的正则表达式

0-99 :[1-9]?\d

100-199 :1\d{2}

200-249 :2[0-4]\d

250-255 :25[0-5]

表示为:(0-99|100-199|200-249|250-255.){3}(0-99|100-199|200-249|250-255)

Re库介绍

​ 正则表达式使用raw string类型(原生字符串类型),表示为r'text'。当然也可以使用string类型表达,但是\需要表示为\\

Re库的主要功能函数

函数 说明
re.search() 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象
re.match() 从一个字符串的开始位置匹配正则表达式,返回match对象
re.findall() 搜索字符串,列表类型返回全部匹配子串
re.split() 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
re.finditer() 搜索字符串,返回匹配的迭代类型,每个迭代元素都是match对象
re.sub() 在一个字符串中替换所有匹配子串,返回替换后字符串

Re库的另一个等价用法

​ 除了函数式用法外,还有一种面向对象的用法,即先编译出正则表达式对象,在对这个对象进行操作

其中生成编译对象的用法为

regex = re.complie(pattern, flags=0)

regex为编译后生成的正则表达式对象,pattern为正则字符串或原生字符串表示。flags为正则表达式使用时的控制标记。通过调用regex对象(也可以是其他名字)的方法实现搜索,具体方法的名字与上面功能函数相同。

上面很多的函数都返回了match对象,接下来简单介绍match对象

Match对象介绍

Match对象是一次匹配的结果,包含很多匹配的信息

Match对象的属性

属性 说明
.string 待匹配的文本
.re 匹配时使用的pttern对象(正则表达式)
.pos 正则表达式搜索文本的开始位置
.endpos 正则表达式的结束位置

Match对象的方法

方法 说明
.group(0) 获得匹配后的字符串
.start() 匹配字符串在原始字符串的开始位置
.end() 匹配字符串在原始字符串的结束位置
.span() 返回(开始位置,结束位置)

以"BIT100081 TSU10084"为例

>>> import re
>>> m = re.search(r'[1-9]\d{5}', "BIT100081 TSU100084")
>>> m.string
'BIT100081 TSU100084'
>>> m.re
re.complie('[1-9]\\d{5}')    #正则表达式对象
>>> m.endpos
19
>>> m.group(0)
'100081'
>>> m.start()
3
>>> m.end()
9
>>> m.span()
(3, 9)

Re库的贪婪匹配和最小匹配

Re库默认采用贪婪匹配, 即输出匹配最长的子串,如果要输出最短的字符串,则采用最小匹配,在匹配符后加上?

最小匹配操作符

操作符 说明
*? 前一个字符0次或无限次扩展,最小匹配
+? 前一个字符1次或无限次扩展,最小匹配
?? 前一个字符0次或1次扩展,最小匹配
{m,n}? 扩展前一个字符m至n次,最小匹配
上一篇 下一篇

猜你喜欢

热点阅读