#内容为视频笔记及个人理解,若有错误还望各位大佬指正
正则表达式
正则表达式可以帮助我们得到网页当中源码的部分信息, 使用正则表达可以锁定到我们想要的位置。
单字符
字符 | 作用 |
---|---|
. | 除换行符以外的所有字符 |
[] | [aoe] [a-z] 匹配集合中任意一个字符, 范围内匹配 |
\d | 数字 [0-9] |
\D | 非数字的所有字符 |
\w | 字母、数字、下划线 |
\W | 除 字母、数字、下划线 以外的所有字符 |
\s | 所有的空白字符, 空格, 制表符, 换页符等等(\n, \t, \f, \r, \v) |
\S | 非空白的所有字符 |
多字符
字符 | 作用 |
---|---|
* | 匹配前一个字符任意多次 |
+ | 匹配前一个字符至少一次或多次 |
? | 匹配前一个字符可有可无 |
{m} | 匹配前一个字符规定次数 |
{m, } | 匹配前一个字符至少次 |
{m, n} | 匹配前一个字符m-n次 |
-
* 示例
.* 匹配前一个字符, 任意多个除换行符以外的字符
-
+ 示例
.+ 匹配前一个字符, 一个或以上的除换行符以外的字符
-
? 示例
1? 匹配前一个字符 1, 1 可有可无, 若有只能有一个
-
{} 示例
1{2} 匹配连续出现两次的前一个字符 1
-
{m,} 示例
1{5,} 匹配前一个字符 1, 至少出现5次或以上
-
{m,n} 示例
1{5,10} 匹配前一个字符 1, 出现次数不得小于5 不得大于10
开头和结尾
字符 | 作用 |
---|---|
^ | 匹配开头的字符 |
$ | 匹配末尾的字符 |
[^1-3] | 匹配不包含 1 或 2 或 3 的字符 |
-
^ 示例
^[0-9] 匹配开头以 0-9之间 的字符串
-
$ 示例
[a-z]$ 匹配结尾以 a-z之间 的字符串
-
[^] 示例
[^0-9] 不匹配 0-9之间 的字符
匹配分组 “|”
字符 | 作用 |
---|---|
| | 匹配 | 两侧的表达式 (或) |
(ab) | 将括号中的字符作为一个分组来匹配 |
\ | 引用分组内的内容 |
?P<name> | 给分组起别名, ?P<>为格式, <>内为名称 |
(?P=name) | 引用别名, (?P= )为格式, =后为名称 |
-
| 示例
^[0-9]?[0-9]$ | ^100$ 匹配 开头为 0-9 的第一位数字(可有可无), 第二位以 0-9 结尾 或 100
-
() 示例
\w{4,20}@(163\|126\|qq)\.com$ 匹配地址为 163 或 126 或 qq
-
\ 示例
<([a-zA-Z]+)><([a-zA-Z0-9]+)>.*</\2></\1> \1 指向 第一个分组([a-zA-Z]+) \2 指向 第二个分组([a-zA-Z0-9]+) 引用符号 并非引用分组的表达式 而是具体的内容是否一致 例: <html><h1>text</h1></html> 上述表达式成立 <htmla><h1>text</h1></html> 上述表达式不成立 <html> 和 <htmla> 都满足分组 ([a-zA-Z]+) 但 分组1 的内容和 引用\1 的内容不一致 判定不等 在 Python 中一个 \ 是转义字符, 所以要使用 \\ 来转义转义字符
-
?P<> 和 (?P=) 示例
<(?P<name1>[a-zA-Z]+)><(?P<name2>[a-zA-Z0-9]+)>.*</(?P=name2)></(?P=name1)> 起别名时在分组的开头写入, 引用时要注意有一对括号 好比在 ?P 中存储值, 在引用时使用 (?P=) 来获取相应的值
注: 在使用 re 模块的 match() 时,如果表达式中含有分组符号 "()" ,返回的对象在调用group()时, 可以传入一个区号来获取指定括号内的内容
import re
# (163|126|sina)的区号为 1 , (com|cn|net)的区号为 2
match_obj = re.match("\w{4,20}@(163|126|sina).(com|cn|net)$", "iterma@sina.cn")
if match_obj:
print(match_obj.group())
print(match_obj.group(1)) # 获取区号 1 的内容
print(match_obj.group(2)) # 获取区号 2 的内容
else:
print("匹配失败")
结果:
iterma@sina.cn
sina
cn
贪婪和非贪婪模式
在大多数语言中默认的情况下都是贪婪模式, 只有极少数的默认情况为非贪婪模式
- 贪婪模式 : 在满足条件的情况下, 尽可能多的取值
- 非贪婪模式 : 在满足条件的情况下, 尽可能少的取值
import re
str_ = "aaa123456"
exp = "aaa\\d+"
result = re.search(exp, str_)
if result:
print(result.group())
else:
print("匹配失败")
结果: aaa123456
\d+ 可以取多个数字的值, 这是贪婪模式的取值模式
import re
str_ = "aaa123456"
exp = "aaa\\d+?"
result = re.search(exp, str_)
if result:
print(result.group())
else:
print("匹配失败")
结果: aaa1
当我们在 \d+ 后面添加一个 ? 时, 取值不再是取最多的值, 而是取最少的值, 因为 + 号的最小范围是 1 所有只显示了一个数字, 当然如果非贪婪模式只是为了取一个值, 那就显的用处并不大了
非贪婪模式, 在我们需要爬取网页的图片时经常被用到
图片文件经常保存在 src 的标签中, 因此我们只需要把表达式定位在 src 内部的链接地址上就可以获取这个图片的地址(不要忘了在前面加上 https: )
import re
str_ = 'src="//pic.qiushibaike.com/system/pictures/12368/123686643/medium/S40V6I0VQFEN8SFV.jpg" alt="糗事#123686643" class="illustration" width="100%" height="auto">'
exp = 'src="(.*?)" '
result = re.search(exp, str_)
if result:
print("https:" + result.group(1))
else:
print("匹配失败")
结果: https://pic.qiushibaike.com/system/pictures/12368/123686643/medium/S40V6I0VQFEN8SFV.jpg
完整的链接地址:
https://pic.qiushibaike.com/system/pictures/12368/123686643/medium/S40V6I0VQFEN8SFV.jpg (在edge上可以不加 https , 但是使用 chrome 无法直接打开, 因此为了保险还是加上了)
src="(.*?)"
我自己刚开始也搞不懂这段表达式, 所以现在趁热打铁, 希望也能帮助到别人
1. src=" 限制了源码中的前5个字符
2. 末尾的 " 号出现的第一次的位置之间才是我们想要的字符串, 但是如果不使用贪婪模式, " 号会直接匹配到最末尾的 " 号
3. (.*?)" 在末尾为 " 号的条件下, 我们取中间最少的值, 即头部的 " 号 和 末尾的第一个 " 号之间的值
re模块
re.match(pattern, string, flag=0)
使用正则表达式从头匹配一个符合规则的字符串, 从起始位置开始匹配, 匹配成功返回一个 对象 , 未匹配成功返回 None
- pattern : 正则表达式
- string : 要匹配的字符串
- flag : 匹配模式
这个方法并不是完全匹配, 当pattern结束时若string还有剩余字符, 仍然视为成功, 想要完全匹配, 可以在表达式末尾加上边界匹配符 “$”
match() 方法一旦匹配成功, 就是一个match object对象, 而match object对象有以下方法:
- group() 返回被 RE 匹配的字符串
- start() 返回匹配开始的位置
- end() 返回匹配结束的位置
- span() 返回一个元组包含匹配 (开始, 结束) 的位置
import re
match_obj = re.match("[iterma]{4,20}@163\.com$", "iterma@163.com")
print(match_obj.group())
print(match_obj.span())
输出结果:
iterma@163.com
(0, 14)
re.search(pattern, string, flag=0)
re.search()函数会在字符串内以查找模式匹配只要找到第一个匹配然后返回, 如果字符串中没有匹配, 返回None
search 和 match 的区别
import re
exp = "<(?P<name1>[a-zA-Z]+)><(?P<name2>[a-zA-Z0-9]+)>text</(?P=name2)></(?P=name1)>"
str_ = "<html><h1>text</h1></html>"
# 第一次测试
match_obj = re.match(exp, str_)
if match_obj:
print(match_obj.group())
print(match_obj.span())
else:
print("匹配失败")
# 第二次测试
match_obj = re.match(exp, "_"+str_)
if match_obj:
print(match_obj.group())
print(match_obj.span())
else:
print("匹配失败")
# 第三次测试
match_obj = re.search(exp, "_"+str_)
if match_obj:
print(match_obj.group())
print(match_obj.span())
else:
print("匹配失败")
结果:
# 第一次结果
<html><h1>text</h1></html>
(0, 26)
# 第二次结果
匹配失败
# 第三次结果
<html><h1>text</h1></html>
(1, 27)
对比 span() 可以看出字符串只要包含表达式 那么search就会返回结果, 而match的表达式必须在字符串的开头
findall(pattern, string, flag=0)
re.findall() 可以查找字符串中所有满足表达式的结果, 并保存在一个列表中返回
import re
exp = "\\d+"
str_ = "查找次数:2099, 阅读次数:2094, 评论次数:1233, 转发次数:500"
match_obj = re.findall(exp, str_)
if match_obj:
print(match_obj)
else:
print("匹配失败")
结果:
['2099', '2094', '1233', '500']
re.findall() 函数返回的是一个列表!!!
sub(pattern, repl, string, count, flag=0)
re.sub() 与字符串中的 replace() 类似, 查找满足表达式的内容, 并替换为新的内容, 返回一个新的字符串
- repl : 要替换的内容
- count : 要替换的次数
import re
exp = "\\d+"
str_ = "查找次数:2099, 阅读次数:2094, 评论次数:1233, 转发次数:500"
match_obj = re.sub(pattern=exp, repl="10000", string=str_, count=3)
if match_obj:
print(match_obj)
else:
print("匹配失败")
结果:
查找次数:10000, 阅读次数:10000, 评论次数:10000, 转发次数:500
re.sub() 函数返回的是一个新的字符串!!!
split(pattern, string, maxsplit, flag=0)
re.split() 与字符串的 split() 类似, 以满足表达式的内容来拆分字符串, 并返回一个新的字符串
- maxsplit : 拆分的最大次数
import re
str_ = "sing:我是一只小青龙,小青龙,我有一个小秘密,小秘密"
exp = ":|,"
match_obj = re.split(exp, str_, maxsplit=3)
if match_obj:
print(match_obj)
else:
print("匹配失败")
结果:
['sing', '我是一只小青龙', '小青龙', '我有一个小秘密,小秘密']
re.split() 返回的是一个列表!!!
#第一次学爬虫, 此文章内容为个人理解, 希望不要误导跟我一样初入爬虫的朋友们
正则表达式 (案例)
要求: 爬取糗事百科的图片
import requests
import re
import os
def get_pic(num_):
for num in range(1, num_+ 1):
url = "https://www.qiushibaike.com/imgrank/page/%d" % num
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0',
}
response_obj = requests.get(url=url, headers=header)
response_txt = response_obj.text
exp = '<img src="(.*?)" alt="(.*?)"' # 表达式获取图片地址和图片的名称
url_list = re.findall(exp, response_txt)
try:
os.mkdir("pic_items")
except:
pass
head = "https:"
for i in url_list:
if ("糗事" in i[1]): # 过滤不相关的图片
new = head + i[0] # 完整的图片地址
pic_url = requests.get(url=new, headers=header)
pic = pic_url.content
with open("pic_items\\%s.jpg" % i[1], "wb") as pc:
pc.write(pic)
def main(num_=1):
get_pic(num_)
if __name__ == "__main__":
main(2)