re模块和正则表达式
re模块和正则表达式
正则 —— 通用的,处理 字符串
正则表达式
正则是一种处理文字的规则
给我们提供一些规则,让我们从杂乱无章的文字中提取有效信息
模块
它只是我们使用python去操作一些问题的工具而已,和要操作的这个东西本身是两件事情
re模块 —— python使用正则
正则规则
需要记忆的特别多:两大类
测试工具http://tool.chinaz.com/regex/
[ ]字符组
表示在一个字符的位置可以出现的所有情况的集合就是一个字符组
# 表示数字的字符组: # [0123456789] # [0-9] # [2-8] # 简写模式必须由小到大 # 表示字母的字符组: # [abcd] # [a-z] # [A-Z]
元字符
# . 匹配除了换行符以外的任意字符 # \w 匹配字母或数字或下划线 word # \s 匹配任意的空白符 space # \d 匹配数字 digit # \n 匹配一个换行符 next # \t 匹配一个制表符 table # \b 匹配一个单词的结尾 # ^ 匹配字符串的开始 # $ 匹配字符串的结尾 # \W 匹配非字母或数字或下划线 # \D 匹配非数字 # \S 匹配非空白符 # a|b 匹配字符a或字符b # () 匹配括号内的表达式,也表示一个组 # [] 匹配字符组中的字符 # [^]匹配除了字符组中字符的所有字符 # 表示匹配任意字符:[\w\W][\d\D][\s\S]
量词
# * 重复零次或多次 # + 重复一次或多次 # ?重复零次或一次 # {n} 重复n次 # {n,} 重复n次或更多次 # {n,m} 重复n到m次
正则匹配:字符量词非贪婪标志
字符:字符、字符组、元字符 表示一个字符位置上可以出现的内容
匹配身份证号实例
15位:首位不能为零,数字组成
18位:首位不能为零,前17位是数字,最后一位可以是数字或者x
# ^[1-9]\d{14}(\d{2}[0-9x])?$ # ^([1-9]\d{16}[0-9x]|[1-9]\d{14})$
r'\\n' --> r'\n'
在在线工具中能执行,放到python的字符串中表示成r就可以正常的执行了
.*? 非贪婪匹配
.*?t 遇到一个t马上停,经典用法
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' # search从左到右依次找,找到一个就回来,需要使用group()获取返回值 # 如果re.search找不到,就返回None,使用group会报错 ret = re.match('a', 'abc').group() print(ret) # match从头开始配置,匹配上了需要使用group()来获取返回值 # 匹配不上返回None,使用group会报错 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 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]) # 查看剩余的左右结果
findall和split的优先级
ret = re.findall(r'www.(baidu|oldboy).com', 'www.oldboy.com') print(ret) # ['oldboy'] 这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可 # 分组优先:优先显示括号内部的内容 # 取消分组优先 ?: ret = re.findall(r'www.(?:baidu|oldboy).com', 'www.oldboy.com') print(ret) # ['www.oldboy.com'] ret=re.split("(\d+)","eva3egon4yuan") print(ret) # 结果 :['eva', '3', 'egon', '4', 'yuan'] # 在匹配部分加上()之后所切出的结果是不同的, # 没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项, # 这个在某些需要保留匹配部分的使用过程是非常重要的。
search分组命名和引用
# 分组命名和组的引用 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>
匹配整数
# 匹配整数 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']
爬虫练习
import re 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