专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
程序猿  ·  41岁DeepMind天才科学家去世:长期受 ... ·  23 小时前  
程序员的那些事  ·  OpenAI ... ·  昨天  
OSC开源社区  ·  2024: 大模型背景下知识图谱的理性回归 ·  3 天前  
OSC开源社区  ·  升级到Svelte ... ·  4 天前  
程序员的那些事  ·  惊!小偷“零元购”后竟向 DeepSeek ... ·  3 天前  
51好读  ›  专栏  ›  SegmentFault思否

写爬虫,怎么可以不会正则呢?

SegmentFault思否  · 公众号  · 程序员  · 2019-08-26 11:55

正文

导读: 正则在各语言中的使用是有差异的,本文以 Python 3 为基础。 本文主要讲述的是正则的语法,对于 re 模块不做过多描述,只会对一些特殊地方做提示。


很多人觉得正则很难,在我看来,这些人一定是没有用心。 其实正则很简单,根据二八原则,我们只需要懂 20% 的内容就可以解决 80% 的问题了。 我曾经有几年几乎每天都跟正则打交道,刚接手项目的时候我对正则也是一无所知,花半小时百度了一下,然后写了几个 demo,就开始正式接手了。 三年多时间,我用到的正则鲜有超出我最初半小时百度到的知识的。


1、正则基础


1.1、基础语法


(1)常用元字符


(2)限定词(又叫量词)


(3)常用反义词


(4)字符族


以上便是正则的基础内容,下面来写两个例子看下:

s = '123abc你好'
re.search('\d+', s).group()
re.search('\w+', s).group()

结果:

123
123abc你好

是不是很简单?

1.2、修饰符


修饰符在各语言中也是有差异的。

Python 中的修饰符:


(1)re.A

修饰符 A 使 \w 只匹配 ASCII 字符, \W 匹配非 ASCII 字符。

s = '123abc你好'
re.search('\w+', s, re.A).group()
re.search('\W+', s, re.A).group()

结果:

123abc
你好

但是描述中还有 \d \D ,数字不都是 ASCII 字符吗? 这是什么意思? 别忘了,还有 全角和半角!

s = '0123456789'     # 全角数字
re.search('\d+', s, re.U).group()

结果:

0123456789

(2)re.M

多行匹配的模式其实也不常用,很少有一行行规整的数据。

s = 'aaa\r\nbbb\r\nccc'

re.findall('^[\s\w]*?$', s)
re.findall('^[\s\w]*?$', s, re.M)

结果:

['aaa\r\nbbb\r\nccc']        # 单行模式
['aaa\r''bbb\r''ccc']    # 多行模式

(3)re.S

这个简单,直接看个例子。

s = 'aaa\r\nbbb\r\nccc'

re.findall('^.*', s)
re.findall('^.*', s, re.S)

结果:

['aaa\r']
['aaa\r\nbbb\r\nccc']

(4)re.X

用法如下:

rc = re.compile(r"""
\d+ # 匹配数字
# 和字母
[a-zA-Z]+
"""
, re.X)
rc.search('123abc').group()

结果:

123abc

注意,用了 X 修饰符后,正则中的所有空格会被忽略,包括正则里面的原本有用的空格。 如果正则中有需要使用空格,只能用 \s 代替。

(5)(?aiLmsux)

修饰符不仅可以代码中指定,也可以在正则中指定。 (?aiLmsux) 表示了以上所有的修饰符,具体用的时候需要哪个就在 ? 后面加上对应的字母,示例如下, (?a) re.A 效果是一样的:

s = '123abc你好'
re.search('(?a)\w+' , s).group()
re.search('\w+', s, re.A).group()

结果是一样的:

123abc
123abc

1.3、贪婪与懒惰


当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。

s = 'aabab'
re.search('a.*b', s).group()    # 这就是贪婪
re.search('a.*?b', s).group()   # 这就是懒惰

结果:

aabab
aab

简单来说:

  • 所谓贪婪,就是尽可能 多 的匹配;
  • 所谓懒惰,就是尽可能 少 的匹配。
  • * + {n,} 这些表达式属于贪婪;
  • *? +? {n,}? 这些表达式就是懒惰(在贪婪的基础上加上 ? )。

2、正则进阶


2.1、捕获分组



注意: 在其他语言或者网上的一些正则工具中,分组命名的语法是 (? exp) (?'name'exp) ,但在 Python 里,这样写会报错: This named group syntax is not supported in this regex dialect。 Python 中正确的写法是: (?P exp)

示例一:

分组可以让我们用一条正则提取出多个信息,例如:

s = '姓名:张三;性别:男;电话:138123456789'
m = re.search('姓名[::](\w+).*?电话[::](\d{11})', s)
if m:
    name = m.group(1)
    phone = m.group(2)
    print(f'name:{name}, phone:{phone}')

结果:

name:张三, phone:13812345678

示例二:

(?P exp) 有时还是会用到的, (?P=name) 则很少情况下会用到。 我想了一个 (?P=name) 的使用示例,给大家看下效果:

s = '''
张三
30
138123456789
'''


pattern = r'.*?)>(.*?)(?P=name)>'
It = re.findall(pattern, s)

结果:

[('name', '张三'), ('age', '30'), ('phone', '138123456789')]

2.2、零宽断言



注意: 正则中常用的前项界定 (?<=exp) 和前项否定界定 (? 在 Python 中可能会报错: look-behind requires fixed-width pattern,原因是 python 中 前项界定的表达式必须是定长的,看如下示例:
(?<=aaa)        # 正确
(?<=aaa|bbb)    # 正确
(?<=aaa|bb)     # 错误
(?<=\d+)        # 错误
(?<=\d{3})      # 正确






请到「今天看啥」查看全文