正则表达式 re模块

 

正则表达式
正则表达式在线测试工具:在线测试工具 http://tool.chinaz.com/regex/
正则是什么?    
通过一个规则 来从一段字符串中找到符合规则的内容/判断某段字符串是否符合规则
有什么用?  
1,注册页/网页上 要求你输入一个信息 判断你输入的信息是否格式正确
2,爬取数据 从网页上把代码以字符串的形式下载回到内存,提取这个大字符串中我想要的内容了
规则 匹配 待匹配的字符串
正则规则
1.字符组  约束字符中某一个字符位置上的值是什么 只要是出现在字符组中的内容都算匹配到了
2.'[3-9]' 从小到大的顺序到底是根据asc码的顺序来的
3.匹配数字 [0-9] 匹配字母[A-Za-z]  匹配数字和字母[0-9a-zA-Z]
字符组 : [字符组]
在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示
字符分为很多类,比如数字、字母、标点等等。
假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2...9这10个数之一。
正则
待匹配字符
匹配
结果
说明
[0123456789]
8
True
在一个字符组里枚举合法的所有字符,字符组里的任意一个字符
和"待匹配字符"相同都视为可以匹配
[0123456789]
a
False
由于字符组中没有"a"字符,所以不能匹配
 
[0-9]
 
7
True
也可以用-表示范围,[0-9]就和[0123456789]是一个意思
 
[a-z]
 
s
 
True
 
同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示
 
[A-Z]
 
B
 
True
 
[A-Z]就表示所有的大写字母
 
[0-9a-fA-F]
 
e
 
True
 
可以匹配数字,大小写形式的a~f,用来验证十六进制字符

 

字符:

 
元字符
 
匹配内容
匹配除换行符以外的任意字符
\w(word) 匹配字母或数字或下划线
\s(space) 匹配任意的空白符
\d(dight) 匹配数字
\n 匹配一个换行符
\t 匹配一个制表符
\b 匹配一个单词的两端
^ 匹配字符串的开始
$ 匹配字符串的结尾
\W
匹配非字母或数字或下划线
\D
匹配非数字
\S
匹配非空白符
a|b
匹配字符a或字符b
()
匹配括号内的表达式,也表示一个组
[...]
匹配字符组中的字符
[^...]
匹配除了字符组中字符的所有字符

 

量词:

量词
用法说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

. ^ $

正则 待匹配字符 匹配
结果
说明
海. 海燕海娇海东 海燕海娇海东   匹配所有"海."的字符
^海. 海燕海娇海东 海燕 只从开头匹配"海."
  海.$   海燕海娇海东 海东 只匹配结尾的"海.$"

* + ? { }

正则 待匹配字符 匹配
结果
说明
李.? 李杰和李莲英和李二棍子

李杰
李莲
李二

 
?表示重复零次或一次,即只匹配"李"后面一个任意字符
 
李.* 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子
*表示重复零次或多次,即匹配"李"后面0或多个任意字符
李.+ 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子
+表示重复一次或多次,即只匹配"李"后面1个或多个任意字符
李.{1,2} 李杰和李莲英和李二棍子

李杰和
李莲英
李二棍

{1,2}匹配1到2次任意字符

 注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配

正则 待匹配字符 匹配
结果
说明
李.*? 李杰和李莲英和李二棍子

惰性匹配

 字符集[][^]

正则 待匹配字符 匹配
结果
说明
李[杰莲英二棍子]* 李杰和李莲英和李二棍子

李杰
李莲英
李二棍子

 
表示匹配"李"字后面[杰莲英二棍子]的字符任意次
 
李[^和]* 李杰和李莲英和李二棍子

李杰
李莲英
李二棍子

表示匹配一个不是"和"的字符任意次
[\d] 456bdha3

4
5
6
3

表示匹配任意一个数字,匹配到4个结果
[\d]+ 456bdha3

456
3

表示匹配任意个数字,匹配到2个结果

 

李[^和]+   :               李杰   李莲英  李二棍子
[李杰莲英二棍子]  :  李杰   李莲英  李二棍子
李.+?                    :李杰     李莲    李二

分组 ()与 或 |[^]

 身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部🈶️数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:

正则 待匹配字符 匹配
结果
说明
^[1-9]\d{13,16}[0-9x]$ 110101198001017032

110101198001017032

   表示可以匹配一个正确的身份证号
^[1-9]\d{13,16}[0-9x]$ 1101011980010170

1101011980010170

表示也可以匹配这串数字,但这并不是一个正确的身份证号码,它是一个16位的数字
^[1-9]\d{14}(\d{2}[0-9x])?$ 1101011980010170

False

现在不会匹配错误的身份证号了
()表示分组,将\d{2}[0-9x]分成一组,就可以整体约束他们出现的次数为0-1次
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$ 110105199812067023

110105199812067023

表示先匹配[1-9]\d{16}[0-9x]如果没有匹配上就匹配[1-9]\d{14}

 

转义符 \

在正则表达式中,有很多有特殊意义的是元字符,比如\d和\s等,如果要在正则中匹配正常的"\d"而不是"数字"就需要对"\"进行转义,变成'\\'。

在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\d",字符串中要写成'\\d',那么正则里就要写成"\\\\d",这样就太麻烦了。这个时候我们就用到了r'\d'这个概念,此时的正则是r'\\d'就可以了。

正则 待匹配字符 匹配
结果
说明
\d \d  False
因为在正则表达式中\是有特殊意义的字符,所以要匹配\d本身,用表达式\d无法匹配
\\d \d  True
转义\之后变成\\,即可匹配
"\\\\d" '\\d'  True
如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次
r'\\d' r'\d'  True
在字符串之前加r,让整个字符串不转义

 

贪婪匹配

贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配

正则 待匹配字符 匹配
结果
说明
<.*>

<script>...<script>

<script>...<script>
默认为贪婪匹配模式,会匹配尽量长的字符串
<.*?> r'\d'  

<script>
<script>

加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串
几个常用的非贪婪匹配Pattern
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x

就是取前面任意长度的字符,直到一个x出现
.*?x到底用了几个地方:
1,量词:表示皮诶0次或1次
2,在量词之后:是惰性匹配的标志
3,分组命名:(?P<NAME>正则表达式)引用的时候(?P=NAME)
4,findall/split取消分组优先:(?:正则表达式)
就是取前面任意长度的字符,直到一个x出现
量词 : ? + *  {n} {n,} {n,m}   跟在元字符后面
贪婪匹配 : 正则表达式本身就是贪婪匹配的
惰性匹配 : 尽量少匹配,需要在量词的后面加上一个?
            最常见的用法 .*?标识  表示匹配任意长度的字符遇到标识就立刻停止

元字符 量词 [惰性匹配符号?]
看正则表达式的话,怎么区分 ?
    找元字符 元字符后面如果出现量词 那么这个量词就是约束这个元字符出现的次数
    如果元字符后面如果不出现量词 表示这个元字符匹配一次
    如果在量词后面还有? 表示惰性匹配

r'\\n',r'\n'
print('\\n')
print('\\\\n')
print(r'\n')   # 取消字符串中所有转义符的转义作用
print(r'\\n')

在正则表达式中 涉及到特殊转义的内容 都放在r''中
在工具网页上匹配成功之后 在python中匹配不成功 匹配的字符串中又带有'\',那么直接将测试好的内容都放在r''中

 re 模块:

re模块下的常用方法:

import re

ret = re.findall('a', 'eva egon yuan')  # 返回所有满足匹配条件的结果,放在列表里
print(ret) #结果 : ['a', 'a']

ret = re.search('a', 'eva egon yuan').group()
print(ret) #结果 : 'a'
# 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以
# 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。

ret = re.match('a', 'abc').group()  # 同search,不过尽在字符串开始处进行匹配
print(ret) #match 只匹配第一个相当于在正则表达式前加了^
#结果 : 'a'

ret = re.split('[ab]', 'abcd')  # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
print(ret)  # ['', '', 'cd']

ret = re.sub('\d', 'H', 'eva3egon4yuan4', 1)#将数字替换成'H',参数1表示只替换1个
print(ret) #evaHegon4yuan4

ret = re.subn('\d', 'H', 'eva3egon4yuan4')#将数字替换成'H',返回元组(替换的结果,替换了多少次)
print(ret)

obj = re.compile('\d{3}')  #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
ret = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串
print(ret.group())  #结果 : 123

import re
ret = re.finditer('\d', 'ds3sy4784a')   #finditer返回一个存放匹配结果的迭代器
print(ret)  # <callable_iterator object at 0x10195f940>
print(next(ret).group())  #查看第一个结果
print(next(ret).group())  #查看第二个结果
print([i.group() for i in ret])  #查看剩余的左右结果
View Code
re 怎么用??
import re
正则表达式 本身具有处理字符串的能力
如果 要处理的字符串 存在python 的内存里
模块 -> 操作正则表达式
findall  一次性返回所有匹配到的项,直接存在列表中
ret = re.findall('\d+','kjasdgk912798jkshf912847jzhsfk91278')
print(ret)

search  一次只匹配第一个符合条件表达式,以对象的形式返回,通过对象.group()取实际值,如果没有匹配到 会返回None 调用group会报错
ret = re.search('\d+','kjasdg14有058fkjkshfjzhsfk')
print(ret)    # 正则匹配结果的对象
if ret:
    print(ret.group())
macth 同search,不过只匹配开头,相当于在正则表达式前加了'^'
ret = re.match('\d+','3714hello,alex')
print(ret.group())

?:写在一个分组的最开始,表示在findall方法中取消这个分组的优先级
import re
ret = re.findall('\d+(?:\.\d+)?','1.2345+4.3')
print(ret)   #  1.2345   4.3

ret = re.findall('\d+(\.\d+)?','1.2345+4.3')
print(ret)   #  .234   .3

import re
ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
print(ret)
ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')
print(ret)

'aa|bb'.split('|')
split中如果带有分组,会在分割的同时保留被分割内容中带分组的部分
ret = re.split('(\d\d)','alex83wusir38egon20')
print(ret)
(\d\d) ,  \d\d 带()以什么分割并且保留,不带()不保留
ret = re.split('\d\d','alex83wusir38egon20')
print(ret)
sub替换,可是只参数,替换第几个
ret = re.sub('\d+','sb','alex83wusir38egon20',2)
print(ret)
subn   替换结果  返回元组
ret = re.subn('\d+','sb','alex83wusir38egon20')
print(ret)
节省时间
compile   将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
obj = re.compile('\d{3}') 
print(obj)
ret = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串
print(ret.group())
ret = obj.search('abc444eeee') #正则表达式对象调用search,参数为待匹配的字符串
print(ret.group())
finditer 迭代功能的 节省空间的返回一个存放匹配结果的迭代器
 ret = re.finditer('\d+','12aas14jsk38947cjhxj83') print(ret) for i in ret: print(i.group()) 

总结: 基础查找 findall(分组优先显示) search match
替换分割 split(分组保留) sub subn
代码优化 compile finditer

分组命名:
html标签语言
html是一种代码
对于浏览器来说 浏览器和代码之间有一种约定
写在一个标识符之内的代码 可以有一种独立的格式
标识符:
<h1>sgjkagjgdjkf</h1>
给分组起名字 (?P<tag>正则表达式),使用分组的名字(?P=tag)
ret = re.search('<(?P<tag>\w+?)>\w+</(?P=tag)>',"<b>hello</b>")
print(ret.group())
print(ret.group('tag'))

根据分组的索引使用分组 \1,1是分组的索引值
ret = re.search(r'<(\w+?)>\w+</\1>',"<b>hello</b>")
print(ret.group())
print(ret.group(1))

ret = re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
ret.group(1)

ret = re.finditer(r'<(\w+?)>\w+</\1>',"<b>hesakdfkhfkllo</b>")
for i in ret:
    print(i.group(1))
    print(i.group())
?用法:
量词\惰性匹配\取消分组优先\分组命名
匹配所有整数:
ret=re.findall(r"\d+\.\d+|(\d+)","1-2*(60+(-40.35/5)-(-4*3))")
while '' in ret:
    ret.remove('')
print(ret)
re模块是一个和正则表达式相关的模块
正则表达式 和 python没关系
正则表达式:
检测字符串是否符合要求
从大段的文字中找到符合要求的内容
while True:
    number = input('请输入号码:')
    if len(number) == 11 \
        and number .isdigit() \
        and (number .startswith('13')) \
        or number.startswith('14') \
        or number.startswith('15') \
        or number.startswith('18') :
        pass
    else:
        print('不是合法手机号')
View Code
正则表达式本身也和python没有什么关系,就是匹配字符串内容的一种规则。
import re
三种最基础的查找方法
findall :找出全部数字
(正则表达式,待匹配的字符串)返回一个列表,是所有匹配的值,分组优先的问题
取消分组优先显示(?:)
search:从左到右找,找到第一个就返回,不在继续找,找不到返回None
(正则表达式,待匹配的字符串)只返回第一个匹配的结果
如果没有匹配到  返回None  .group()会报错
如果匹配到了  返回一个对象 .group()方法获取结果
match:找以数字开头的,找不到报错
finditer:
finditer compile——进阶用法
分割和替换:split  sub  subn
编译:compile
pattern 正则表达式,string待匹配的字符串,flags=0
import re
ret = re.findall('\d+','eva123egon4yuan567')
print(ret)    #['123', '4', '567']
ret2 = re.search('\d+','eva123egon4yuan567')
print(ret2)#不会直接返回结果,而是返回第一个匹配的变量
print(ret2.group())#使用这个变量,group()得到返回的结果
ret3 = re.search('\d+','eva')
if ret3:
     print(ret3.group())
ret4 = re.match('\d+','321eva123egon4yuan567')
print(ret4)
print(ret4.group())
ret = re.split('\d+','alex27egon18yuan40')
print(ret)  #['alex', 'egon', 'yuan', '']
import re #节省空间
ret = re.finditer('\d','ds3sy4784a')#finditer返回一个存放匹配结果的迭代器
print(ret)  #<callable_iterator object at 0x0000000001E98828>
print(next(ret).group())#查看第一个结果   #3
print(next(ret).group())#查看第二个结果    # 4
print([i.group() for i in ret])#查看所有剩余结果#['7', '8', '4']
import re
***abc***
@@@abc@@@
===abc===
-123-
ret = re.search('(?P<flag>.+).*(?P=flag)','@@@abc@@@')
print(ret.group())
import re
View Code
整数匹配:
exp = '1-2*(60+(-40.35/5)-(-4*3))'
ret = re.findall('\d+\.\d+|(\d+)',exp)
print(ret)  #['1', '2', '60', '', '5', '4', '3']
ret.remove('')
print(ret)#['1', '2', '60', '', '5', '4', '3']
View Code

1 findall的优先级查询:

import re

ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
print(ret)  # ['oldboy']     这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可

ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')
print(ret)  # ['www.oldboy.com']
View Code

2 split的优先级查询:

ret=re.split("\d+","eva3egon4yuan")
print(ret) #结果 : ['eva', 'egon', 'yuan']

ret=re.split("(\d+)","eva3egon4yuan")
print(ret) #结果 : ['eva', '3', 'egon', '4', 'yuan']

#在匹配部分加上()之后所切出的结果是不同的,
#没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
#这个在某些需要保留匹配部分的使用过程是非常重要的。
View Code

flags:

flags有很多可选值:

re.I(IGNORECASE)忽略大小写,括号内是完整的写法
re.M(MULTILINE)多行模式,改变^和$的行为
re.S(DOTALL)点可以匹配任意字符,包括换行符
re.L(LOCALE)做本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用
re.U(UNICODE) 使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag
re.X(VERBOSE)冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释
View Code

 综合练习与扩展:

1、匹配标签

import re


ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")
#还可以在分组中利用?<name>的形式给分组起名字
#获取的匹配结果可以直接用group('名字')拿到对应的值
print(ret.group('tag_name'))  #结果 :h1
print(ret.group())  #结果 :<h1>hello</h1>

ret = re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
#如果不给组起名字,也可以用\序号来找到对应的组,表示要找的内容和前面的组内容一致
#获取的匹配结果可以直接用group(序号)拿到对应的值
print(ret.group(1))
print(ret.group())  #结果 :<h1>hello</h1>
View Code

2、匹配整数:

import re

ret=re.findall(r"\d+","1-2*(60+(-40.35/5)-(-4*3))")
print(ret) #['1', '2', '60', '40', '35', '5', '4', '3']
ret=re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")
print(ret) #['1', '-2', '60', '', '5', '-4', '3']
ret.remove("")
print(ret) #['1', '-2', '60', '5', '-4', '3']
View Code

3、数字匹配

1、 匹配一段文本中的每行的邮箱
      http://blog.csdn.net/make164492212/article/details/51656638

2、 匹配一段文本中的每行的时间字符串,比如:‘1990-07-12’;

   分别取出1年的12个月(^(0?[1-9]|1[0-2])$)、
   一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$

3、 匹配qq号。(腾讯QQ号从10000开始)  [1,9][0,9]{4,}

4、 匹配一个浮点数。       ^(-?\d+)(\.\d+)?$   或者  -?\d+\.?\d*

5、 匹配汉字。             ^[\u4e00-\u9fa5]{0,}$ 

6、 匹配出所有整数
View Code

4、爬虫练习:

 View Code

import re
import json
from urllib.request import urlopen

def getPage(url):
    response = urlopen(url)
    return response.read().decode('utf-8')

def parsePage(s):
    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):
    url = 'https://movie.douban.com/top250?start=%s&filter=' % num
    response_html = getPage(url)
    ret = parsePage(response_html)
    print(ret)
    f = open("move_info7", "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
简化版
爬虫:
我要获取这一整个网页上的内容--网址
我要获取网页上的某些信息--网址
如何获取到这些网页上的内容
先看到 复制粘贴 —— 全是人工
让程序自己去获取数据 —— 比较智能
网页上的内容
拿到刚刚那个网页的源代码
找源代码中的规律
从源代码中拿到我想要的数据
import re
import json
from urllib.request import urlopen

def getPage(url):
    response = urlopen(url)
    return response.read().decode('utf-8')

def parsePage(s):
    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):
    url = 'https://movie.douban.com/top250?start=%s&filter=' % num
    response_html = getPage(url)
    ret = parsePage(response_html)
    print(ret)
    f = open("move_info7", "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
View Code

 

 

posted on 2018-07-12 14:24  liangliang123456  阅读(228)  评论(0编辑  收藏  举报

导航