网络爬虫 | 正则表达式

Posted 数据STUDIO

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络爬虫 | 正则表达式相关的知识,希望对你有一定的参考价值。

正则表达式中匹配与查找

正则表达式,简称为regex,是文本模式的描述方法。

>>> import re
>>> pattern = re.compile('\d\d\d-\d\d\d-\d\d\d\d')
>>> phone=pattern.search('Call me at 415-555-1011 tomorrow')
>>> phone.group() #通过group返回匹配的结果
'415-555-1011'

compile()

Python中所有正则表达式的函数都在re模块中,向re.compile()传入一个字符串值,表示正则表达式,它将返回一个regex模式对象。

regex对象search()方法查找传入的字符串,寻找该正则表达式的所有匹配。

如果字符串中没有找到该正则表达式模式,search()方法将返回None。如果找到了该模式,search()方法将返回一个match对

re.compile(pattern[, flags])

pattern : 一个字符串形式的正则表达式

flags 可选,表示匹配模式,比如忽略大小写,多行模式等,具体参数为:

  • re.I忽略大小写
  • re.L表示特殊字符集 \w, \W, \b, \B, \s, \S依赖于当前环境
  • re.M 多行模式
  • re.S 即为 ' . '并且包括换行符在内的任意字符( ' . '不包括换行符)
  • re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S依赖于 Unicode 字符属性数据库
  • re.X 为了增加可读性,忽略空格和 ' # '后面的注释

search()

另一种方法,直接使用re.search()方法,扫描整个字符串并返回第一个成功的匹配。

re.search(pattern, string, flags=0)

pattern: 匹配的正则表达式转换而来的字符串。

string: 要匹配的字符串。

flags: 可选参数,标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。


group()

search对象可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式,它返回被查找字符串中实际匹配的文本。

匹配对象方法 描述
group(num=0) 匹配的整个表达式的字符串,group() 可以一次输入多个组号,可以取得匹配文本的不同部分,在这种情况下它将返回一个包含那些组所对应值的元组。
groups() 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。

(\d\d\d)-(\d\d\d-\d\d\d\d),然后可以使用group()匹配对象方法,从一个分组中获取匹配的文本。第一对括号是第1组。第二对括号是第2组。

>>> pattern = ('(\d\d\d)-(\d\d\d-\d\d\d\d)')
>>> phone=re.search(pattern,'Call me at 415-555-1011 tomorrow')

>>> phone.group(0)#返回所有匹配的文本
'455-555-1011'
>>> phone.group(1)#返回第一个括号内容
'455'
>>> phone.group(2)#返回第二个括号内容
'555-1011'
>>> phone.groups()#返回的是元组
('455''555-1011')

用管道匹配多个分组

字符|称为"管道"。希望匹配许多表达式中的一个时,就可以使用它。例如,正则表达式r'Jim|云朵'将匹配'Jim'或'云朵'。如果都出现在被查找的字符串中,则匹配第一次出现的文本。

>>> pattern = (r'Jim|yunduo')
>>> match=re.search(pattern,'yunduo is the author of DATASTUDIO')
>>> match.group()#第一次出现的匹配文本
'yunduo'

用问号实现可选匹配

不论这段文本在不在,正则表达式都会认为匹配。字符?表明它前面的分组在这个模式中是可选的。

>>> pattern = re.compile(r'(\d\d\d-)?\d\d\d-\d\d\d\d')#包含或者不包含区号
>>> phone = pattern.search('My number is 415-555-1011')#有区号
>>> phone.group()
'415-555-1011'
>>> phone = pattern.search('My number is 555-1011')#有区号
>>> phone.group()
'555-1011'

用星号匹配零次或多次

  • *(称为星号)意味着"匹配零次或多次",即星号之前的分组,可以在文本中出现任意次。它可以完全不存在,或一次又一次地重复。

  • +(加号)则意味着"匹配一次或多次"。星号不要求分组出现在匹配的字符串中,但加号不同,加号前面的分组必须"至少出现一次"。

>>> pattern = (r'DA(TA)*STUDIO')
>>> match=re.search(pattern,'DATATATATASTUDIO')
>>> match.group()
'DATATATATASTUDIO'
>>> match=re.search(pattern,'DASTUDIO')
>>> match.group()
'DASTUDIO'

>>> pattern = (r'DA(TA)+STUDIO'# 至少出现一次
>>> match=re.search(pattern,'DASTUDIO')
>>> match.group()
----------------------------------------------------
AttributeError      Traceback (most recent call last)
<ipython-input-122-4009f4d8d45b> in <module>
      1 pattern = (r'DA(TA)+STUDIO'# 至少出现一次
      2 match=re.search(pattern,'DASTUDIO')
----> 3 match.group()

AttributeError: 'NoneType' object has no attribute 'group'

用花括号匹配特定次数

如果想要一个分组重复特定次数,就在正则表达式中该分组的后面,跟上花括号包围的数字。例如,正则表达式(Ha){3}将匹配字符串'HaHaHa',但不会匹配'HaHa'

可以指定一个范围,即在花括号中写下一个最小值、一个逗号和一个最大值。例如,正则表达式(Ha){3,5}将匹配'HaHaHa'、'HaHaHaHa'和'HaHaHaHaHa'

贪婪与非贪婪

如果需要匹配一段包含各种不同类型数据的字符串,传统方法需要挨个去匹配,而使用.*可以匹配所有字符,是一种万能匹配的方式。

  • 正则表达式默认是贪婪的,尽可能匹配最长的字符串

  • 另一种为非贪婪模式:加问号'?',尽可能匹配最短的字符

>>> haRegex = re.compile(r'(ha){3}')
>>> match = haRegex.search('hahahahha')
>>> match.group()
'hahaha'

>>> haRegex = re.compile(r'(ha){3,5}')
>>> match = haRegex.search('hahahahaha')#贪婪模式,尽可能长的匹配
>>> match.group()
'hahahahaha'

>>> haRegex = re.compile(r'(ha){3,5}?')
>>> match = haRegex.search('hahahahaha')#非贪婪模式,尽可能短的匹配
>>> match.group()
'hahaha'

点'.' 星'*'匹配所有字符

  • . ---- 匹配任意字符,除换行
  • * ---- 匹配零个或者多个表达式
  • .* ---- 匹配任意零个或者多个字符
  • .*? ---- 匹配任意零个或者多个字符,非贪婪模式

\n 换行符是需要用跨行匹配

  • (.,re.DOTALL):匹配任意字符,包括换行
# 点匹配任意一个字符
>>> regex = re.compile(r'<.>')
>>> match = regex.search('STU<D>IO')
>>> match.group()
'<D>'

# 点星匹配任意字符
>>> regex = re.compile(r'<.*>')
>>> match = regex.search('STU<loveyunduo>DIO')
>>> match.group()
'<loveyunduo>'

# 点星问号匹配任意字符, 非贪婪匹配
>>> regex = re.compile(r'<.*?>')
>>> match = regex.search('STU<loveyunduo>lovedata>DIO')
>>> match.group()
'<loveyunduo>'

#跨行匹配
>>> regex = re.compile(r'<.*>',re.DOTALL)
>>> text='''data<stu
...     dio>yunduo'''

>>> match = regex.search(text)
>>> match.group()
'<stu\ndio>'

findall()方法匹配所有内容

在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。

re.findall(pattern, string, flags=0)
pattern.findall(string[, pos[, endpos]])

pattern 匹配模式,由要匹配的正则表达式转换而来

string 待匹配的字符串。

flags 可选参数,标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

pos 可选参数,指定字符串的起始位置,默认为 0。

endpos 可选参数,指定字符串的结束位置,默认为字符串的长度。


# findall() 匹配多个,返回的是列表
regex = re.compile(r'\d+')
regex.findall('one1two2three33four4444')
['1''2''33''4444']

匹配字符串边界

如果字符串在开始处、结尾处,或者字符串的分界符为空格、标点符号以及换行,可以使用\b 匹配字符串边界。

# 需要匹配两侧均为边界
# 左侧为边界,右侧不是边界
>>> regex = re.compile(r'\bdata\b')
>>> match = regex.search('datastudio')
>>> print(match)
None

# 左侧为边界,右侧是空格
>>> regex = re.compile(r'\bdata\b')
>>> match = regex.search('data studio')
>>> print(match)
<re.Match object; span=(04), match='data'>

# 左侧为空格,右侧不是边界
>>> regex = re.compile(r'\bdata\b')
>>> match = regex.search(' datastudio')
>>> print(match)
None

# 左侧是边界,右侧是符号点
>>> regex = re.compile(r'\bdata\b')
>>> match = regex.search('data.studio')
>>> print(match)
<re.Match object; span=(04), match='data'>

匹配所有指定字符开头的字符串

>>> pattern = 'data_\w+'
>>> string = '关注DATA_STUDIO DATA_STUDIO data_studio'
>>> match = re.findall(pattern, string, re.I) # 搜索字符串,不区分大小写
>>> print(match)
['DATA_STUDIO''data_studio']

re模块中的字符处理

re.sub()

re.sub用于替换字符串中的匹配项,即将某个字符串中所有匹配正则表达的部分替换成其他字符串。

re.sub(pattern, repl, string, count=0, flags=0)

pattern : 正则中的模式字符串,由要匹配的正则表达式转换而来。

repl : 替换的字符串,也可为一个函数。

string : 要被查找替换的原始字符串。

count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。

flags : 编译时用的匹配模式,数字形式。可选参数,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。


# 替换字符串
>>> import re
>>> pattern = r'1[3456789]\d{9}'
>>> string = '电话号码为:18188888888 的提取码为:12345678'
>>> match = re.sub(pattern, '1**********', string)
>>> print(match)
'电话号码为:1********** 的提取码为:12345678'

# 删除多余字符串
>>> string = 'a1l8l1 8kh8oo88 8oo8ool8lll8'
>>> pattern = '[a-z]'
>>> match = re.sub(pattern, "", string, flags=re.I) 
# 匹配字符串,将所有字母替换为空,并区分大小写
>>> 
print(match)
181 8888 8888

re.split()

split 方法按照能够匹配的子串将字符串分割后返回列表。

re.split(pattern, string[, maxsplit=0, flags=0])

pattern 正则中的模式字符串,由要匹配的正则表达式转换而来。

string 要被查找替换的原始字符串。

maxsplit 分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。如若需要分割的字符串非常大,并且不希望穷尽分割,可使用此参数。

flags 编译时用的匹配模式,数字形式。可选参数,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。


>>> import re
>>> pattern = r'[?|&]'
>>> url = 'http://httpbin.org/get?name="Jim"&age=18'
>>> match = re.split(pattern, url)
>>> print(result)
['http://httpbin.org/get''name="Jim"''age=18']

附录常见匹配

字符 描述
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\' 匹配 "" 而 "(" 则匹配 "("。
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。
* 匹配前面的子表达式零次或多次。例

获取更多常见匹配字符及描述,可关注公众号并回复" 正则表达式 "获取
-- 数据STUDIO -- 

以上是关于网络爬虫 | 正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

7.网络爬虫—正则表达式详讲

python3网络爬虫学习——正则表达式

网络爬虫 | 正则表达式

网络爬虫findall()正则(.*?)不起作用,无返回

Python 正则表达式 (python网络爬虫)

PYTHON网络爬虫与信息提取[正则表达式的使用](单元七)