爬虫之正则表达式学习

网络爬虫之Requests库入门
信息组织及分析之Beautiful Soup
从先前的文章中已经介绍了爬虫的流程,先用requests框架获取HTML文本,用beautifulsoup4库的BeautifulSoup类解析标签树,通过类方法获取指定结构下的内容,此时我们还需要在内容中找到我们最想要的部分,正则表达式便是最便捷且有效的文本过滤方式。

正则表达式(regex)

正则表达式是一种文本特征的匹配模板,简洁表达一组字符串典型特征用于文本内容过滤获取我们想要的特定部分。re库是 Python的正则表达式标准库,下面将介绍正则表达式的书写格式和re库的基本使用。

Python中的正则表达式匹配流程
  1. import re导入re库
  2. re.compile()方法用字符串构建Regex对象
  3. 向Regex对象的search()方法传入字符串,返回包含所需字符串的Match对象
  4. 调用Match对象的group()方法,返回匹配文本的字符串
# 简单的正则表达式匹配
import re
# 用complie函数构建正则表达式对象
phoneRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
# Regex对象调用search函数对字符串进行匹配返回一个Match对象
matchObject = phoneRegex.search("School's number is 123-456-7890")
# 以上代码re.compile+regex.search等价于re.search()
matchObject = re.search(r'\d\d\d-\d\d\d-\d\d\d\d',"School's number is 123-456-7890")
print(type(matchObject))
print(matchObject.group())

注意:在第2步中用在字符串前面用r表明是原始字符串
为了表达一些特殊字符字符串引入了转义的机制;而正则表达式同样需要使用部分字符如*、()、?等来表示特定的解析方式,在匹配这些字符原本内涵时需要加转义字符\。在python程序编译时,字符串都会被解析器进行转义检查获取原始字符串,在compiler方法中对正则表达式字符串再进行正则转义。

# 正则转义
string = 'd:\\abc\\n'
print(string)

import re
tran_string1 = re.match('d:\\\\',string).group()
print(tran_string1)

tran_string2 = re.match(r'd:\\',string).group()
print(tran_string2)
  • 分组匹配
    如果我们希望提取出匹配内容的特定部分如电话号码中的区号、姓名中的姓,可以利用()对正则表达式进行分组。Metch对象中有一个regs属性记录了整体匹配和分组匹配的初末索引值元组,第一个便是整体匹配,因此输入group(1)会返回整个匹配的文本。
# 分组匹配
import  re
# 利用()对正则表达式进行分组
phoneRegex = re.compile(r'(\d\d\d-)(\d\d\d)-(\d\d\d\d)')
# search函数是找到第一个匹配项
matchObject = phoneRegex.search("School's number is 123-456-7890")
# regex.group()方法传入0和不传入参数都是返回整个匹配的文本
print(matchObject.group())
print(matchObject.group() == matchObject.group(0))
print(matchObject.group(1))
# regex.group()方法获取包含所有分组的元组
print(matchObject.groups())
for group in matchObject.groups():
    print(group)
  • 管道匹配
# 管道匹配用于匹配多个表达式中的一个(hero或者是people)
import re
pileRegex = re.compile(r'hero|people')
matchObject = pileRegex.search("hero and people")
print(matchObject.group())
matchObject = pileRegex.search("people and hero")
print(matchObject.group())

利用管道匹配符号|和分组匹配可以简易表达某个前缀下的多个表达式

# 管道匹配和分组匹配构建前缀li下的lihaha、lihuanhuan、lidada
import re
qianRegex = re.compile(r'li(haha|huanhuan|dada)')
matchObject = qianRegex.search("I'm lihaha")
print(matchObject.group())
matchObject = qianRegex.search("I'm lihuanhuan" )
print(matchObject.group())
print(matchObject.group(1))
  • 可选匹配(匹配零次或者一次)
    在有些情况下,信息并不总是按照格式那么完备,存在部分缺省如电话号码中没有了区号,我们如何兼备有区号和没有区号的电话,就可以在电话模板中将区号设为可选。
# 可选匹配
import re
kexuanRegex = re.compile(r'(\d\d\d-)?\d\d\d-\d\d\d\d')
matchObject = kexuanRegex.search("123-456-7890")
print(matchObject.group())
matchObject = kexuanRegex.search("456-7890")
print(matchObject.group())
  • 可选匹配+重复匹配(匹配零次或者多次)
    用星号*实现零次或者多次匹配,指定部分完全不存在或者多次重复
# 可选匹配+重复匹配(匹配零次或者多次)
import re
xingRegex = re.compile(r'Ba(wo)*man')
matchObject = xingRegex.search("Baman")
print(matchObject.group())
print(xingRegex.search("Bawowoman").group() is not None)
print(xingRegex.search("Bawowowoman").group() is not None)

重复匹配

用加号+可以用于匹配一次或者多次,零次是不匹配的

# 重复匹配
import re
chongRegex = re.compile(r'Ba(wo)+man')
matchObject = chongRegex.search("Baman")
print(matchObject is None)
print(chongRegex.search("Bawoman").group() is not None)

指定次数匹配

在分组后面利用花括号指定分组出现的特定次数

# 指定次数匹配
import re
limitRegex = re.compile(r'Ba(wo){3}man')
matchObject = chongRegex.search("Bawowowoman")
print(matchObject.group())
print(limitRegex.search("Bawowoman") is None)
  • 贪心匹配与非贪心匹配
    Python中的正则表达式默认是贪心匹配模式,可以在特定模块后加上?来声明非贪心匹配
# 贪心匹配与非贪心匹配
import re
tanxinRegex = re.compile(r'Ba(wo){3,5}man')
matchObject = tanxinRegex.search("Bawowowowowoman")
print(matchObject.group())
feitanxinRegex = re.compile(r'Ba(wo){3,5}?man')
matchObject = feitanxinRegex.search("Bawowowoman")
print(matchObject.group())

字符集分类

前面展示了正则表达式中使用了部分字符作为功能性字符对格式进行限定,有些字符是同一个类别比如0-9为数字字符,可以用表示性字符\d来聚合多个数字字符。而\w可以匹配单词字符,\s可以匹配空白字符。

# 字符集匹配
import re
zifuRegex = re.compile(r'\d+\s\w+')
matchAll = zifuRegex.findall("12 mouths, 4 seasons,10 people")
print(matchAll)

search函数是从字符串中第一次找到匹配项并返回,findall函数是在整个字符串中完整查找
除了既定的字符集外我们可以建立自己的字符分类,用[]进行构建,同时在方括号内普通的正则表达式符号不会被解析。

# 字符分类
import re
zifuRegex = re.compile(r'[0-9]+')
matchAll = zifuRegex.findall("12 mouths, 4 seasons,10 people")
print(matchAll)

通配字符是正则表达式中表示字符包容性最强的,它匹配除了换行符之外的所有字符,可以产生新的搭配组合。

  • .*匹配除了换行符外所有字符
    如果想要提取出某个标注后面所有的字符,可以用.*匹配所有字符
import re
tongRegex = re.compile(r'感想:(.*)')
matchAll = tongRegex.search("感想:这篇文章怎么这么长啊!")
print(matchAll.group(1))
  • .*匹配所有字符
    通过传入re.DOTALL作为re.compile()的第二个参数,可以匹配所有字符包含换行符。
# 匹配所有字符
import re
noLineRegex = re.compile(r'.*')
print(noLineRegex.search('I\n love you').group())
lineRegex = re.compile(r'.*',re.DOTALL)
print(lineRegex.search('I\n love you').group())

限定始末位置匹配

当我们想要在字符串中匹配出以某个模式开始或以某个模式结束的内容,可以使用插入符号(^)和美元符号($)来分别限定。

### 始末匹配
import re
# 初匹配python
shiRegex = re.compile(r'(^python).*')
matchObject = shiRegex.search("python hello")
print(matchObject.group())
matchObject = shiRegex.search("hello python")
print(matchObject is None)
# 末匹配python
moRegex = re.compile(r'.*(python$)')
matchObject = moRegex.search("python hello")
print(matchObject is None)
matchObject = moRegex.search("hello python")
print(matchObject.group())

# 初末限定匹配
chumoRegex = re.compile(r'^s(\d+)s$')
matchObject = chumoRegex.search("s12315s")
print(matchObject.group())

不区分大小写字母的匹配

正则表达式是大小写敏感进行匹配的,我们可以设定re.comiler()方法的第二个参数为re.IGNORECASE或re.I来满足我们只关心字母的情况。

# 不区分大小写
import re
ignoreRegex = re.compile(r'^ems(\d+)',re.I)
matchObject = ignoreRegex.search("EMS1234")
print(matchObject.group())

正则表达式中的注释

对复杂的正则表达式进行管理的时候我们可以将原始字符串用三重引号'''创建多行字符串,并向re.compile方法的第二个参数传入re.VERBOSE来忽略正则表达式字符串中的空白符和注释。

# 正则表达式中的注释
import re
phoneRegex = re.compile(r'''(
(\d{3}|\(\d{3}\))?   #area code
(\s|-|\.)?           #separator
(\d{3})              #first 3 digits
(\s|-|\.)            #separator
(\d{4})              #last 4 digits
)''',re.VERBOSE)
matchObject = phoneRegex.search("my phone is 123-456-7890")
print(matchObject.group())

常用操作符
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容