第五章 python中正则表达式的使用
第一节 正则表达式的一些介绍1)掌握正则表达式的案例
2)写一个小爬虫
3)正则表达式(或RE)是一个小型的、高度专业化的编程语言,(在python中)它内嵌在python中,并通过re模块实现。
- 可以为想要匹配的相应字符串集指定规则
- 该字符串集可能包含英文语句、e-mail地址、命令或任何你想搞定的东西
- 可以问诸如“这个字符串匹配该模式吗?”
- “在这个字符串中是否有部分匹配该模式呢?”
- 你也可以使用RE以各种方式来修改或分割字符串。
4)正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行
5)正则表达式语言相对小型和受限(功能有限)
- 并非所有字符串处理都能用正则表达式完成
6)字符匹配
- 普通字符
a.大多数字母和字符一般都会和自身匹配
b.如正则表达式test会和字符串“test”完全匹配
- 元字符
. ^ $ * + ? { } [ ] \ | ( )
A.[ ]
- 常用来指定一个字符集:;
- 元字符在字符集中不起作用:
- 补集匹配不在区间范围内的字符:[^5]
B.^
- 匹配行首。除非设置MULTILINE标志,它只是匹配字符串的开始。在MULTILINE模式里,它也可以直接匹配字符串中的每个换行。
- 匹配行尾,行尾被定义为要么是字符串尾,要么是一个换行字符后面的任何位置。
>>> import re
>>> s = r'abc'
>>>
>>> re.findall(s,"aaaaaaaaaaaaa")
[]
>>> re.findall(s,"a")
[]
>>> re.findall(s,"abcaaaaaaaaaaaaaaaaaaa")
['abc']
>>> re.findall(s,"abcaaaaaaaaaaaaaaaaaaabc")
['abc', 'abc']
>>>
>>> st = "top tip tqp twp tep"
>>>
>>> res = r"top"
>>> re.findall(res,st)
['top']
>>> res = r"tip"
>>> re.findall(res,st)
['tip']
>>>
>>> res = r"tp"
>>> re.findall(res,st)
['top', 'tip']
>>> res = r"t[^io]p"
>>> re.findall(res,st)
['tqp', 'twp', 'tep']
现在我们以匹配
>>> s = "hello world,hello boy"
>>>
>>> r = r"hello"
>>> re.findall(r,s)
['hello', 'hello']
>>> re.findall(r,'t^')
[]
>>> r = "t" 以什么结尾
>>>
>>> re.findall(r,'ta')
['ta']
>>> re.findall(r,'tb')
['tb']
>>> re.findall(r,'tax')
['ta']
>>> re.findall(r,'t$')
['t$']
>>> r = r"xx"
>>> r = r"xx" #通过正则表达式我们可以这样写
>>> r = r"xx"
>>> r = r"xx">>> re.findall(r,'x1x')
['x1x']
>>> re.findall(r,'x2x')
['x2x']
>>> re.findall(r,'x9x')
['x9x']
第二节 >>> r = r"^abc"
>>>
>>> import re
>>> re.findall(r,'abc')
['abc']
>>> r = r"\^abc" 这里我们使用\来做转义
>>> import re
>>> re.findall(r,'^abc ^abc ^abc') 这里就可以匹配到r里面的^abc这个字符串['^abc', '^abc', '^abc']
1)\
- 反斜杠后面可以加不同的字符以表示不同特殊意义
- 也可以用于取消所有的元字符:\[ 或 \\
\d 匹配任何十进制数;它相当于类.
\D 匹配任何非数字字符;它相当于类[^0-9]。
\s 匹配任何空白字符;它相当于类[\t\n\r\f\v]。
\w 匹配任何字母数字字符;它相当于类。
\W 匹配任何非字母数字字符;它相当于类[^a-zA-Z0-9]。
>>> r = ""
>>> re.findall(r,'1234567890')
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
>>> r = "\d" 这里我们使用了\d,它就表示
>>> re.findall(r,'1234567890')
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
下面我们做一个对电话号码进行匹配查询:
>>> import re
>>> 010-12345678
-12345670
>>> r= r"^010-\d\d\d\d\d\d\d\d"
>>> re.findall(r,'010-87654321')
['010-87654321']
>>> r= r"^010-\d{8}" 我们可以使用{}来指定前面的\d出现的次数,可以达到同样的效果
>>> re.findall(r,'010-87654321')
['010-87654321']
2)重复
- 正则表达式第一功能是能够匹配补丁长的字符集,另一个功能就是你可以指定正则表达式的一部分的重复次数。
3)*
- 指定前一个字符可以被匹配零次或多次,而不是只有一次。匹配引擎会试着重复尽可能多的次数(不超过整数界定范围,20亿)
- a*b-- "abcbd"
>>> r = r"ab*"
>>> re.findall(r,'a')
['a']
>>> re.findall(r,'ab')
['ab']
>>> re.findall(r,'abbbbb')
['abbbbb']
4)+
- 表示匹配一或更多次。
- 注意*和+之间的不同;*匹配零或更多次,所以可以根本就不出现,而+则要求至少出现依次
5)?
- 匹配一次或零次;你可以认为它用于标识某事物是可选的
>>> r = r"ab+"
>>> re.findall(r,'a')
[]
>>> re.findall(r,'ab')
['ab']
>>> re.findall(r,'abb')
['abb']
>>> re.findall(r,'abbbbbbb')
['abbbbbbb']
下面我们来匹配一个字符串电话号码:
>>> r = r"^010-?\d{8}$" ?在这里表示前面的那个-是可有可无的
>>> re.findall(r,'01012345678')
['01012345678']
>>> re.findall(r,'010-12345678')
['010-12345678']
>>> re.findall(r,'010--12345678') 我们看一下这样就不行了,这样就达到了我们的要求
[]
在正则表达式匹配中有一个贪婪模式和一个非贪婪模式:
>>> r = r"ab+" 贪婪模式,将后面多个b同时会输出>>>
>>> re.findall(r,'abbbbbbbbb')
['abbbbbbbbb']
>>> r = r"ab+?" 非贪婪模式做最小匹配
>>> re.findall(r,'abbbbbbbbb')
['ab']
6){m,n}
- 其中m和n是十进制整数。该限定符的意思是至少有m个重复,至多到n个重复。a/{1,3}b
- 忽略m会认为下边界是0,而忽略n的结果将是上边界为无穷大(实际上是20亿)
- {0,}等同于*,{1,}等同于+,而{0,1}则与?相同。如果可以的话,最好使用*,+,或?
>>> r = r"a{1,3}"
>>> re.findall(r,'a')
['a']
>>> re.findall(r,'b')
[]
>>> re.findall(r,'aa')
['aa']
>>> re.findall(r,'aaa')
['aaa']
>>> re.findall(r,'aaaa')
['aaa', 'a']
第三节 正则表达式常用函数
1)使用正则表达式
- re模块提供了一个正则表达式引擎的接口,可以让你将REstring编译成对象并用它们来进行匹配。
- 编译正则表达式
hy@hy:~$ python
>>> import re
>>> p = re.compile('ab*')
>>> print p
以我们上节的匹配电话号码的方法
>>> import re
>>>
>>> r1 = r"\d{3,4}-?\d{8}"
>>> re.findall(r1,"010-12345678")
['010-12345678']
>>> re.findall(r1,"010-1234567")
[]
>>> p_tel=re.compile(r1) 我们将前面定义好的这个r1进行编译,>>> p_tel
>>> p_tel.findall('010-12345678') 编译好的正则要比未编译的正则匹配起来的速度要快很多
['010-12345678']
>>> p_tel.findall('010-123456789')
['010-12345678']
- re.compile90也接受可选的标志参数,常用来实现不同的特殊功能和语法变更
hy@hy:~$ python
>>> py_re = re.compile(r'py',re.I)
>>> py_re.findall('PY')
['PY']
>>> py_re.findall('py')
['py']
>>> py_re.findall('Py')
['Py']
>>> py_re.findall('pY')
['pY']
- 反斜杠的麻烦
a.字符串前面加“r”反斜杠就不会被任何特殊方式处理
http://note.youdao.com/yws/res/4859/5D93531AA3204E86AF875312B01B238D
b.执行匹配
- `RegexObject`实例有一些方法和属性,完整的列表可查询Python Library Reference
http://note.youdao.com/yws/res/4863/D9DAAF638F6B47F79C8C8281B82645BA
如果没有匹配到的话,match()和search()将返回None。
如果成功的话,就会返回一个`MatchObject`实例,
>>> py_re.match('py hello') 我们可以看到使用match去匹配的时候,它会返回一个对象 这个对象当中其实是包含py这个字符串的
>>> py_re.match('hello') 当我们去掉py这个字符串或将它放在后面的时候它返回值为空
>>> py_re.match('hello py')
下面我们使用search去匹配,我们会发现它不管匹配元素在什么位置,只要有都可以找到:
>>> x = py_re.match('hello py')
>>> if x :
... pass
...
>>> py_re.search('hello py')
>>> py_re.search('py hello')
>>> py_re.search('py hello ')
下面我们看一下finditer这个方法: >>> py_re.findall('hello py hello')
['py']
>>> py_re.findall('hello py hello py py')
['py', 'py', 'py']
>>> py_re.finditer('hello py hello py py')
它返回的是一个迭代器的对象,
>>> x = py_re.finditer('hello py hello py py') 这里我们定义x是他的一个迭代对象
>>> x.next() 迭代对象,我们想看他里面的方法的话,可以使用它里面的一个方法next()
- MatchObject实例方法
http://note.youdao.com/yws/res/4885/A6BA860964164892B148098E93230D3E
c.实际程序中,最常见的作法是将`MatchObject`保存在一个变量里,然后检查它是否为None
>>> x = py_re.match('py hello')
>>> x
>>> x.group() 使用group()这个方法就可以看到py这个数据了
'py'
d.模块级函数
- re模块也提供了顶级函数调用如match()、search()、sub()、subn()、split()、findall()等
>>> rs = r'p..y'
>>> re.sub(rs,'python','paby pooy phky ccccc') 输出符合p..y 规则的字符串被替换
'python python python ccccc' 这里我们看到这个字符串的输出结果符合p..y这个规则的替换
>>> re.subn(rs,'python','paby pooy phky ccccc') 这个会输出替换了多少次
('python python python ccccc', 3)
>>> ip = "1.2.3.4"
>>> ip.split('.') 我们可以对ip以'.'进行切割['1', '2', '3', '4']
>>> s = "123+456-789*000"
>>>
>>> re.split(r'[\+\-\*]',s) 我们使用正则对其进行不同分隔符的指定
['123', '456', '789', '000']
>>> dir(re) 列出一些re的方法['DEBUG', 'DOTALL', 'I', 'IGNORECASE', 'L', 'LOCALE', 'M', 'MULTILINE', 'S', 'Scanner', 'T', 'TEMPLATE', 'U', 'UNICODE', 'VERBOSE', 'X', '_MAXCACHE', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__version__', '_alphanum', '_cache', '_cache_repl', '_compile', '_compile_repl', '_expand', '_pattern_type', '_pickle', '_subx', 'compile', 'copy_reg', 'error', 'escape', 'findall', 'finditer', 'match', 'purge', 'search', 'split', 'sre_compile', 'sre_parse', 'sub', 'subn', 'sys', 'template']
>>> help(re) re的更多用法可以使用help去查看
第四节 正则表达式内置属性及分组
1)编译标志-flags
http://note.youdao.com/yws/res/4904/197648EC8F6C4E529C0FDE66C72FD4D3>>> r1 = r"py.net"
>>>
>>> re.findall(r1,'py.net')
['py.net']
>>> re.findall(r1,'pyonet')
['pyonet']
>>> re.findall(r1,'py\nnet')
[]
>>> re.findall(r1,'py\nnet',re.S)
['py\nnet']
>>> re.findall(r1,'py\tnet',re.S)
['py\tnet']
M多行匹配,影响^或$:
>>> import re
>>> s = """
... hello py
... py hello
... hello py hello
... py hello
... """
>>> s
'\nhello py\npy hello\nhello py hello\npy hello\n'>>> r = r"^py"
>>> re.findall(r,s,re.M) 使用正则的M这个多行匹配属性['py', 'py']
我们使用re.X匹配正则:
>>> tel = r"""
... \d{3,4}
... -?
... \d{8}
... """
>>>
>>> re.findall(tel,'010-123456787')
[]>>> re.findall(tel,'010-123456787',re.X) 使用正则的X属性匹配电话号码
['010-12345678']
可以使用VERBOSE这个属性如上面的,下面是对相应的属性做的相应的说明
>>> charef = re.compile(r"""
... (
... +[^0-9] #Decimal form
... |0+[^0-7] #Octal form
... |x+[^0-9a-fA-F] #Hexadecimal form
... """,re.VERBOSE)
2)分组
- "("和")"
>>> email=r"\w{3}@\w+(\.com|\.cn)" 我们在这里定义一个email以.com或.cn结尾>>> 这里我们(定义了一个分组)
>>> re.match(email,'zzz@hyserver.com') 匹配以.com结尾成功
>>> re.match(email,'zzz@hyserver.cn') 匹配以.cn结尾成功
>>> re.match(email,'zzz@hyserver.org') 匹配以.org结尾失败
>>>
>>> re.findall(email,'zzz@hyserver.com') 当我们做匹配的时候['.com'] 还会优先的返回分组当中的数据
下面我们看一下其他类型的匹配:
>>> s = r"""
... hhsdj dskj hello src=csvt yes jdjsds
... djhsjk src=123 yes jdsa
... src=234 yes
... hello src=python yes ksa
... """
>>> print s
hhsdj dskj hello src=csvt yes jdjsds
djhsjk src=123 yes jdsa
src=234 yes
hello src=python yes ksa
>>> r1 = r"hello src.+ yes"
>>> re.findall(r1,s) 通过r1这样的匹配可以很好的找到里面对应的数据['hello src=csvt yes', 'hello src=python yes']
>>> r1= r"hello src=(.+) yes" 我们也可以将里面的.+定义成一个分组然后进行相应的匹配,它就会只返回=后面的数据,这样我们在做爬虫的时候,对网页的某个值进行提取
>>> re.findall(r1,s)
['csvt', 'python']
第五节 一个小爬虫
1)下载贴吧或空间中所有图片
hy@hy:~/Documents/py/jpg$ vim getjpg.py
1 #!/usr/bin/python
2 import re
3 import urllib
4
5 def getHtml(url):
6 page = urllib.urlopen(url)
7 html = page.read()
8 return html
9
10 def getImg(html):
11 reg = r'src="(.*?\.jpg)" width'
12 imgre = re.compile(reg)
13 imglist = re.findall(imgre,html)
14 x = 0
15 for imgurl in imglist:
16 urllib.urlretrieve(imgurl,'%s.jpg' % x)
17 x+=1
18 return imglist
19
20 html = getHtml("http://image.baidu.com/i?tn=baiduimage&ct=201326592&lm=-1&cl=2&word=%CD%BC%C6%AC&fr=ala&ala=1&alatpl=others&pos=0")
21 getImg(html)
hy@hy:~/Documents/py/jpg$ python getjpg.py
hy@hy:~/Documents/py/jpg$ ls
0.jpg 12.jpg15.jpg18.jpg20.jpg23.jpg26.jpg3.jpg6.jpg9.jpg
10.jpg13.jpg16.jpg19.jpg21.jpg24.jpg27.jpg4.jpg7.jpggetjpg.py
11.jpg14.jpg17.jpg1.jpg 22.jpg25.jpg2.jpg 5.jpg8.jpg
这样我们就将这个网页上所有以.jpg结尾的图片下下来了。
第六节 数据结构之深拷贝和浅拷贝
python对内存的使用
- 所谓浅拷贝就是对引用的拷贝(只拷贝父对象)
- 所谓深拷贝就是对象的资源的拷贝
- 解释一个例子:
>>> a =
>>>
>>> b = a
>>> b
>>> a
>>>
>>> id(a)
139845710543400
>>> id(b)
139845710543400
>>> a.append('d') 因为b和a指向的是同一个地址空间,所以我们在改变a的时候b也会改变>>> a
>>> b
>>> b.append(4) 我们去改变b也是同样的道理>>> b
>>> a
下面我们来看一下如何实现拷贝
>>> import copy 首先我们要导入一个copy模块>>>
>>> a = ]
>>> b=a
>>>
>>> c = copy.copy(a) 调用copy这个模块里面的copy方法>>> b
]
>>> c
]
>>> id(a)
139845710626400
>>> id(b)
139845710626400
>>> id(c) 由于c是copy过来的,所以它的地址空间和a是不同的139845710722040
>>> a.append('d') 然后我们通过给a里面增加一个值,看看c是否改变>>> a
, 'd']
>>> b
, 'd']
>>> c
]在这里我们发现c列表里面的值没有改变,这里的拷贝就是一个浅拷贝,只拷贝父对象
>>> id(a) 但是我们会发现a和c里面的元素还是指向同一个空间的16290136
>>> id(c)
16290136
下面让我们看一下拷贝的时候的内存空间的分配: http://note.youdao.com/yws/res/4962/3DCC6B23CDF14CAAA56DB81BA3463D8A
>>> a.append('d') 我们来测试,改变a里面的元素,看看c元素是否改变>>> a
, 'd']
>>> c
] 这里我们发现c里面的相应的元素也改变了
下面我们做一个深拷贝:
>>> d=copy.deepcopy(a)
>>> d
, 'd']
>>> id(a) 我们来看看a和d中的一个元素的地址空间139845710626688
>>> id(d)
139845710721824 我们发现这时已经不是同一个地址空间了
>>> a.append('e') 我们再去测试给a增加一个元素,看看d是否有变化>>> a
, 'd', 'e']
>>> d
, 'd'] 我们可以看到d没有变化
页:
[1]