第一节 正则表达式的一些介绍
1)掌握正则表达式的案例
2)写一个小爬虫
3)正则表达式(或RE)是一个小型的、高度专业化的编程语言,(在python中)它内嵌在python中,并通过re模块实现。
- 可以为想要匹配的相应字符串集指定规则
- 该字符串集可能包含英文语句、e-mail地址、命令或任何你想搞定的东西
- 可以问诸如“这个字符串匹配该模式吗?”
- “在这个字符串中是否有部分匹配该模式呢?”
- 你也可以使用RE以各种方式来修改或分割字符串。
4)正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行
5)正则表达式语言相对小型和受限(功能有限)
- 并非所有字符串处理都能用正则表达式完成
6)字符匹配
- 普通字符
a.大多数字母和字符一般都会和自身匹配
b.如正则表达式test会和字符串“test”完全匹配
- 元字符
. ^ $ * + ? { } [ ] \ | ( )
A.[ ]
- 常用来指定一个字符集:[abc];[a-z]
- 元字符在字符集中不起作用:[akm$]
- 补集匹配不在区间范围内的字符:[^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"t[io]p"
>>> 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[abc$]" 以什么结尾
>>>
>>> re.findall(r,‘ta‘)
[‘ta‘]
>>> re.findall(r,‘tb‘)
[‘tb‘]
>>> re.findall(r,‘tax‘)
[‘ta‘]
>>> re.findall(r,‘t$‘)
[‘t$‘]
>>> r = r"x[0123456789]x"
>>> r = r"x[0-9]x" #通过正则表达式我们可以这样写
>>> r = r"x[a-zA-Z]x"
>>> r = r"x[a-zA-Z0-9]x"
>>> 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 匹配任何十进制数;它相当于类[0-9].
\D 匹配任何非数字字符;它相当于类[^0-9]。
\s 匹配任何空白字符;它相当于类[\t\n\r\f\v]。
\w 匹配任何字母数字字符;它相当于类[a-zA-Z0-9_]。
\W 匹配任何非字母数字字符;它相当于类[^a-zA-Z0-9]。
>>> r = "[0-9]"
>>> re.findall(r,‘1234567890‘)
[‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘, ‘0‘]
>>> r = "\d" 这里我们使用了\d,它就表示[0-9]
>>> 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[bcd]*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编译成对象并用它们来进行匹配。
- 编译正则表达式
[email protected]:~$ python
>>> import re
>>> p = re.compile(‘ab*‘)
>>> print p
<_sre.SRE_Pattern object at 0x7f66cc831c00>
以我们上节的匹配电话号码的方法
>>> 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
<_sre.SRE_Pattern object at 0x7f66cc7d89d0>
>>> p_tel.findall(‘010-12345678‘) 编译好的正则要比未编译的正则匹配起来的速度要快很多
[‘010-12345678‘]
>>> p_tel.findall(‘010-123456789‘)
[‘010-12345678‘]
- re.compile90也接受可选的标志参数,常用来实现不同的特殊功能和语法变更
[email protected]:~$ 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”反斜杠就不会被任何特殊方式处理
b.执行匹配
- `RegexObject`实例有一些方法和属性,完整的列表可查询Python Library Reference
如果没有匹配到的话,match()和search()将返回None。
如果成功的话,就会返回一个`MatchObject`实例,
>>> py_re.match(‘py hello‘) 我们可以看到使用match去匹配的时候,它会返回一个对象
<_sre.SRE_Match object at 0x7f66cc6dd440> 这个对象当中其实是包含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‘)
<_sre.SRE_Match object at 0x7f66cc6dd4a8>
>>> py_re.search(‘py hello‘)
<_sre.SRE_Match object at 0x7f66cc6dd440>
>>> py_re.search(‘py hello ‘)
<_sre.SRE_Match object at 0x7f66cc6dd4a8>
下面我们看一下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‘)
<callable-iterator object at 0x7f66cc6e8a50> 它返回的是一个迭代器的对象,
>>> x = py_re.finditer(‘hello py hello py py‘) 这里我们定义x是他的一个迭代对象
>>> x.next() 迭代对象,我们想看他里面的方法的话,可以使用它里面的一个方法next()
<_sre.SRE_Match object at 0x7f66cc6dd4a8>
- MatchObject实例方法
c.实际程序中,最常见的作法是将`MatchObject`保存在一个变量里,然后检查它是否为None
>>> x = py_re.match(‘py hello‘)
>>> x
<_sre.SRE_Match object at 0x7f66cc6dd440>
>>> 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
>>> 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]+[^0-9] #Decimal form
... |0[0-7]+[^0-7] #Octal form
... |x[0-9a-fA-F]+[^0-9a-fA-F] #Hexadecimal form
... """,re.VERBOSE)
2)分组
- "("和")"
>>> email=r"\w{3}@\w+(\.com|\.cn)" 我们在这里定义一个email以.com或.cn结尾
>>> 这里我们(定义了一个分组)
>>> re.match(email,‘[email protected]‘) 匹配以.com结尾成功
<_sre.SRE_Match object at 0x7f30724615d0>
>>> re.match(email,‘[email protected]‘) 匹配以.cn结尾成功
<_sre.SRE_Match object at 0x7f3072461648>
>>> re.match(email,‘[email protected]‘) 匹配以.org结尾失败
>>>
>>> re.findall(email,‘[email protected]‘) 当我们做匹配的时候
[‘.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)下载贴吧或空间中所有图片
[email protected]:~/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)
[email protected]:~/Documents/py/jpg$ python getjpg.py
[email protected]:~/Documents/py/jpg$ ls
0.jpg 12.jpg 15.jpg 18.jpg 20.jpg 23.jpg 26.jpg 3.jpg 6.jpg 9.jpg
10.jpg 13.jpg 16.jpg 19.jpg 21.jpg 24.jpg 27.jpg 4.jpg 7.jpg getjpg.py
11.jpg 14.jpg 17.jpg 1.jpg 22.jpg 25.jpg 2.jpg 5.jpg 8.jpg
这样我们就将这个网页上所有以.jpg结尾的图片下下来了。
第六节 数据结构之深拷贝和浅拷贝
python对内存的使用
- 所谓浅拷贝就是对引用的拷贝(只拷贝父对象)
- 所谓深拷贝就是对象的资源的拷贝
- 解释一个例子:
>>> a = [1,2,3,‘a‘,‘b‘,‘c‘]
>>>
>>> b = a
>>> b
[1, 2, 3, ‘a‘, ‘b‘, ‘c‘]
>>> a
[1, 2, 3, ‘a‘, ‘b‘, ‘c‘]
>>>
>>> id(a)
139845710543400
>>> id(b)
139845710543400
>>> a.append(‘d‘) 因为b和a指向的是同一个地址空间,所以我们在改变a的时候b也会改变
>>> a
[1, 2, 3, ‘a‘, ‘b‘, ‘c‘, ‘d‘]
>>> b
[1, 2, 3, ‘a‘, ‘b‘, ‘c‘, ‘d‘]
>>> b.append(4) 我们去改变b也是同样的道理
>>> b
[1, 2, 3, ‘a‘, ‘b‘, ‘c‘, ‘d‘, 4]
>>> a
[1, 2, 3, ‘a‘, ‘b‘, ‘c‘, ‘d‘, 4]
下面我们来看一下如何实现拷贝
>>> import copy 首先我们要导入一个copy模块
>>>
>>> a = [1,2,3,[‘a‘,‘b‘,‘c‘]]
>>> b=a
>>>
>>> c = copy.copy(a) 调用copy这个模块里面的copy方法
>>> b
[1, 2, 3, [‘a‘, ‘b‘, ‘c‘]]
>>> c
[1, 2, 3, [‘a‘, ‘b‘, ‘c‘]]
>>> id(a)
139845710626400
>>> id(b)
139845710626400
>>> id(c) 由于c是copy过来的,所以它的地址空间和a是不同的
139845710722040
>>> a.append(‘d‘) 然后我们通过给a里面增加一个值,看看c是否改变
>>> a
[1, 2, 3, [‘a‘, ‘b‘, ‘c‘], ‘d‘]
>>> b
[1, 2, 3, [‘a‘, ‘b‘, ‘c‘], ‘d‘]
>>> c
[1, 2, 3, [‘a‘, ‘b‘, ‘c‘]] 在这里我们发现c列表里面的值没有改变,这里的拷贝就是一个浅拷贝,只拷贝父对象
>>> id(a[0]) 但是我们会发现a和c里面的元素还是指向同一个空间的
16290136
>>> id(c[0])
16290136
下面让我们看一下拷贝的时候的内存空间的分配:
>>> a[3].append(‘d‘) 我们来测试,改变a里面的元素,看看c元素是否改变
>>> a
[1, 2, 3, [‘a‘, ‘b‘, ‘c‘, ‘d‘], ‘d‘]
>>> c
[1, 2, 3, [‘a‘, ‘b‘, ‘c‘, ‘d‘]] 这里我们发现c里面的相应的元素也改变了
下面我们做一个深拷贝:
>>> d=copy.deepcopy(a)
>>> d
[1, 2, 3, [‘a‘, ‘b‘, ‘c‘, ‘d‘], ‘d‘]
>>> id(a[3]) 我们来看看a和d中的一个元素的地址空间
139845710626688
>>> id(d[3])
139845710721824 我们发现这时已经不是同一个地址空间了
>>> a.append(‘e‘) 我们再去测试给a增加一个元素,看看d是否有变化
>>> a
[1, 2, 3, [‘a‘, ‘b‘, ‘c‘, ‘d‘], ‘d‘, ‘e‘]
>>> d
[1, 2, 3, [‘a‘, ‘b‘, ‘c‘, ‘d‘], ‘d‘] 我们可以看到d没有变化