什么是正则表达式
正则表达式,又称规则表达式,通常被用来检索、替换那些符合某个模式(规则)的文本。
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
给定一个正则表达式和另一个字符串,我们可以达到如下的目的:
给定的字符串是否符合正则表达式的过滤逻辑(“匹配”);
通过正则表达式,从文本字符串中获取我们想要的特定部分(“过滤”)。
常用的匹配规则:
w 匹配字母数字及下划线 W 匹配f非字母数字下划线 s 匹配任意空白字符,等价于[ f] S 匹配任意非空字符 d 匹配任意数字 D 匹配任意非数字 A 匹配字符串开始 匹配字符串结束,如果存在换行,只匹配换行前的结束字符串 z 匹配字符串结束 G 匹配最后匹配完成的位置 匹配一个换行符 匹配一个制表符 ^ 匹配字符串的开头 $ 匹配字符串的末尾 . 匹配任意字符,除了换行符,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('^hellos(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('<divsclass="f18 mb20">(.*?)</div>', re.S) 31 pattern = re.compile('<divsid="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(' ','').replace(' ', ' ').replace(' ... ',' ') 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章 ' % self.page) 60 f.write(' ') 61 f.write(item) 62 f.write(' ') 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
谢谢使用!