正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。
正则表达式
模式字符串使用特殊的语法来表示一个正则表达式:
- 字母和数字表示他们自身。一个正则表达式模式中的字母和数字匹配同样的字符串。
- 多数字母和数字前加一个反斜杠时会拥有不同的含义。
- 标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。
- 反斜杠本身需要使用反斜杠转义。
- 由于正则表达式通常都包含反斜杠,所以你最好使用原始字符串来表示它们。模式元素(如 r'\t',等价于 '\\t')匹配相应的特殊字符。
- 下表列出了正则表达式模式语法中的特殊元素。如果你使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。
模式 | 描述 |
---|---|
^ | 匹配字符串的开头 |
$ | 匹配字符串的末尾 |
. | 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符 |
[...] | 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k' |
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
[^...] | 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符 |
re* | 匹配0个或多个的表达式 |
re+ | 匹配1个或多个的表达式 |
re? | 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式 |
re{ n} | 精确匹配 n 个前面表达式。例如, o{2} 不能匹配 "Bob" 中的 "o",但是能匹配 "food" 中的两个 o |
re{ n,} | 匹配 n 个前面表达式。例如, o{2,} 不能匹配"Bob"中的"o",但能匹配 "foooood"中的所有 o。"o{1,}" 等价于 "o+"。"o{0,}" 则等价于 "o*" |
re{ n, m} | 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式 |
a|b | 匹配a或b |
(re) | 匹配括号内的表达式,也表示一个组 |
(?imx) | 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域 |
(?-imx) | 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域 |
(?:re) | 类似 (...), 但是不表示一个组 |
(imx:re) | 在括号中使用i, m, 或 x 可选标志 |
(?-imx: re) | 在括号中不使用i, m, 或 x 可选标志 |
(?#...) | 注释 |
(?= re) | 前向肯定界定符。如果所含正则表达式,以 ... 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边 |
(?! re) | 前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功 |
(?> re) | 匹配的独立模式,省去回溯 |
\w | 匹配字母数字及下划线 |
\W | 匹配非字母数字及下划线 |
\s | 匹配任意空白字符,等价于 [\t\n\r\f] |
\S | 匹配任意非空字符 |
\d | 匹配任意数字,等价于 [0-9] |
\D | 匹配任意非数字 |
\A | 匹配字符串开始 |
\Z | 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串 |
\z | 匹配字符串结束 |
\G | 匹配最后匹配完成的位置 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er' |
\B | 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er' |
\n, \t | 匹配一个换行符。匹配一个制表符 |
\1...\9 | 匹配第n个分组的内容 |
\10 | 匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式 |
实例 | 描述 |
---|---|
python | 匹配 "python". |
[Pp]ython | 匹配 "Python" 或 "python" |
rub[ye] | 匹配 "ruby" 或 "rube" |
[aeiou] | 匹配中括号内的任意一个字母 |
[0-9] | 匹配任何数字。类似于 [0123456789] |
[a-z] | 匹配任何小写字母 |
[A-Z] | 匹配任何大写字母 |
[a-zA-Z0-9] | 匹配任何字母及数字 |
[^aeiou] | 除了aeiou字母以外的所有字符 |
^[0-9]*$ | 数字 |
^\d{m,n}$ | m-n位的数字 |
^(\-|\+)?\d+(\.\d+)?$ | 正数、负数、和小数 |
^(-?\d+)(\.\d+)?$ | 浮点数 |
<.*> | 默认为贪婪模式,会尽可能匹配长的字符(回溯算法) |
---|---|
<.*?> | 加上?后贪婪模式转换为非贪婪模式,匹配尽量短的字符 |
*? | 重复任意次,但是尽量减少重复 |
+? | 重复一次或者更多次,但是尽可能减少重复 |
?? | 重复零次或者一次,但是尽可能减少重复 |
.*?x | 提取前面任意长的字符,直到一个x出现 |
用户名 | /^[a-z0-9_-]{3,16}$/ |
---|---|
汉字 | ^[\u4e00-\u9fa5]{0,}$ |
密码 | /^[a-z0-9_-]{6,18}$/ |
十六进制值 | /^#?([a-f0-9]{6}|[a-f0-9]{3})$/ |
电子邮箱 | /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/ /^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{1,2}[a-z]+)+$/ |
URL | /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/ |
IP 地址 | /((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)/ /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ |
HTML 标签 | /^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$/ |
删除代码\\注释 | (?<!http:|\S)//.*$ |
Unicode编码中的汉字范围 | /^[\u2E80-\u9FFF]+$/ |
手机号码 | ^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$ |
QQ号 | [1-9][0-9]{4,} (腾讯QQ号从10000开始) |
注:上表引自
- http://www.runoob.com/python/python-reg-expressions.html#flags
- http://tool.oschina.net/uploads/apidocs/jquery/regexp.html
- http://www.cnblogs.com/Eva-J/articles/7228075.html#_label10
re模块
Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。
re 模块使 Python 语言拥有全部的正则表达式功能。
compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。
re 模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。
本章节主要介绍Python中常用的正则表达式处理函数。
re.match函数
尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
语法:re.match(pattern, string, flags = 0)
参数:
- pattern : 正则中的模式字符串
- string : 要被查找替换的原始字符串
- flags: 标志位,用于控制正则表达式的匹配方式,默认为0
返回值
匹配成功re.match方法返回一个匹配的对象,否则返回None。
匹配对象方法
匹配对象方法 | 描述 |
---|---|
group(num=0) | 匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。 |
groups() | 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。 |
flags
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响 ^ 和 $ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解 |
import re
print(re.match('www', 'www.baidu.com').span()) # 在起始位置匹配
print(re.match('com', 'www.baidu.com')) # 不在起始位置匹配
结果:
(0, 3)
NoneProcess finished with exit code 0
可以看到,当匹配成功后得到的是一个对象,我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式:
import re
string = "Python is easier than Java"
obj = re.match(r'(.*) is (.*?) .*',string,re.M|re.I)
if obj:
print("obj.group()",obj.group())
print("obj.group(1)",obj.group(1))
print("obj.group(2)", obj.group(2))
else:
print("No match")
结果:
obj.group() Python is easier than Java
obj.group(1) Python
obj.group(2) easierProcess finished with exit code 0
re.search
扫描整个字符串并返回第一个成功的匹配。
语法:re.search(pattern, string,flags = 0)
参数:
- pattern : 正则中的模式字符串
- string : 要被查找替换的原始字符串
- flags: 标志位,用于控制正则表达式的匹配方式,默认为0
返回值
- 匹配成功re.search方法返回一个匹配的对象,否则返回None。
import re
print(re.search('www', 'www.runoob.com').span()) # 在起始位置匹配
print(re.search('com', 'www.runoob.com').span()) # 不在起始位置匹配
结果:
(0, 3)
(11, 14)Process finished with exit code 0
我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式:
import re
line = "Cats are smarter than dogs"
searchObj = re.search(r'(.*) are (.*?) .*', line, re.M | re.I)
if searchObj:
print("searchObj.group() : ", searchObj.group())
print("searchObj.group(1) : ", searchObj.group(1))
print("searchObj.group(2) : ", searchObj.group(2))
else:
print("Nothing found!!")
结果:
searchObj.group() : Cats are smarter than dogs
searchObj.group(1) : Cats
searchObj.group(2) : smarterProcess finished with exit code 0
re.match与re.search区别
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
import re
line = "Cats are smarter than dogs";
matchObj = re.match(r'dogs', line, re.M | re.I)
if matchObj:
print("match --> matchObj.group() : ", matchObj.group())
else:
print("No match!!")
print("-------------LINE----------------")
matchObj = re.search(r'dogs', line, re.M | re.I)
if matchObj:
print("search --> matchObj.group() : ", matchObj.group())
else:
print("No match!!")
结果:
No match!!
-------------LINE----------------
search --> matchObj.group() : dogsProcess finished with exit code 0
re.sub和repl参数
Python 的 re 模块提供了re.sub用于替换字符串中的匹配项。
语法:re.sub(pattern, repl, string, count=0, flags=0)
参数:
- pattern : 正则中的模式字符串。
- repl : 替换的字符串,也可为一个函数。
- string : 要被查找替换的原始字符串。
- count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。
返回值
- 返回新的字符串
repl是一个参数:
import re
phone = '155-1234-5678 # 这是国内的一个电话号码'
num1 = re.sub(r' #.*$', "", phone)
print(num1)
num2 = re.sub(r'\D', '', phone)
print(num2)
结果:
155-1234-5678
15512345678Process finished with exit code 0
repl是一个函数:
import re
# 将匹配的数字乘以 2
def double(matched):
value = int(matched.group('value'))
return str(value * 2)
s = 'A23G4HFD567'
print(re.sub('(?P<value>\d+)', double, s))
结果:
A46G8HFD1134
Process finished with exit code 0
re.compile
compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。
语法:re.compile(pattern[, flags])
参数:
- pattern : 正则中的模式字符串。
- flags: 标志位,用于控制正则表达式的匹配方式,默认为0
返回值
- 无
import re
pattern = re.compile(r'([a-z]+) ([a-z]+) ([a-z]+)', re.I)
m = pattern.match('Hello World Wide Web')
print(m.span(0))
print(m.group())
print("---------------LINE------------------")
print(m.span(1))
print(m.group(1))
print("---------------LINE------------------")
print(m.span(2))
print(m.group(2))
print("---------------LINE------------------")
print(m.span(3))
print(m.group(3))
结果:
(0, 16)
Hello World Wide
---------------LINE------------------
(0, 5)
Hello
---------------LINE------------------
(6, 11)
World
---------------LINE------------------
(12, 16)
WideProcess finished with exit code 0
引入这个参数的目的是:先编译表达式,后续执行过程中能够提高效率。
findall
在字符串中找到正则表达式所匹配的所有子串。
语法:findall(string[, pos[, endpos]])
参数:
- string : 待匹配的字符串。
- pos : 可选参数,指定字符串的起始位置,默认为 0。
- endpos : 可选参数,指定字符串的结束位置,默认为字符串的长度。
返回值
- 匹配成功返回一个有值列表
- 匹配失败返回一个空列表
import re
pattern = re.compile(r'\d+') # 查找数字
result1 = pattern.findall('python666 123 google 456')
result2 = pattern.findall('66yth66on123google456', 0, 10)
print(result1)
print(result2)
结果:
['666', '123', '456']
['66', '66', '1']Process finished with exit code 0
注意: match 和 search 是匹配一次 findall 匹配所有。
re.finditer
和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
语法:re.finditer(pattern, string, flags=0)
参数:
- pattern : 正则中的模式字符串
- string : 要被查找替换的原始字符串
- flags: 标志位,用于控制正则表达式的匹配方式,默认为0
返回值
- 返回一个迭代器
import re
it = re.finditer(r'\d+', '1222dfdfs4fdf4d5sffds4fsf52dfsf5ds')
for i in it:
print(i.group())
结果:
1222
4
4
5
4
52
5Process finished with exit code 0
re.split
split 方法按照能够匹配的子串将字符串分割后返回列表。
语法:re.split(pattern, string[, maxsplit=0, flags=0])
参数:
- pattern : 正则中的模式字符串
- string : 要被查找替换的原始字符串
- maxsplit: 标志位,分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数
- flags: 标志位,用于控制正则表达式的匹配方式
返回值
- 有值就返回列表
import re
res1 = re.split(r'\W+', 'Hello,World,This is Python Class')
res2 = re.split(r'(\W+)', 'Hello,World,This is Python Class')
res3 = re.split(r'\W+', 'Hello,World,This is Python Class', 3) # 切完三个后面就不切放在一起
print(res1)
print(res2)
print(res3)
结果:
['Hello', 'World', 'This', 'is', 'Python', 'Class']
['Hello', ',', 'World', ',', 'This', ' ', 'is', ' ', 'Python', ' ', 'Class']
['Hello', 'World', 'This', 'is Python Class']Process finished with exit code 0
面试题
1. 写一个简单的爬虫。
参考答案:
import re
from urllib.request import urlopen # 引入网站打开模块
def getPage(url):
"""
打开网站
:param url: 目标网站
:return: 转化为utf-8的网站源码
"""
response = urlopen(url)
return response.read().decode('utf-8')
def parsePage(s):
"""
设置正则表达式,提取网站中的关键字
:param s:flags
:return:筛选得到的信息一个generator
"""
com = re.compile(
'<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>'
'.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>',
re.S) # 分析电影相同的元素,提取关键字
ret = com.finditer(s)
for i in ret:
yield {
"id": i.group("id"),
"title": i.group("title"),
"rating_num": i.group("rating_num"),
"comment_num": i.group("comment_num"),
}
def main(num):
"""
运行主函数
:param num:网站中的页码
:return: None
"""
url = 'https://movie.douban.com/top250?start=%s&filter=' % num
response_html = getPage(url)
ret = parsePage(response_html)
# print(ret) # 打印 generator
f = open("豆瓣电影评分排行榜", "a", encoding="utf8")
for obj in ret:
print(obj)
data = str(obj)
f.write(data + "\n")
# 运行
count = 0
for i in range(10):
main(count)
count += 25
结果:
(略)