1.简介
其实re在基本模块里已经介绍过,但是在爬虫中re是非常重要的,所以在这里再进行详细描述。
re在解析html内容时是效率最高的,但是也是最难的,一般来说,都是结合xpath和re使用,这样解析html文件会事半功倍
2.单个字符串的匹配
# -*-coding:utf8 -*- import re #本节匹配单个字符 #1.匹配某个字符串 # text="hello" # text2="ahello" #match只能从开头开始匹配 # ret=re.match('he',text) #调用group方法,就得得到匹配结果。如果没有匹配到,是None的话,调用group()会报错 # print(ret.group()) #2.点,匹配任意的字符 #可以匹配到空格,但是不能匹配到换行符 # text='hello' # text2='\n' # ret=re.match(".",text) # ret=re.match(".",text2) # print(ret.group()) #3.\d 匹配任意的数字(0-9) # text='12' # ret=re.match("\d",text) # print(ret.group()) #4.\D 匹配任意的非数字 # text='+' # ret=re.match("\D",text) # print(ret.group()) #5.\s,匹配空白字符(\n,\t,\r,空格) # text=' asd' # ret=re.match("\s",text) # print(ret.group()) #6.\w,匹配字母,数字及下划线 # text='_' # ret=re.match("\w",text) # print(ret.group()) #7.\W,匹配非字母数字下划线 # text='+()*&^%' # ret=re.match("\W+",text) # print(ret.group()) #8.[]组合的方式,只要满足中括号中的字符,就可以匹配 # text='-0731-888888' #\可以转义有特殊含义的字符 # ret=re.match("[\d\-]+",text) # print(ret.group()) #group()和groups()的区别 ''' groups()以元组返回所有分组匹配的字符。 group(0)代表整个匹配结果,group(1)列出第一个分组匹配部分,group(2)列出第二个分组匹配部分。 如果没有分组,则调用group(),则可以显示匹配内容。没有分组,使用groups会匹配到空 ''' #8.1 中括号的形式代替\d # text='0731-888888' # ret=re.match("[0-9]+",text) # print(ret.group()) #8.2 中括号的形式代替\D # text='0731-888888' # ret=re.match("[^0-9]",text) # print(ret.group()) #8.3 中括号的形式代替\w # text='s0731-888888' # ret=re.match("[a-zA-Z_]",text) # print(ret.group()) #8.4 中括号的形式代替\W # text='PW31-888888' # ret=re.match("[^a-zA-Z_]",text) # print(ret.group())
3.匹配多个字符
# -*-coding:utf8 -*- import re #9. * 可以匹配0或任意多个字符 # text=' 0731' # ret=re.match('\d*',text) # print(ret.group()) #10. + 匹配一个或者多个字符(最少要有一个字符匹配上) # text='abcd' # ret=re.match('\w+',text) # print(ret.group()) #11. ? 匹配一个或者0个字符(要么没有,要么只有一个) # text='+abcd' # ret=re.match('\w?',text) # print(ret.group()) #12. {m},匹配m个字符 # text='abcd' # ret=re.match('\w{2}',text) # print(ret.group()) #13. {m,n},匹配m-n个字符 # text='abcd' # ret=re.match('\w{1,5}',text) # print(ret.group())
4.多个小案例
# -*-coding:utf8 -*- import re #1.验证手机号码:手机号码的规则是以1开头,第二位可以是34587,后面9位随意 # text='18570662061' # ret=re.match("1[34578]\d{9}",text) # print(ret.group()) #2.验证邮箱 # text='hynever123_@qq.com' # ret=re.match('\w+@[a-z0-9]+\.com',text) # print(ret.group()) #3.验证URL:URL的规则是前面是http或者是https或者是ftp然后再加上一个冒号,再加上一个斜杠,再后面 # 就是可以出现任意非空白字符了 # text='http://www.baidu.com/s?wd=python' # ret=re.match('(http|https|ftp)://[^\s]+',text) # print(ret.group()) #4.验证身份证 text='45621524214712387X' ret=re.match('\d{17}[\dxX]',text) print(ret.group())
5.开始、结束、或语法
# -*-coding:utf8 -*- import re #1.^(脱字号)表示匹配以什么开头 # text='hello world' # ret=re.search('^h',text) # print(ret.group()) #放在中括号[]中,表示取反 #2. $ 表示以匹配以什么结尾 # text='xxx@163.comasdasdas' # ret=re.match('\w+@163.com$',text) # print(ret.group()) #3. | 匹配多个字符串或者表达式 # text='httpsdasda' # ret=re.match('(ftp|http|https)',text) # print(ret.group()) #4.贪婪模式与非贪婪模式(默认采用的都是贪婪模式) # text='0123456' # ret=re.match('\d+',text) # print(ret.group()) #想用非贪婪模式,在匹配条件后面加一个问号.这样匹配的就是满足的最小条件 # text='0123456' # ret=re.match('\d+?',text) # print(ret.group()) # text='<h1>标题</h1>' # ret=re.match('<.+>',text) # ret=re.match('<.+?>',text) # print(ret.group()) #案例:匹配0-100之间的数字 #可以出现的:0,1,2,3,4,... #不可以出现的:09,101 (09这种数字写法是不合法的,第一个不能为0) #有三种情况:1,99,100 text=' ret=re.match('[1-9]\d?$|100$',text) print(ret.group())
6.转义字符和原生字符串
# -*-coding:utf8 -*- import re #转义字符和原生字符串 ''' 在正在表达式中,有些字符是有特殊意义的字符。因此如果想要匹配这些字符,那么就必须使用反斜杠进行转义。 比如$代表的是以...结尾,那么就必须使用\$ ''' # text='apple price is $499' # ret=re.search('\$\d+',text) # print(ret.group()) #原生字符串 ''' 在正则表达式中,\是专门用来做转义的。在python中\也是用来做转义的。因此如果想要在普通的字符串中 匹配出\,那么要给出4个\。 ''' # text='\c' #在python语言层面,'\\\\c'经过转义,成了'\\c',然后扔给正则去匹配,又经过转义成了'\c' # ret=re.search('\\\\c',text) # print(ret.group()) #在python语言层面,还有一种转义方式,r'\c',这是python自带的转义,r=raw表示原生的意思 #推荐使用这种写法,以免太过复杂 # text='\c' # ret=re.search(r'\\c',text) # print(ret.group()) # print('\n') # print(r'\n')
7.group分组
# -*-coding:utf8 -*- import re #re模块常用函数 #match:从开始的位置进行匹配。如果开始的位置没有匹配到,那就直接失败了 #search:在字符串中找满足条件的字符。如果找到,就返回。说白了,就是只会找到第一个满足条件的 #分组 ''' 在正则表达式中,可以通过对过滤到的字符串进行分组。分组使用圆括号的方式 ''' text="apple's price is $99,orange's price is $10" ret=re.search('.*(\$\d+).*(\$\d+)',text) #可以打印每个分组里面的内容 # print(ret.group(2)) # print(ret.group(1,2)) #拿到所有的子分组都拿到 print(ret.groups()) #整个正则表达式是一个大的分组,直接用group()或group(0)拿到 # print(ret.group()) # print(ret.group(0))
8.常用函数
# -*-coding:utf8 -*- import re # findall # 找出所有满足条件的,返回的是一个列表 # text="apple's price is $99,orange's price is $10" # ret=re.findall('\$\d+',text) # print(ret) # sub # 用来替换字符串。 # text="apple's price is $99,orange's price is $10" # sub要先传一个匹配条件,再传要修改成的内容,然后传要进行匹配的字符串,再传入count:要修改几个 # count不传个数,则默认全部修改 # ret=re.sub('\$\d+','0',text) # ret=re.sub('\$\d+','0',text,1) # print(ret) # html = """ # <p>服务端研发工程师<br><br>职责:<br><br>负责服务端后台功能的研发,包括Web服务、数据分析统计、运营管理系统。<br><br>要求:<br><br>1. 熟练使用Python及其常用类库<br>2. 熟练使用SQL语言<br>3. 熟悉Linux和MySQL的使用<br>4. 熟悉常用网络协议,譬如HTTP<br><br>加分项:<br><br>1. 热爱技术<br>2. 熟悉Android系统<br>3. 熟悉Java语言</p> # """ # ret = re.sub('<.+?>', '', html) # print(ret) #split #使用正则表达式来分隔字符串。用什么进行分隔,返回的是一个列表。 # text='hello &world ni hao' # ret=re.split(' ',text) # ret=re.split(' |&',text) # ret=re.split('[^a-zA-Z]',text) # print(ret) #compile #对于一些经常要用到的正则表达式,可以使用compile进行编译,后期再使用的时候可以直接拿过来用, #执行效率会更快。而且compile还可以指定flag=re.VERBOSE,在写正则表达式的时候可以做好注释 # text='the number is 20.50' #先写匹配条件。这个正则匹配条件是存放在内存当中,如果是高并发场景,可以用这个 # r=re.compile('\d+\.?\d*') # ret=re.search(r,text) # print(ret.group()) #re.VERBOSE 注释功能,省的每次看正则匹配要花时间,直接把注释给上 # text='the number is 20.50' # r=re.compile(r""" # \d+ #小数点前面的数字 # \.? #小数点本身 # \d* #小数点后面的数字 # """,re.VERBOSE) # ret=re.search(r,text) # print(ret.group())
9.古诗文网爬虫实战
# -*-coding:utf8 -*- import re import requests def parse_page(url): headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36', } response=requests.get(url,headers=headers) text=response.text # . 不能匹配\n 如果想要它匹配\n等字符,要加上flags=re.DOTALL #re.S=re.DOTALL titles=re.findall(r'<div\sclass="cont">.*?<b>(.*?)</b>',text,re.S) dynasties=re.findall(r'<p class="source">.*?<a.*?>(.*?)</a>',text,re.DOTALL) authors=re.findall(r'<p class="source">.*?<a.*?>.*?<a.*?>(.*?)</a>',text,re.DOTALL) content_tags=re.findall(r'<div class="contson".*?>(.*?)</div>',text,re.DOTALL) contents=[] for content in content_tags: x=re.sub(r'<.*?>','',content) contents.append(x.strip()) poems = [] for value in zip(titles,dynasties,authors,contents): title,dynasty,author,content=value poem={ 'title':title, 'dynasty':dynasty, 'author':author, 'content':content } poems.append(poem) print(poems) #zip函数 pass def main(): url='https://www.gushiwen.org/default_1.aspx' parse_page(url) if __name__ == '__main__': main()
10.糗事百科爬虫实战
# -*-coding:utf8 -*- import re import requests headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36', } def parse_page(url): response =requests.get(url,headers=headers) text=response.text authors=re.findall(r'<div class="author clearfix">.*?<h2>(.*?)</h2>',text,re.S) authors=[author.strip() for author in authors] content_tags=re.findall(r'<div class="content">(.*?)</span>',text,re.S) contents=[] for content in content_tags: x=re.sub(r'<.*?>','',content) x=x.replace('\t','') x=x.replace('\n','') contents.append(x) poems=[] for value in zip(authors,contents): author,content=value poem={ 'author':author, 'content':content } poems.append(poem) return poems def main(): urls=['https://www.qiushibaike.com/text/page/{}/'.format(i) for i in range(10)] duanzi=[] for url in urls: poems=parse_page(url) duanzi.extend(poems) return duanzi if __name__ == '__main__': res=main() print(res) print(len(res))