四、正则表达式re模块 常用的匹配规则:Python 的 re 模块也可以直接用re.match(),re.search(),re.findall(),re.finditer(),re.sub()

什么是正则表达式

正则表达式,又称规则表达式,通常被用来检索、替换那些符合某个模式(规则)的文本。 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

给定一个正则表达式和另一个字符串,我们可以达到如下的目的:

给定的字符串是否符合正则表达式的过滤逻辑(“匹配”); 通过正则表达式,从文本字符串中获取我们想要的特定部分(“过滤”)。

常用的匹配规则:

\w      匹配字母数字及下划线
\W      匹配f非字母数字下划线
\s      匹配任意空白字符,等价于[\t\n\r\f]
\S      匹配任意非空字符
\d      匹配任意数字
\D      匹配任意非数字
\A      匹配字符串开始
\Z      匹配字符串结束,如果存在换行,只匹配换行前的结束字符串
\z      匹配字符串结束
\G      匹配最后匹配完成的位置
\n      匹配一个换行符
\t      匹配一个制表符
^       匹配字符串的开头
$       匹配字符串的末尾
.       匹配任意字符,除了换行符,re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符
[....]  用来表示一组字符,单独列出:[amk]匹配a,m或k
[^...]  不在[]中的字符:[^abc]匹配除了a,b,c之外的字符
*       匹配0个或多个的表达式
+       匹配1个或者多个的表达式
?       匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
{n}     精确匹配n前面的表示
{m,m}   匹配n到m次由前面的正则表达式定义片段,贪婪模式
a|b     匹配a或者b
()      匹配括号内的表达式,也表示一个组

Python 的 re 模块

在 Python 中,我们可以使用内置的 re 模块来使用正则表达式。

有一点需要特别注意的是,正则表达式使用 对特殊字符进行转义,所以如果我们要使用原始字符串,只需加一个 r 前缀。

re 模块的一般使用步骤如下:

1、使用 compile() 函数将正则表达式的字符串形式编译为一个 Pattern 对象

    compile 函数

  compile 函数用于编译正则表达式,生成一个 Pattern 对象,它的一般使用形式如下:

import re

# 将正则表达式编译成 Pattern 对象
pattern = re.compile(r'\d+') 

    在上面,我们已将一个正则表达式编译成 Pattern 对象,接下来,我们就可以利用 pattern 的一系列方法对文本进行匹配查找了。

  Pattern 对象的一些常用方法主要有:

  • match 方法:从起始位置开始查找,一次匹配
  • search 方法:从任何位置开始查找,一次匹配
  • findall 方法:全部匹配,返回列表
  • finditer 方法:全部匹配,返回迭代器
  • split 方法:分割字符串,返回列表
  • sub 方法:替换

2、通过 Pattern 对象提供的一系列方法对文本进行匹配查找,获得匹配结果,一个 Match 对象。

  2.1、match 方法

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 对象
<_sre.SRE_Match object at 0x10a42aac0>

>>> m.group(0)   # 可省略 0
'12'
>>> m.start(0)   # 可省略 0
3
>>> m.end(0)     # 可省略 0
5
>>> m.span(0)    # 可省略 0
(3, 5)

  在上面,当匹配成功时返回一个 Match 对象,其中:

    group([group1, …]) 方法用于获得一个或多个分组匹配的字符串,当要获得整个匹配的子串时,可直接使用 group() 或 group(0);

    start([group]) 方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为 0;

    end([group]) 方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为 0;

    span([group]) 方法返回 (start(group), end(group))。

  2.2、search 方法

    search 方法用于查找字符串的任何位置,它也是一次匹配,只要找到了一个匹配的结果就返回,而不是查找所有匹配的结果,它的一般使用形式如下:

    search(string[, pos[, endpos]])

      其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,指定字符串的起始和终点位置,默认值分别是 0 和 len (字符串长度)。

    当匹配成功时,返回一个 Match 对象,如果没有匹配上,则返回 None。

      让我们看看例子:    

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)

  2.3、findall 方法

    上面的 match 和 search 方法都是一次匹配,只要找到了一个匹配的结果就返回。然而,在大多数时候,我们需要搜索整个字符串,获得所有匹配的结果。

    findall 方法的使用形式如下:

    findall(string[, pos[, endpos]])

      其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,指定字符串的起始和终点位置,默认值分别是 0 和 len (字符串长度)。

      findall 以列表形式返回全部能匹配的子串,如果没有匹配,则返回一个空列表。

      看看例子:

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
232312,
3.15

  2.4、finditer 方法

    finditer 方法的行为跟 findall 的行为类似,也是搜索整个字符串,获得所有匹配的结果。但它返回一个顺序访问每一个匹配结果(Match 对象)的迭代器。

  2.5、sub 方法

    sub 方法用于替换。它的使用形式如下:

    sub(repl, string[, count])

    其中,repl 可以是字符串也可以是一个函数:

      如果 repl 是字符串,则会使用 repl 去替换字符串每一个匹配的子串,并返回替换后的字符串,另外,repl 还可以使用 id 的形式来引用分组,但不能使用编号 0;

      如果 repl 是函数,这个方法应当只接受一个参数(Match 对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。

      count 用于指定最多替换次数,不指定时全部替换。

    看看例子:

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

3、最后使用 Match 对象提供的属性和方法获得信息,根据需要进行其他的操作

也可以直接用re.match(),re.search(),re.findall(),re.finditer(),re.sub()

import re
content= "hello 1234567 World_This is a regex Demo"
result = re.match('^hello\s(\d+)\sWorld.*Demo$',content)
print(result)
print(result.group())
print(result.group(1))
print(result.span())

结果:

<_sre.SRE_Match object; span=(0, 40), match='hello 1234567 World_This is a regex Demo'>
hello 1234567 World_This is a regex Demo
1234567
(0, 40)

使用正则表达式的爬虫:(小说)

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 import urllib.request
 5 import re
 6 
 7 class Spider:
 8     def __init__(self):
 9         # 初始化起始页位置
10         self.page = 1
11         # 爬取开关,如果为True继续爬取
12         self.switch = True
13 
14     def loadPage(self):
15         """
16             作用:下载页面
17         """
18         print("正在下载数据....")
19         url = "https://www.siluke.tw/ny10345/" + str(self.page+6059038) + ".html"
20         headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'}
21         request = urllib.request.Request(url, headers = headers)
22         response = urllib.request.urlopen(request)
23 
24         # 获取每页的HTML源码字符串
25         html = response.read().decode('utf-8')
26         #print html
27 
28 
29         # 创建正则表达式规则对象,匹配每页里的段子内容,re.S 表示匹配全部字符串内容
30         #pattern = re.compile('<div\sclass="f18 mb20">(.*?)</div>', re.S)
31         pattern = re.compile('<div\sid="content">(.*?)</div>')
32 
33         # 将正则匹配对象应用到html源码字符串里,返回这个页面里的所有文章的列表
34         content_list = pattern.findall(html)
35 
36         # 调用dealPage() 处理文章里的杂七杂八
37         self.dealPage(content_list)
38 
39     def dealPage(self, content_list):
40         """
41             处理每页的文章
42             content_list : 每页的文章列表集合
43         """
44         for item in content_list:
45             # 将集合里的每个段子按个处理,替换掉无用数据
46             item = item.replace('<br />', '').replace('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;','').replace('&nbsp;&nbsp;&nbsp;&nbsp;', '\n').replace('&nbsp;...&nbsp;&nbsp;','\n')
47 
48             # 处理完后调用writePage() 将每个段子写入文件内
49             self.writePage(item)
50 
51     def writePage(self, item):
52         """
53             把每章逐个写入文件里
54             item: 处理后的文章
55         """
56         # 写入文件内
57         print("正在写入数据....")
58         with open("斗破苍穹.txt", "a") as f:
59             f.write('第%s章\n' % self.page)
60             f.write('\n')
61             f.write(item)
62             f.write('\n')
63 
64     def startWork(self):
65         """
66             控制爬虫运行
67         """
68         # 循环执行,直到 self.switch == False
69         while self.switch:
70             # 用户确定爬取的次数
71             self.loadPage()
72             command = input("如果继续爬取,请按回车(退出输入quit)")
73             if command == "quit":
74                 # 如果停止爬取,则输入 quit
75                 self.switch = False
76             # 每次循环,page页码自增1
77             self.page += 1
78         print("谢谢使用!")
79 
80 
81 if __name__ == "__main__":
82     articleSpider = Spider()
83 #   articleSpider.loadPage()
84     articleSpider.startWork()

运行结果:

正在下载数据....
正在写入数据....
如果继续爬取,请按回车(退出输入quit)quit
谢谢使用!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FSociety

SQL中GROUP BY用法示例

GROUP BY我们可以先从字面上来理解,GROUP表示分组,BY后面写字段名,就表示根据哪个字段进行分组,如果有用Excel比较多的话,GROUP BY比较类...

5.1K20
来自专栏前端桃园

知识体系解决迷茫的你

最近在星球里群里都有小伙伴说道自己对未来的路比较迷茫,一旦闲下来就不知道自己改干啥,今天我这篇文章就是让你觉得一天给你 25 个小时你都不够用,觉得睡觉都是浪费...

21140
来自专栏钱塘大数据

中国互联网协会发布:《2018中国互联网发展报告》

在2018中国互联网大会闭幕论坛上,中国互联网协会正式发布《中国互联网发展报告2018》(以下简称《报告》)。《中国互联网发展报告》是由中国互联网协会与中国互联...

13550
来自专栏微信公众号:小白课代表

不只是软件,在线也可以免费下载百度文库了。

不管是学生,还是职场员工,下载各种文档几乎是不可避免的,各种XXX.docx,XXX.pptx更是家常便饭,人们最常用的就是百度文库,豆丁文库,道客巴巴这些下载...

44530
来自专栏腾讯高校合作

【倒计时7天】2018教育部-腾讯公司产学合作协同育人项目申请即将截止!

15720
来自专栏haifeiWu与他朋友们的专栏

复杂业务下向Mysql导入30万条数据代码优化的踩坑记录

从毕业到现在第一次接触到超过30万条数据导入MySQL的场景(有点low),就是在顺丰公司接入我司EMM产品时需要将AD中的员工数据导入MySQL中,因此楼主负...

28940
来自专栏Ken的杂谈

【系统设置】CentOS 修改机器名

18030
来自专栏怀英的自我修炼

考研英语-1-导学

英二图表作文要重视。总体而言,英语一会比英语二难点。不过就写作而言,英语二会比英语一有难度,毕竟图表作文并不好写。

11810
来自专栏腾讯社交用户体验设计

ISUX Xcube智能一键生成H5

51220
来自专栏钱塘大数据

理工男图解零维到十维空间,烧脑已过度,受不了啦!

让我们从一个点开始,和我们几何意义上的点一样,它没有大小、没有维度。它只是被想象出来的、作为标志一个位置的点。它什么也没有,空间、时间通通不存在,这就是零维度。

32430

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励