sxyzy 发表于 2015-12-15 10:56:10

第五章 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]
查看完整版本: 第五章 python中正则表达式的使用