• 更适合爬虫的正则表达式


    1. 认识正则表达式

    1.1 正则表达式的匹配规则

    模式描述
    \w匹配字母、数字、下划线
    \W匹配非字母、数字、下划线
    \s匹配任何空白字符,包括空格、制表符、换页符等等
    \S匹配任何非空白字符
    \d匹配一个数字字符
    \D匹配一个非数字字符
    ^匹配输入字符串的开始位置。
    $匹配输入字符串的结束位置
    .匹配任意字符,除了换行符,当re.DOTALL标记被指定时,可以匹配包括换行符的任意字符
    *匹配前面的子表达式零次或多次
    ?匹配前面的子表达式零次或一次
    +匹配前面的子表达式一次或多次
    a|b匹配a或b
    ()匹配括号内的表达式,也表示一个组
    \对匹配内容的特殊字符做保留 ,即转义匹配

    1.2 贪婪匹配和非贪婪匹配

    贪婪匹配:
    符号:.*
    表示为匹配尽可能符合要求的字符

    非贪婪匹配:
    符号: .*?
    表示为匹配尽可能少的符合要求的字符

    比如:

    import re
    
    content = 'Hello 1234567 World_This is a Regex Demo'
    result = re.match(r'^He.*(\d+).*Demo$', content)
    print(result)
    print(result.group(1))
    
    import re
    
    content = 'Hello 1234567 World_This is a Regex Demo'
    result = re.match(r'^He.*?(\d+).*Demo$', content)
    print(result)
    print(result.group(1))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    输出结果:

    <re.Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
    7
    <re.Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
    1234567
    
    • 1
    • 2
    • 3
    • 4

    输出解释:
           对于第一个输出,我们使用的是.*的方式来正则字符串,对于.*而言,它会尽量匹配更多的符合要求的字符串,而.*后面的是 \d+,也就是至少会留一个数字给到 \d+,于是.*就把前面的123456都匹配掉,只留一个7给到 \d+,当使用group(1)方法时,访问的是(\d+),这个时候输出的就只有 7 了。
           对于第二个输出就不一样,使用.*?的方式来正则字符串,使用非贪婪的方式,.*?只会匹配到符合要求更少的字符串,当.*?匹配到hellow的空格字符时,就停止匹配,因为后面的数字部分,可以留给(\d+)来匹配,于是在使用group(1)方法时,访问(\d+),这个时候输出就会出现1234567。

    总结:
           在匹配的时候,字符串中间使用非贪婪匹配,但是当匹配字符串的结果在字符串末尾时,要用贪婪匹配。

    1.3 修饰符匹配

    在HTML结点中,会存在很多的修饰符,比如换行,这会妨碍数据的获取,所以要对修饰符的存在进行处理。
    这里只介绍两种常见的修饰符处理:

    修饰符描述
    re.I使匹配内容对大小写不敏感
    re.S使匹配内容包括换行符在内的所有字符

    2. 匹配方法

    这里介绍两个常用匹配方式 2.1 re.search()方法,re.findall()方法,不对match方法解释,因为这个方式并不灵活

    示例html代码

    <div id="songs-list">
    	<h2 class="title">经典老歌h2>
    	<p class="introduction">
    		经典老歌列表
    	p>
    	<ul id="list" class="list-group">
    		<li data-view="2">一路上有你li>
    		<li data-view="7">
    			<a href="/2.mp3" singer="任贤齐">沧海一声笑a>
    		li>
    		<li data-view="4" class="active">
    			<a href="/3.mp3" singer="齐秦">往事随风a>
    		li>
    		<li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月a>li>
    		<li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本a>li>
    		<li data-view="5">
    			<a href="/6.mp3" singer="邓丽君">但愿人长久a>
    		li>
    	ul>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.1 re.search()方法

    原理:search方法首先会扫描字符串,搜寻符合匹配规则的字符串,如果没有,则返回None,如果存在,则返回第一个匹配成功的结果的内容

    html = '''

    经典老歌

    经典老歌列表

    '''
    import re # 查询li中标识符为active的元素,获取其a标签里面的singer的值,获取a标签内的文字部分 result = re.search('(.*?)', html, re.S) if result: print(result.group(1), result.group(2)) #输出:齐秦 往事随风 # 查询li中的第一个singer的值,和a标签里面的文字 result = re.search('(.*?)', html, re.S) if result: print(result.group(1), result.group(2)) #输出:任启贤 沧海一声笑 # 查询li中的第一个singer的值,和a标签里面的文字,与上面不同的是,这里缺少了re.S参数,这这里查询的是没有换行符的第一个符合要求的li对象 result = re.search('(.*?)', html) if result: print(result.group(1), result.group(2)) #输出:beyond 光辉岁月
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    2.2 re.findall()方法

    html = '''

    经典老歌

    经典老歌列表

    '''
    import re # 查询元素li中所有的a节点的超链接,歌手和歌名 results = re.findall('(.*?)', html, re.S) print(results) # 返回的是一个列表,列表元素为元组 print(type(results)) for result in results: print(result) # 分别打印列表元组的整体内容 print(result[0], result[1], result[2]) # 打印一个元组里面的所有元素 # 输出: #[('/2.mp3', '任贤齐', '沧海一声笑'), ('/3.mp3', '齐秦', '往事随风'), ('/4.mp3', 'beyond', '光辉岁月'), ('/5.mp3', '陈慧琳', '记事本'), ('/6.mp3', '邓丽君', '但愿人长久')] # #('/2.mp3', '任贤齐', '沧海一声笑') #/2.mp3 任贤齐 沧海一声笑 #('/3.mp3', '齐秦', '往事随风') #/3.mp3 齐秦 往事随风 #('/4.mp3', 'beyond', '光辉岁月') #/4.mp3 beyond 光辉岁月 #('/5.mp3', '陈慧琳', '记事本') #/5.mp3 陈慧琳 记事本 #('/6.mp3', '邓丽君', '但愿人长久') #/6.mp3 邓丽君 但愿人长久 # 查询所有li元素中a结点的歌名 results = re.findall(r'\s*?()?(\w+)()?\s*?
  • ', html, re.S) for result in results: print(result[1]) # 输出: #一路上有你 #沧海一声笑 #往事随风 #光辉岁月 #记事本 #但愿人长久
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    2.3 re.sub()方法

    将获取到的文本内容进行删除部分内容等
    sub()方法里面含三个参数

    1. 匹配规则
    2. 将符合匹配规则的字符替换成的字符串(如果要去掉的话,则可以赋值为空 ‘’)
    3. 匹配的字符串对象
    import re
    
    content = '54aK54yr5oiR54ix5L2g'
    content = re.sub(r'\d+', '', content)
    print(content)	
    # 输出:aKyroiRixLg
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.4 re.compile()方法

    将正则的部分实例化,避免代码重复书写正则表达式。

    import re
    
    content1 = '2019-12-15 12:00'
    content2 = '2019-12-17 12:55'
    content3 = '2019-12-22 13:21'
    pattern = re.compile('\d{2}:\d{2}')
    result1 = re.sub(pattern, '', content1)
    result2 = re.sub(pattern, '', content2)
    result3 = re.sub(pattern, '', content3)
    print(result1, result2, result3)
    
    # 输出:
    # 2019-12-15  2019-12-17  2019-12-22 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    基于element-ui封装下拉表格组件
    计算机网络——数据链路层介质访问控制
    语义分割算法
    有哪些可以免费下载视频剪辑素材的网站?
    flutter空安全问题,平时用到的数据一定要注意
    PolarDB B-tree 并发控制优化
    go语言|二叉树递归遍历,可能有你没见过的花样玩法
    【精通Java】集合类体系之List集合
    那些测试员面试中的“潜规则”,千万不要踩坑
    C++程序设计
  • 原文地址:https://blog.csdn.net/2301_80263861/article/details/139022558