c 抓取网页数据(Python爬虫世界里必不可少的神兵利器-页面解析和数据提取)
优采云 发布时间: 2022-03-30 15:14c 抓取网页数据(Python爬虫世界里必不可少的神兵利器-页面解析和数据提取)
页面解析和数据提取javascript
一般来说,对于我们来说,需要爬取的是一个网站或者一个应用的内容,以提取有用的价值。内容通常分为两部分,非结构化数据和结构化数据。php
非结构化数据处理文本、电话号码、电子邮件地址HTML 文档结构化数据处理JSON 文档XML 文档为什么要学习正则表达式
其实爬虫主要有四个步骤: css
清除目标(知道要在哪个范围内搜索或网站) 爬取(爬取网站的所有内容) 获取(删除对我们无用的数据) 处理数据(根据以我们想要的方式存储和使用)
我们实际上省略了步骤 3,即昨天案例中的“采取”步骤。由于我们下载的数据都是网页,数据庞大而混乱,而且大部分是我们不关心的,所以我们需要根据自己的需要进行过滤和匹配。html
所以对于文本过滤或者规则匹配来说,最厉害的就是正则表达式,它是Python爬虫世界里不可或缺的利器。爪哇
什么是正则表达式
正则表达式也称为正则表达式,一般用于检索和替换符合某种模式(规则)的文本。节点
正则表达式是一个字符串操作的逻辑公式,就是用一些预先定义好的特定字符和这些特定字符的组合组成一个“规则字符串”,而这个“规则字符串”用来表达A过滤逻辑字符串。Python
给定一个正则表达式和另一个字符串,我们可以实现以下目标: 程序员
正则表达式匹配规则
Python的re模块
在 Python 中,我们可以通过内置的 re 模块来使用正则表达式。网络
需要注意的一点是正则表达式使用特殊字符的转义,所以如果我们想使用原创字符串,只需添加一个 r 前缀,例如:正则表达式
r'chuanzhiboke\t\.\tpython'
使用 re 模块的通常步骤如下:
使用compile()函数将正则表达式的字符串形式编译成Pattern对象
通过Pattern对象提供的一系列方法,对文本进行匹配查找,得到匹配结果,就是一个Match对象。
最后,使用 Match 对象提供的属性和方法来获取信息,并根据需要执行其余操作。编译函数
compile函数用于编译正则表达式并生成Pattern对象,通常以如下形式使用:
import re
# 将正则表达式编译成 Pattern 对象
pattern = re.compile(r'\d+')
上面,我们已经将一个正则表达式编译成一个 Pattern 对象。接下来,我们可以使用pattern的一系列方法来匹配文本。
Pattern 对象的一些常用方法是:
匹配方法
match方法用于查找字符串的头部(也可以指定起始位置),是一个匹配,只要找到一个匹配的结果,就会返回,而不是查找所有的匹配结果。它通常以下列形式使用:
match(string[, pos[, endpos]])
其中,string为要匹配的字符串,pos和endpos为可选参数,指定字符串的起止位置,默认值分别为0和len(字符串长度)。所以,当你不指定 pos 和 endpos 时,match 方法默认匹配字符串的头部。
当匹配成功时,返回一个 Match 对象,如果没有匹配,则返回 None。
>>> import re
>>> pattern = re.compile(r'\d+') # 用于匹配至少一个数字
>>> m = pattern.match('one12twothree34four') # 查找头部,没有匹配
>>> print m
None
>>> m = pattern.match('one12twothree34four', 2, 10) # 从'e'的位置开始匹配,没有匹配
>>> print m
None
>>> m = pattern.match('one12twothree34four', 3, 10) # 从'1'的位置开始匹配,正好匹配
>>> print m # 返回一个 Match 对象
>>> m.group(0) # 可省略 0
'12'
>>> m.start(0) # 可省略 0
3
>>> m.end(0) # 可省略 0
5
>>> m.span(0) # 可省略 0
(3, 5)
上面,匹配成功时返回一个 Match 对象,其中:
让我们看另一个例子:
>>> import re
>>> pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) # re.I 表示忽略大小写
>>> m = pattern.match('Hello World Wide Web')
>>> print m # 匹配成功,返回一个 Match 对象
>>> m.group(0) # 返回匹配成功的整个子串
'Hello World'
>>> m.span(0) # 返回匹配成功的整个子串的索引
(0, 11)
>>> m.group(1) # 返回第一个分组匹配成功的子串
'Hello'
>>> m.span(1) # 返回第一个分组匹配成功的子串的索引
(0, 5)
>>> m.group(2) # 返回第二个分组匹配成功的子串
'World'
>>> m.span(2) # 返回第二个分组匹配成功的子串
(6, 11)
>>> m.groups() # 等价于 (m.group(1), m.group(2), ...)
('Hello', 'World')
>>> m.group(3) # 不存在第三个分组
Traceback (most recent call last): File "", line 1, in IndexError: no such group
-------------------------------------------------- -------------------------------------------------- -- 搜索方法
search 方法用于查找字符串中的任何位置。这也是一场比赛。只要找到匹配结果,就会返回,而不是查找所有匹配结果。它通常以下列形式使用:
搜索(字符串[,pos[,endpos]])
其中,string为要匹配的字符串,pos和endpos为可选参数,指定字符串的起止位置,默认值分别为0和len(字符串长度)。
当匹配成功时,返回一个 Match 对象,如果没有匹配,则返回 None。
让我们看一个例子:
>>> import re
>>> pattern = re.compile('\d+')
>>> m = pattern.search('one12twothree34four') # 这里若是使用 match 方法则不匹配
>>> m
>>> m.group()
'12'
>>> m = pattern.search('one12twothree34four', 10, 30) # 指定字符串区间
>>> m
>>> m.group()
'34'
>>> m.span()
(13, 15)
让我们看另一个例子:
# -*- coding: utf-8 -*-
import re
# 将正则表达式编译成 Pattern 对象
pattern = re.compile(r'\d+')
# 使用 search() 查找匹配的子串,不存在匹配的子串时将返回 None
# 这里使用 match() 没法成功匹配
m = pattern.search('hello 123456 789')
if m:
# 使用 Match 得到分组信息
print 'matching string:',m.group()
# 起始位置和结束位置
print 'position:',m.span()
结果:
matching string: 123456
position: (6, 12)
-------------------------------------------------- -------------------------------------------------- -- findall 方法
以上匹配和搜索方式都是一次性匹配,只要找到匹配结果,就会返回。但是,大多数时候,我们需要搜索整个字符串来获得所有匹配的结果。
findall 方法可以按以下形式使用:
findall(string[, pos[, endpos]])
其中,string为要匹配的字符串,pos和endpos为可选参数,指定字符串的起止位置,默认值分别为0和len(字符串长度)。
findall 将所有匹配的子字符串作为一个列表返回,如果没有匹配则返回一个空列表。
看一下这个例子:
import re
pattern = re.compile(r'\d+') # 查找数字
result1 = pattern.findall('hello 123456 789')
result2 = pattern.findall('one1two2three3four4', 0, 10)
print result1
print result2
结果:
['123456', '789']
['1', '2']
我们先来看一个栗子:
# re_test.py
import re
#re模块提供一个方法叫compile模块,提供咱们输入一个匹配的规则
#而后返回一个pattern实例,咱们根据这个规则去匹配字符串
pattern = re.compile(r'\d+\.\d*')
#经过partten.findall()方法就可以所有匹配到咱们获得的字符串
result = pattern.findall("123.141593, 'bigcat', 232312, 3.15")
#findall 以 列表形式 返回所有能匹配的子串给result
for item in result:
print item
运行结果:
123.141593
3.15
-------------------------------------------------- -------------------------------------------------- -- 查找器方法
finditer 方法的行为类似于 findall 的行为,同样会搜索整个字符串并获取所有匹配的结果。但它返回一个迭代器,该迭代器按顺序访问每个匹配结果(Match 对象)。
看一下这个例子:
# -*- coding: utf-8 -*-
import re
pattern = re.compile(r'\d+')
result_iter1 = pattern.finditer('hello 123456 789')
result_iter2 = pattern.finditer('one1two2three3four4', 0, 10)
print type(result_iter1)
print type(result_iter2)
print 'result1...'
for m1 in result_iter1: # m1 是 Match 对象
print 'matching string: {}, position: {}'.format(m1.group(), m1.span())
print 'result2...'
for m2 in result_iter2:
print 'matching string: {}, position: {}'.format(m2.group(), m2.span())
结果:
result1...
matching string: 123456, position: (6, 12)
matching string: 789, position: (13, 16)
result2...
matching string: 1, position: (3, 4)
matching string: 2, position: (7, 8)
-------------------------------------------------- -------------------------------------------------- -- 分割方法
split 方法根据可以匹配的子字符串将字符串拆分后返回一个列表。它可以以下列形式使用:
split(string[, maxsplit])
其中,maxsplit 用于指定最大拆分次数,而不是指定所有拆分。
看一下这个例子:
import re
p = re.compile(r'[\s\,\;]+')
print p.split('a,b;; c d')
结果:
['a', 'b', 'c', 'd']
-------------------------------------------------- -------------------------------------------------- -- 子方法
sub 方法用于替换。它以下列形式使用:
sub(repl, string[, count])
其中,repl 可以是字符串,也可以是函数:
看一下这个例子:
import re
p = re.compile(r'(\w+) (\w+)') # \w = [A-Za-z0-9]
s = 'hello 123, hello 456'
print p.sub(r'hello world', s) # 使用 'hello world' 替换 'hello 123' 和 'hello 456'
print p.sub(r'\2 \1', s) # 引用分组
def func(m):
return 'hi' + ' ' + m.group(2)
print p.sub(func, s)
print p.sub(func, s, 1) # 最多替换一次
结果:
hello world, hello world
123 hello, 456 hello
hi 123, hi 456
hi 123, hello 456
-------------------------------------------------- -------------------------------------------------- -- 匹配中文
在某些情况下,我们希望匹配文本中的汉字。需要注意的一点是,中文的unicode编码范围主要在[u4e00-u9fa5],这主要是因为这个范围不完整,比如不包括全角(中文)标点,但是,在大多数情况下。
假设现在要提取字符串title = u'hello, hello, world'中的中文,可以这样:
import re
title = u'你好,hello,世界'
pattern = re.compile(ur'[\u4e00-\u9fa5]+')
result = pattern.findall(title)
print result
请注意,我们为正则表达式添加了两个前缀 ur,其中 r 表示使用原创字符串,u 表示它是 unicode 字符串。
结果:
[u'\u4f60\u597d', u'\u4e16\u754c']
注意:贪心模式和非贪心模式贪心模式:在整个表达式匹配成功的前提下,尽量匹配(*);非贪心模式:在整个表达式匹配成功的前提下,尽量少匹配(?);Python 中的量词默认是贪婪的。示例 1:源字符串:abbbc 示例 2:源字符串:aa
测试1
bb
测试2
抄送
这是贪婪模式。匹配到第一个“
",整个表达式都可以匹配成功,但是因为使用了贪心模式,所以还是要尝试向右匹配,看是否有更长的子串可以匹配成功。匹配到第二个"