正则是什么
正则是一门小型的编程语言,在python中正则被封装成re模块。自己对正则的理解就是用来匹配字符串中一些字符,正则使得匹配字符串的匹配更加多样。
三种匹配方法
re模块中匹配了三种方法(findall,match,search)
s = "/home/kioskday25/PycharmProjects/python_stack/day25/正则表达式的正常使用符那方法.py"
pattern = r'day'
pattern1 = r'/home'
print(re.findall(pattern,s))
#注意:findall会匹配子符串中所有的字符,并以列表的形式把他们列出来
print(re.match(pattern1,s).group())
#注意:match方法只匹配字符串的开头:
#当在开头匹配到字符串时,返回一个对象,如果要查看匹配的结果,需要借用group方法,gruop方法返回的是一个字符串
#当没有匹配到结果时返回一个空
print(re.search(pattern,s).group())
#注意:search方法匹配的是整个字符串,当字符串中有多个满足匹配结果时,只匹配第一个,并通过gruop方法获取到匹配的结果。
结果如下:
正则特殊字符类
#.:匹配除\n之外的任意字符,并将每个匹配到的字符通过列表存储
print(re.findall(r'.','westos\n'))
#\d:匹配一个数字,等加于[0-9]
#\D:匹配除数字外的任意一个字符
print(re.findall('\d','python12345linux'))
print(re.findall('\D','python12345linux'))
#\s:匹配单个任何空白的字符
#\S:匹配除单个任何空白字符的任何字符
print(re.findall('\s',"python\r123\tlinux"))
print(re.findall('\S',"python\r123\tlinux"))
#\w:匹配字母数字或下划线
#\W:匹配除字母数字下划线的字符
print(re.findall('\w',"python_123_linxu%%%%@@@"))
print(re.findall('\W',"python_123_linxu%%%%@@@"))
print(re.findall('[1-5][0-9]','123456'))
pattern = r'[ABC][]'
注意:在findall方法中,匹配返回的结果是一个列表。
结果:
在上面的匹配中,如果我们要匹配一个数字,那我们就必须写多次\d,显得非常麻烦。在正则中,我们可以利用一个语法实现实现\d多次,免去多次书写的麻烦。
#*代表前面的字符出现0次或者无限多次
print(re.findall("\d*",'132233'))
#+代表前面的字符出现一次或者无限次
print(re.findall("\d+",'122245'))
#?代表一个字符出现1次或者0次
print(re.findall("\d?","123455"))
#{m}:前一个字符出现m次
#{m,}:前一个字符至少出现m次
#{m,n}:前一个字符出现m次到n次
pattern = r"[a-zA-z][\w]{5,11}@qq\.com"
print(re.findall(pattern,'a1905050@qq.com'))
结果:
转义字符
|:匹配左右任意一个表达式即可
print(re.findall(r"(westos|hello)\d+",'westoshello446'))
(mn):将括号里面的内容作为一个分组
s = '<span class="red">31</span>'
print(re.findall(pattern,s))
pattern1 = r'(<span class="red">(\d+)</span>)'
s = '<span class="red">31</span>'
print(re.findall(pattern1,s))
print(re.findall(r'((westos|hello)\d+)',"westos1hello2"))
#把匹配到的内容分成组,每组以元组的形式存储
\num:引用分组第num个字符串
(?P):分组起别名
爬去贴吧中的qq邮箱
分析:实现这个效果要利用正则表达式,对得到的网页源码进行匹配操作。
代码可分为以下几个模块:
- 获取源码:对网页的内容进行爬取,并进行解码
- 获取网页的页数:写一个正则表达式,从获取到的源码中获取到贴吧的总页数。
- 获取邮箱:写一个邮箱的正则表达式,从获取到的的源码中获取到邮箱,具体为先获取到页数,然后利用循环对每一页的源码进行获取,然后对源码进行一个邮箱的提取操作,。
- 主函数:调用上面几个函数,然后把函数返回的邮箱地址,写入文件中。
from itertools import chain
from urllib.request import urlopen
def getPageHtml(url):
#获取网页的源码文件
obj = urlopen(url)
return obj.read().decode('utf-8')
#print(getPageHtml("http://tieba.baidu.com/p/3600458679"))
'''<li class="l_reply_num" style="margin-left:8px" ><span class="red" style="margin-right:3px">1193</span>回复贴,共<span class="red">26</span>页</li>'''
def getPagenum(text):
#从源码文件中获取,总页数
pattern = r'<span class="red">(\d{0,3})</span>'
return re.findall(pattern,text)[0]
#text = getPageHtml("http://tieba.baidu.com/p/3600458679")
#print(getPagenum(text))
'''http://tieba.baidu.com/p/3600458679?pn=2'''
def getPageEMail(count):
#对所有页数的文件挨个进行爬取,并利用正则表达式从源码中,匹配到信息
mails = []
for i in range(int(count)):
url = "http://tieba.baidu.com/p/2314539885?pn=%d" %(i+1)
text = getPageHtml(url)
#<li class="d_name" data-field='{"user_id":1159023837}'>
#811393332@qq.com
pattern1 = r'\d{5,12}@qq\.com'
print("正在爬取http://tieba.baidu.com/p/3600458679?pn=%d的内容" %(i+1))
print(re.findall(pattern1,text))
mails.append(re.findall(pattern1,text))
return mails
def main():
text = getPageHtml("http://tieba.baidu.com/p/2314539885")
count = getPagenum(text)
email = getPageEMail(count)
#chain 方法是对不同集合中的元素进行操作时,将不同的列表连接起来
with open("mails.txt",'w') as f:
for i in chain(*email):
f.write(i+"\n")
main()
结果:
爬取图片
from urllib.request import urlopen
def getPageHtml(url): #获取网页的源码
obj = urlopen(url)
return obj.read()
def getPagepic(text):
#从网页的源码中获取到图片的网址,返回的是一个列表,列表里面存储的是当前网页的所有图片的地址
pattern ='<img class="BDE_Image" .*?src="(http://.*?\.jpg)".*?>'
return re.findall(pattern,text.decode('utf-8'))
url = "http://tieba.baidu.com/p/5904388543"
text = getPageHtml(url)
picurl = getPagepic(text)
#对存有图片地址的列表进行一个遍历,依次对图片的网址打开进行一个获取图片。
for i,v in enumerate(picurl):
with open("img/img%d.jpg" %(i+1),"wb") as f:
content = getPageHtml(v)
print("正在爬取第%d张图片" %(i+1))
f.write(content)
结果: