前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >写爬虫,怎么可以不会正则呢?

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

作者头像
数据森麟
发布于 2019-09-27 07:16:36
发布于 2019-09-27 07:16:36
40000
代码可运行
举报
文章被收录于专栏:数据森麟数据森麟
运行总次数:0
代码可运行

本文转自公众号『大龄码农的Python之路』

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

1、正则基础

1.1、基础语法

(1)常用元字符

语法

描述

\b

匹配单词的开始或结束

\d

匹配数字

\s

匹配任意不可见字符(空格、换行符、制表符等),等价于[ \f\n\r\t\v]。

\w

匹配任意 Unicode 字符集,包括字母、数字、下划线、汉字等

.

匹配除换行符(\n)以外的任意字符

^ 或 \A

匹配字符串或行的起始位置

$ 或 \Z

匹配字符串或行的结束位置

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

语法

描述

*

重复零次或更多次

+

重复一次或更多次

?

重复零次或一次

{n}

重复 n 次

{n,}

重复 n 次或更多次

{n,m}

重复 n 到 m 次

(3)常用反义词

语法

描述

\B

匹配非单词的开始或结束

\D

匹配非数字

\S

匹配任意可见字符, [^ \f\n\r\t\v]

\W

匹配任意非 Unicode 字符集

[^abc]

除 a、b、c 以外的任意字符

(4)字符族

语法

描述

[abc]

a、b 或 c

[^abc]

除 a、b、c 以外的任意字符

[a-zA-Z]

a 到 z 或 A 到 Z

[a-d[m-p]]

a 到 d 或 m 到 p,即 [a-dm-p](并集)

[a-z&&[def]]

d、e 或 f(交集)

[a-z&&[^bc]]

a 到 z,除了 b 和 c:[ad-z](减去)

[a-z&&[^m-p]]

a 到 z,减去 m 到 p:[a-lq-z](减去)

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
s = '123abc你好'
re.search('\d+', s).group()
re.search('\w+', s).group()

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
123
123abc你好

是不是很简单?

1.2、修饰符

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

Python 中的修饰符:

修饰符

描述

re.A

匹配 ASCII字符类,影响 \w, \W, \b, \B, \d, \D

re.I

忽略大小写

re.L

做本地化识别匹配(这个极少极少使用)

re.M

多行匹配,影响 ^ 和 $

re.S

使 . 匹配包括换行符(\n)在内的所有字符

re.U

匹配 Unicode 字符集。与 re.A 相对,这是默认设置

re.X

忽略空格和 # 后面的注释以获得看起来更易懂的正则。

(1)re.A

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
s = '123abc你好'
re.search('\w+', s, re.A).group()
re.search('\W+', s, re.A).group()

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
123abc
你好

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
s = '0123456789'    # 全角数字
re.search('\d+', s, re.U).group()

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
0123456789

(2)re.M 多行匹配的模式其实也不常用,很少有一行行规整的数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
s = 'aaa\r\nbbb\r\nccc'

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

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
['aaa\r\nbbb\r\nccc']        # 单行模式
['aaa\r', 'bbb\r', 'ccc']    # 多行模式

(3)re.S 这个简单,直接看个例子。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
s = 'aaa\r\nbbb\r\nccc'

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

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
['aaa\r']
['aaa\r\nbbb\r\nccc']

(4)re.X 用法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
rc = re.compile(r"""
\d+ # 匹配数字
# 和字母
[a-zA-Z]+
""", re.X)
rc.search('123abc').group()

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
123abc

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
s = '123abc你好'
re.search('(?a)\w+', s).group()
re.search('\w+', s, re.A).group()

结果是一样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
123abc
123abc

1.3、贪婪与懒惰

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
s = 'aabab'
re.search('a.*b', s).group()    # 这就是贪婪
re.search('a.*?b', s).group()   # 这就是懒惰

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
aabab
aab

简单来说:

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

2、正则进阶

2.1、捕获分组

语法

描述

(exp)

匹配exp,并捕获文本到自动命名的组里

(?Pexp)

匹配exp,并捕获文本到名称为 name 的组里

(?:exp)

匹配exp,不捕获匹配的文本,也不给此分组分配组号

(?P=name)

匹配之前由名为 name 的组匹配的文本

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

示例一:

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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}')

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
name:张三, phone:13812345678

示例二:

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
s = '''
<name>张三</name>
<age>30</age>
<phone>138123456789</phone>
'''

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

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[('name', '张三'), ('age', '30'), ('phone', '138123456789')]

2.2、零宽断言

语法

描述

(?=exp)

匹配exp前面的位置

(?<=exp)

匹配exp后面的位置

(?!exp)

匹配后面跟的不是exp的位置

(?<!exp)

匹配前面不是exp的位置

注意:正则中常用的前项界定 (?<=exp) 和前项否定界定 (?<!exp) 在 Python 中可能会报错:look-behind requires fixed-width pattern,原因是 python 中 前项界定的表达式必须是定长的,看如下示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
(?<=aaa)        # 正确
(?<=aaa|bbb)    # 正确
(?<=aaa|bb)     # 错误
(?<=\d+)        # 错误
(?<=\d{3})      # 正确

2.3、条件匹配

这大概是最复杂的正则表达式了。语法如下:

语法

描述

(?(id/name)yes|no)

如果指定分组存在,则匹配 yes 模式,否则匹配 no 模式

此语法极少用到,印象中只用过一次。

以下示例的要求是:如果以 _ 开头,则以字母结尾,否则以数字结尾。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
s1 = '_abcd'
s2 = 'abc1'

pattern = '(_)?[a-zA-Z]+(?(1)[a-zA-Z]|\d)'

re.search(pattern, s1).group()
re.search(pattern, s2).group()

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
_abcd
abc1

2.4、findall

Python 中的 re.findall 是个比较特别的方法(之所以说它特别,是跟我常用的 C# 做比较,在没看注释之前我想当然的掉坑里去了)。我们看这个方法的官方注释:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Return a list of all non-overlapping matches in the string.

If one or more capturing groups are present in the pattern, return 
a list of groups; this will be a list of tuples if the pattern 
has more than one group.

Empty matches are included in the result.

简单来说,就是

  • 如果没有分组,则返回整条正则匹配结果的列表;
  • 如果有 1 个分组,则返回分组匹配到的结果的列表;
  • 如果有多个分组,则返回分组匹配到的结果的元组的列表。

看下面的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
s = 'aaa123bbb456ccc'

re.findall('[a-z]+\d+', s)          # 不包含分组
re.findall('[a-z]+(\d+)', s)        # 包含一个分组
re.findall('([a-z]+(\d+))', s)      # 包含多个分组
re.findall('(?:[a-z]+(\d+))', s)    # ?: 不捕获分组匹配结果

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
['aaa123', 'bbb456']
['123', '456']
[('aaa123', '123'), ('bbb456', '456')]
['123', '456']

零宽断言中讲到 Python 中前项界定必须是定长的,这很不方便,但是配合 findall 有分组时只取分组结果的特性,就可以模拟出非定长前项界定的效果了。

结语

其实正则就像是一个数学公式,会背公式不一定会做题。但其实这公式一点也不难,至少比学校里学的数学简单多了,多练习几次也就会了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据森麟 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
3.7v锂电池升压电路_锂电池升压5v电路图
三节3.7V的锂电池串联,11.1V和最大12.6V锂电池充电电路的解决方案。在应用中,一般使用低压5V,如USB口直接输入的给三串锂电池充电,还有是15V或者18V,20V输入降压给锂电池充电的两种情况。
全栈程序员站长
2022/11/10
1.8K0
3.7v锂电池升压电路_锂电池升压5v电路图
低压差降压稳压电路设计
TPS73xx系列(TPS7301,25,30,33,48,50等)是一款低压差串联型降压稳压芯片,可以提供500mA的稳压电流,在输出100mA电流的情况下,输入输出电压压差可以最多不超过35mV,这可以大大提高稳压电源的效率,或者提供大的电源稳压范围。
全栈程序员站长
2022/08/31
5560
低压差降压稳压电路设计
0.9uA 低功耗低压差稳压器
FM6215 系列采用 CMOS 工艺制造的高精度、低功耗低压差稳压器。该系列具有极低的静态电流, 输出电压 3.3v的产品静态功耗仅为 0.9uA(TYP),最大输出电流可达到 300mA。 产品采用 SOT23-5 封装,因此,该系列适用于需要高密度安装的应用场合,例如便携式移动设备。
芯动大师
2024/01/08
1870
0.9uA 低功耗低压差稳压器
硬件知识之(低压差线性稳压器)LDO的选择
低压差线性稳压器是新一代的集成电路稳压器,它与三端稳压器最大的不同点在于,低压差线性稳压器(ldo)是一个自耗很低的微型片上系统(soc)。它可用于电流主通道控制,芯片上集成了具有极低线上导通电阻的mosfet,肖特基二极管、取样电阻和分压电阻等硬件电路,并具有过流保护、过温保护、精密基准源、差分放大器、延迟器等功能。pg是新一代ldo,具各输出状态自检、延迟安全供电功能,也可称之为power good,即“电源好或电源稳定”。低压差线性稳压器通常具有极低的自有噪声和较高的电源抑制比。
杨源鑫
2020/07/28
2.5K0
硬件知识之(低压差线性稳压器)LDO的选择
LDO产品的基础知识解析
压降电压VDO,是指为实现正常稳压,输入电压VIN必须高出所需输出电压VOUT(nom) 的最小压差。
芯动大师
2024/07/01
1640
LDO产品的基础知识解析
RK3588 EVB开发板原理图讲解【三】
3.通信协议支持:支持 I2C 和 SPI 双通信协议,可根据不同应用场景灵活选择通信方式,满足多样化的系统通信需求。
用户11537198
2025/03/01
2600
哟哟哟,共模半导体来了!(以经典的GM1200为例)
看官们看了我写这么多的LT3042的芯片,肯定会问,这么好的东西在哪里买啊?(好的,已经有朋友知道我要搞小广告了)
云深无际
2025/05/27
1320
哟哟哟,共模半导体来了!(以经典的GM1200为例)
【懒人必备】智能窗帘机器人,告别手动拉窗帘——硬件设计篇
随着智能家居的深入拓展,智能窗帘凭借成熟的技术和产品,逐步成为了智能家居家庭中的标配,在家装智能化市场有很高的应用价值。目前智能窗帘的产品主要以电动窗帘产品为主,该产品主要通过电机驱动实现对窗帘的操控,从安装上讲,该产品更适合前装市场,因为需要结合用户户型、门窗大小预留安装空间和电源接口。对于后装市场,往往需要专业人员上门丈量确认是否符合改装要求,大大增加了安装成本,因此目前市场上,出现的小型的窗帘机器人,完美解决的后装市场的这个痛点,使得普通窗帘秒变智能窗帘。
全栈程序员站长
2022/09/09
1.1K0
【懒人必备】智能窗帘机器人,告别手动拉窗帘——硬件设计篇
Qualcomm《Pseudo-Capless LDO设计指南》解读
昨天在看 Z-Lab 的时候发现了一个来自 13 年前应用指南,高通的渠道可是把握的很严格,平时都看不到什么资料:
云深无际
2025/04/28
1920
Qualcomm《Pseudo-Capless LDO设计指南》解读
手机基带电路设计20问(1)
这个问题本身其实并不准确,首先,电压和电流是由负载的需求来决定的,比如负载需要的是1.2V的电压,前端电源就不能给他提供3.3V的电;负载需要500mA的电流,前端电源的输出电牛就不能低于500mA,要知道负载是先决条件,电源要根据负载来选择。再回到问题本身,从负载的角度而言,低电压、电流的负载功耗当然也低;从电源的角度而言,以LDO电源为例,某负载的需求是1V@300mA,那么LDO的输入、输出电流也是300mA,假如LDO输入是3V,则LDO本身的功耗就是(3-1)*0.3=0.6W,如果降低LDO的输入为1.2V,则LDO本身的功耗就是(1.2-1)*0.3=0.06W,功耗降低为前者的10%,如果负载是屏幕这种常开的类型,0.6W的功耗就会严重减低手机待机时间,此时需要优化电源,降低LDO上的损耗。
工程师看海
2022/06/23
6470
手机基带电路设计20问(1)
锂电池升压IC_锂电池充电升压芯片
锂电池常规的供电电压范围是3V-4.2V之间,标称电压是3.7V。锂电池具有宽供电电压范围,需要进行降压或者升压到固定电压值,进行恒压输出,同时根据输出功率的不同,(输出功率=输出电压乘以输出电流)。不同的输出电流大小,合适很佳的芯片电路也是不同。
全栈程序员站长
2022/11/10
1.8K0
锂电池升压IC_锂电池充电升压芯片
你会选择LDO dropout voltage吗?
LDO是我们常用的电源解决方案,dropout voltage(压差)是LDO最常见的参数之一,但是并不是所有的工程师都能够正确的设计LDO dropout voltage,导致产品可靠性具有隐患,降低平均无故障时间。
工程师看海
2022/06/23
3920
你会选择LDO dropout voltage吗?
3.7v锂电池升压到5v_锂电池升压5伏电路图
PW5410A 是一颗低噪声,恒频 1.2MHZ 的开关电容电压倍增器。 PW5410A 的输入电压范围2.7V-5V,输出电压 5V 固定电压,输出电流高达 250MA。外围元件仅需要三个贴片电容即可组成一个升压电路系统.
全栈程序员站长
2022/11/08
1.3K0
3.7v锂电池升压到5v_锂电池升压5伏电路图
IOT超低功耗设计应用笔记
IOT低功耗设备设计大致为3个方面的设计:器件选型、电路设计、软件设计、续航寿命估算——
硬件大熊
2022/06/23
5680
基于CW32F030单片机的便携式多功能测试笔
在日常的硬件调试工作中,我们最常使用的仪器仪表可能就是万用表了,虽然万用表号称“万用”,但大部分时候,我们需要使用到的功能无非是电压测量和通断测量。
芯动大师
2023/11/28
4310
基于CW32F030单片机的便携式多功能测试笔
智能门锁:电源管理概述1
作为一款高频度使用的低功耗产品,智能门锁的电源管理是低功耗设计极其关键的一部分。高效、合理的电源管理设计能让门锁快速响应动态与静态的动作切换,同时保持更低程度的功率损耗。
硬件大熊
2022/06/23
4610
智能门锁:电源管理概述1
19种电压转换的电路设计方式
大家好,又见面了,我是你们的朋友全栈君。 博主福利:100G+电子设计学习资源包! http://mp.weixin.qq.com/mp/homepage?__biz=MzU3OTczMzk5Mg==
全栈程序员站长
2022/09/05
8170
19种电压转换的电路设计方式
电源部分硬件原理图分享
LM1117是LDO型变换器件(低压差线性稳压器),LM1117可以将5V转3.3V,5V转2.5V,5V转1.8V等,并且转换电压稳定。
Jasonangel
2021/05/28
1.2K0
ESP32-C3设计汇总
首先是ESP32-C3只支持BLE,也就是数据是以packet出现的。不支持普通的蓝牙,也就是数据的处理的时候是可以字符串去操作的。
云深无际
2024/08/20
2990
ESP32-C3设计汇总
单火线设计系列文章5:技术难点 - 闭态”鬼火”问题
单火智能开关解决了智能家居“免布线”“无零线”安装智能开关的问题,但单火取电技术在应用中仍有一些技术问题或瓶颈,下面列举单火取电技术常遇到的技术难点和经验对策。
硬件大熊
2022/06/23
7960
单火线设计系列文章5:技术难点 - 闭态”鬼火”问题
推荐阅读
相关推荐
3.7v锂电池升压电路_锂电池升压5v电路图
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验