什么是正则表达式? 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑
所以正则表达式并非某一编程语言所特有,它在不同的环境下有不同的用法。比如说shell脚本的awk命令或者是#、%分割字符等等,但是不管哪种方法,正则表达式都为我们解决文本匹配问题提供了巨大的帮助。
而应用到爬虫里,当我们获取了目标网页的html之后,往往需要批量地匹配目标字符串,这些字符串或许是网址,或许是网页的关键信息,但是指望人工手动地去复制粘帖是不现实的,因此就需要正则表达式去匹配和输出。
\w | 匹配字母数字及下划线 |
---|---|
\W | 匹配非字母数字及下划线 |
\s | 匹配任意空白字符,等价于[\t\n\r\f] |
\S | 匹配任意非空字符 |
\d | 匹配任意数字,等价于[0-9] |
\D | 匹配任意非数字 |
\A | 匹配字符串开始 |
\Z | 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串 |
\z | 匹配字符串结束 |
\G | 匹配最后匹配完成的位置 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
^ | 匹配字符串的开头 |
$ | 匹配字符串的末尾 |
. | 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符 |
[…] | 用来表示一组字符,单独列出:[amk]匹配"a","m"或"k" |
[^…] | 不在[]中的字符:[^abc]匹配除了a,b,c之外的字符 |
* | 匹配0个或多个的表达式 |
+ | 匹配1个或多个的表达式 |
? | 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式 |
{n} | 精确匹配n个前面表达式 |
{n,m} | 匹配n到m次由前面的正则表达式定义的片段,贪婪方式 |
a|b | 匹配a或b |
() | 匹配括号内的表达式,也表示一个组 |
总是会从第一个字符开始匹配
import re
content = "Hello 123 4567 World_This is a Regex Demo"
result = re.match("^Hello\s\d{3}\s\d{4}\s\w{10}.*Demo$",content)
#参照上表,'^'匹配开头,'$'匹配结尾
#接受三个参数,正则表达式、目标字符串、匹配模式(可选)
print(len(content))
print(result) ##打印结果,为对象
print(result.group()) #返回匹配结果,若有多个结果,使用索引获取
print(result.span()) #返回匹配长度
输出
41
Hello 123 4567 World_This is a Regex Demo
(0, 41)
泛匹配
result = re.match("^Hello.*Demo$",content)
#与上例匹配结果相同
#这里使用'.*'来匹配'Hello'和'Demo'中的所有内容
目标匹配
import re
content = "Hello 1234567 World_This is a Regex Demo"
result = re.match("^Hello\s(\d+)\s\w.*Demo$",content)
#指定端点,左边\s,右边\s
print(result.group())
print(result.group(1))
#若表达式里存在括号,则可以给group传递参数来提取第n个括号内的内容,
#括号也可以指定为一个整体进行操作
输出
Hello 1234567 World_This is a Regex Demo
1234567
贪婪匹配与非贪婪匹配
import re
content = "Hello 1234567 World_This is a Regex Demo"
result = re.match("^He.*(\d+).*Demo$",content)
#贪婪匹配,'.*'匹配尽可能多的字符,因此(\d+)实际上只匹配到了1个数字
print(result.group())
print(result.group(1))
result1 = re.match("^He.*?(\d+).*Demo$",content)
#'?'指定非贪婪匹配,匹配尽可能少的字符,指有或者没有
print(result1.group())
print(result1.group(1))
输出
Hello 1234567 World_This is a Regex Demo
7
Hello 1234567 World_This is a Regex Demo
1234567
匹配模式
import re
content = '''Hello 1234567 World_This \n is a Regex Demo'''
result = re.match("^He.*?(\d+).*?Demo$",content)
result1 = re.match("^He.*?(\d+).*?Demo$",content,re.S)
#由于content中存在换行符,'\'正常情况下无法匹配,返回None
#指定匹配模式(re.S)后,'\'就可以匹配任意包括换行符的字符
print(result)
print(result1)
输出
None
<_sre.SRE_Match object; span=(0, 41), match='Hello 1234567 World_This \nis a Regex Demo'>
转义
import re
content = "price is $5.00"
#如果目标字符串中存在上表中的特殊字符串,则需要转义符'\'来进行特殊处理
result = re.match("price is \$5\.00", content)
#特殊字符采用换行符'\'
print(result.group())
输出
price is $5.00
re.match方法有一个缺陷,就是它只能够从第一个字符开始匹配,如果我们所提供的正则表达式和目标字符串的第一个字符不同,则匹配结果会返回None
比如
import re
content = "price is $5.00"
result = re.match("rice is \$5\.00", content)
#此时第一个字符不同
print(result)
输出
None
而re.search就是解决这个问题的方法
它会扫描整个字符串,返回第一个成功匹配的字符串
import re
content = "Extra stings Hello 1234567 World_This is a Regex Demo Extra stings"
result = re.match("Hello.*?(\d+).*?Demo",content) #第一个字符不匹配,返回None
result1 = re.search("Hello.*?(\d+).*?Demo",content)
print(result)
print(result1)
输出
None
<_sre.SRE_Match object; span=(13, 53), match='Hello 1234567 World_This is a Regex Demo'>
原理类似,扫描整个字符串,返回符合条件的所有结果,返回结果可以用索引访问
替换字符串中每一个匹配的子串后返回替换后的字符串
import re
content = "Extra stings Hello 1234567 World_This is a Regex Demo Extra stings"
result = re.sub("\d+","Re",content) #替换数字为'Re'
print(result)
输出
Extra stings Hello Re World_This is a Regex Demo Extra stings
或者想要在指定字符串后添加新的元素
import re
content = "Extra stings Hello 1234567 World_This is a Regex Demo Extra stings"
result = re.sub("(\d+)", r"\1 8910",content)
#‘\1’ 代表取所要替换的字符的第一个组合,r代表使其成为原生字符,
#r"\1 8910"替换为"\\1 8910"效果相同
print(result)
输出
Extra stings Hello 1234567 8910 World_This is a Regex Demo Extra stings
将正则字符串编译成正则表达式对象,以便复用
import re
content = '''Hello 1234567 World_This
is a Regex Demo'''
pattern = re.compile("Hello.*Demo",re.S)
result = re.match(pattern,content)
print(result.group())
输出
Hello 1234567 World_This
is a Regex Demo
import re
import requests
content = requests.get("https://book.douban.com/").text
pattern = re.compile('.*?href="(.*?)".*?title="(.*?)"',re.S)
results = re.findall(pattern,content)
for result in results:
url,name = result
print(url,name) #运行时间较长
https://book.douban.com/subject/34778578/?icn=index-latestbook-subject 恐妻家
https://book.douban.com/subject/30432492/?icn=index-latestbook-subject 旅行
https://book.douban.com/subject/34454619/?icn=index-latestbook-subject 遗忘,刑警
https://book.douban.com/subject/30264052/?icn=index-latestbook-subject 西方通史:从古代源头到20世纪
https://book.douban.com/subject/33420970/?icn=index-latestbook-subject 绕日飞行
https://book.douban.com/subject/30420913/?icn=index-latestbook-subject 美国官僚体制
https://book.douban.com/subject/30435811/?icn=index-latestbook-subject 没有男人的公寓
https://book.douban.com/subject/34442426/?icn=index-latestbook-subject 纳粹猎人
输出影评网址和电影名
最近学校的事情有点多、、更新应该不会很频繁,另外,ig、fpx冲冲冲!