了解正则表达式

一 正则表达式

  1. 常见的通配符
    . 当前目录
    … 当前目录的上一级目录
    [0-9]表示数字0-9
    [a-z]表示字母a-z
    [A-Z]表示大写字母A-Z
  2. 通配符的使用
    找出 /etc 下以 “.conf"结尾的文件
    以前可能会用到 endswith(”.conf")这样来进行查找
    现在,也可用 glob模块来查找
import  glob
print(glob.glob('/etc/*.conf'))

但是,如果再加上含有大写字母的,以 ".conf"结尾的文件呢?
这样endswith就解决不了了

print(glob.glob('/etc/*[A-Z]*.conf'))

当然这只是简单的查找,一旦问题复杂话,glob就不好处理了

  1. 正则表达式的引出
    这里就需要导入 re 模块
import   re

假如,一个简单的字符串如下:

s='home/kiosk/anaconda2/envs/mysql/bin/python/home/kiosk/PycharmProjects/py'

那么,正则表达式是如何来书写的呢?
一个完整的正则表达式包括(第一个参数是你正则的规则, 第二个参数是检测的字符串)
re 模块里面有三种的查找方式(findall、match、search)
1). findall

print(re.findall(r'kiosk',s))

在这里插入图片描述
findall 会将查找到符合要求,放到一个列表里面
2). match

print(re.match(r'home',s))

match有点不一样,如果找到匹配, 则返回一个对象
在这里插入图片描述
这里就需要用到 group 方法了

print(re.match(r'home',s).group())

在这里插入图片描述
还有一点需要注意的是: match 方法是从左往右依次匹配的

a = re.match(r"westos", "hellowestoshello")
print(a)

如果没有找到匹配,则返回None
3). search

print(re.search(r'kiosk',s))

在这里插入图片描述
可以看到 search 返回的也是一个对象

obj = re.search(r'kiosk', s)
print(obj.group())

同样要用到 group方法
在这里插入图片描述
要注意的一点时,只会返回第一个值

  1. 正则表达式中的特殊字符类
    .: 匹配除了\n之外的任意字符; [.\n]
    \d: digit–(数字), 匹配一个数字字符, 等价于[0-9]
    \D: 匹配一个非数字字符, 等价于[^0-9]
    \s: space(广义的空格: 空格, \t, \n, \r), 匹配单个任何的空白字符;
    \S: 匹配除了单个任何的空白字符;
    \w: 字母数字或者下划线, [a-zA-Z0-9_]
    \W: 除了字母数字或者下划线, [^a-zA-Z0-9_]
    1). .
print(re.findall(r'.','正义永远\n不会迟到800s'))

在这里插入图片描述
2). d、D

print(re.findall(r'\d','正义永远不会迟到800s'))
print(re.findall(r'\D','正义永远不会迟到800s'))

在这里插入图片描述
3). w、W

print(re.findall(r'\w','123正义df永远_不会迟到&'))
print(re.findall(r'\W','123正义df永远_不会迟到&'))

在这里插入图片描述
4). s、S

print(re.findall(r'\s','\n正义\t永远\r不会 迟到'))
print(re.findall(r'\S','\n正义\t永远\r不会 迟到'))

在这里插入图片描述

  1. 指定字符出现指定次数

匹配字符出现次数:
: 代表前一个字符出现0次或者无限次; d, .*
+: 代表前一个字符出现一次或者无限次; d+
?: 代表前一个字符出现1次或者0次; 假设某些字符可省略, 也可以不省略的时候使用

第二种方式:
{m}: 前一个字符出现m次;
{m,}: 前一个字符至少出现m次; * == {0, }; + ==={1,}
{m,n}: 前一个字符出现m次到n次; ? === {0,1}
比如匹配电话号码:

pattern=r'\d{3}[\s-]?\d{4}[\s-]?\d{4}'
print(re.findall(pattern,'183 3345 3351'))
print(re.findall(pattern,'18333453351'))

在这里插入图片描述

  1. 表示边界
    ^:表示以什么开头
    $:表示以什么结尾
  2. 表示分组
    | : 匹配| 左右任意一个表达式即可;
    (ab): 将括号中的字符作为一个分组
    \num: 引用分组第num个匹配到的字符串
    (?P): 分组起别名
    (?P=name) : 引用分组的别名
    1). |
import re
# westos 或者 hello
print(re.match(r"westos|hello", "helloaaa").group())
print(re.match(r"westos|hello", "westoshelloaaa").group())

执行的结果:
在这里插入图片描述
2). (ab)

import re
print(re.findall(r'(westos|hello)\d+', 'westos1hello2'))
pattern  = r'<span class="red">(\d+)</span>'
s = '<span class="red">31</span>'
print(re.findall(pattern, s))

执行的结果:
在这里插入图片描述
当使用分组时,可以看出 findall 只能获取到分组里面的内容

import re
pattern  = r'(<span class="red">(\d+)</span>)'
s = '<span class="red">31</span>'
print(re.findall(pattern, s))
print(re.findall(r'((westos|hello)\d+)', 'westos1hello2'))

执行的结果:
在这里插入图片描述
当findall不能满足时, 这是就需要考虑使用search 或者match

import re
Obj = re.search(r'(westos|hello)(\d+)', 'westos1hello2')
if Obj:
    print(Obj.group())
    print(Obj.groups())
else:
    print('Not Found')

执行的结果:
在这里插入图片描述
3). \num

import re
s = '<html><title>正则表达式</title></html>'
pattern = r'<(\w+)><(\w+)>(\w+)</\w+></\w+>'
print(re.findall(pattern, s))

执行的结果:
在这里插入图片描述
没有用分组方法前可以这样写
但是发现有重复的代码块
使用分组方法后:

import re
s = '<html><title>正则表达式</tite></html>'
# 目前有三个分组, \1: 代指第一个分组的内容, \2: 代指第二个分组的内容,
pattern = r'<(\w+)><(\w+)>(\w+)</\2></\1>'
print(re.findall(pattern, s))

执行的结果:
在这里插入图片描述
4). (?P)

import re
s1 = 'http://www.westos.org/linux/book/'
pattern = 'http://[\w\.]+/(?P<courseName>\w+)/(?P<courseType>\w+)/'
print(re.findall(pattern, s1))
Obj = re.match(pattern, s1)
if Obj:
    print(Obj.group())
    print(Obj.groups())
    print(Obj.groupdict())
else:
    print('Not Found')

执行的结果:
在这里插入图片描述
从这里就可以看到 group、groups 和 groupdict 三者之间的区别
group 返回的是一个列表
groups 返回的是一个元组
groupdict 返回的是一个字典,通常与 (?P) 配合使用

  1. 匹配qq邮箱
    找出列表中符和条件的邮箱地址, 并存储到/tmp/mail.txt文件中
    邮箱地址以@qq.com结尾
    @qq.com前面的内容由字母,数字或者下划线组成, 但至少4位, 最多20
import re
mailList =["[email protected]", "[email protected]", "[email protected]", "[email protected]"
"[email protected]", "[email protected]"]
# 判断邮件地址是否合法;
def ismailOK(mail_name):
    reg = r"\w{4,20}@qq.com$"
    reg = re.compile(reg)
    a = re.match(reg, mail_name)
    if a:
        return True
    else:
        return False
mailOkList = [i for i in mailList if ismailOK(i)]
# 将符和条件的邮件地址写入文件 ;
with open("/tmp/mail.txt", "a+") as f:
    for i in mailOkList:
        f.write(i+"\n")
        print(i)

执行的结果:
在这里插入图片描述

  1. re高级用法
    search()方法: 只找到符和条件的第一个并返回;
    findall()方法: 返回符合条件的所有内容;
    sub()方法: 对符合正则的内容进行替换;
    split()方法: 指定多个分隔符进行分割
    1). sub()
    将1000替换为0
import re
s = "阅读次数为1000, 转发次数为100"
reg = r"\d+"
a = re.search(reg, s)
a.group()
re.findall(reg, s)
print(re.sub(reg, '0' , s))

执行的结果:
在这里插入图片描述
在1000的基础上加1

import re
s = "阅读次数为1000, 转发次数为100"
reg = r"\d+"
a = re.search(reg, s)
def addNum(x):
    a = int(x.group()) + 1
    return str(a)
print(re.sub(reg, addNum, s))

执行的结果:
在这里插入图片描述
2). split()
用 :将字符串分割开

import re
s = "fentiao 18:18811112222"
print(re.split(r":| ", s))

执行的结果:
在这里插入图片描述

二 运用正则表达式来实现爬虫

  1. 获得贴吧上的邮箱
    作为一个吧主,分享了一波资源,下面一群萌新留下了他的邮箱,你想给这些萌新将内容通过邮箱发送过去,总不能一个一个的来记录邮箱吧。
    这里就用到了爬虫系统,将一个个邮箱爬去下来,记入一个文档中
    实现步骤:
    1).先根据正则表达式爬取到该帖子的总页数
    2).再根据正则表达式爬取每一页的邮箱地址
    3).最后将爬取到的邮箱写入文档中
from itertools import chain
from urllib.request import urlopen
import re
def getPageContent(url):
    """
        获取网页源代码
    :param url: 指定url内容
    :return: 返回页面的内容(str格式)
    """
    with urlopen(url) as html:
        return  html.read().decode('utf-8')

def parser_page(content):
    """
    根据内容获取所有的贴吧总页数;
    :param content: 网页内容
    :return: 贴吧总页数
    """
    pattern = r'<span class="red">(\d+)</span>'
    data = re.findall(pattern, content)
    return  data[0]


def parser_all_page(pageCount):
    """
    根据贴吧页数, 构造不同的url地址;并找出所有的邮箱
    :param pageCount:
    :return:
    """
    emails = []
    for page in range(int(pageCount)):
        url = 'http://tieba.baidu.com/p/2314539885?pn=%d' %(page+1)
        print("正在爬取:%s" %(url))
        content = getPageContent(url)
        # pattern = r'\w[-\w.+]*@[A-Za-z0-9][-A-Za-z0-9]+\.+[A-Za-z]{2,14}'
        pattern = r'[a-zA-Z0-9][-\w.+]*@[A-Za-z0-9][-A-Za-z0-9]+\.+[A-Za-z]{2,14}'
        findEmail = re.findall(pattern, content)
        print(findEmail)
        emails.append(findEmail)
    return  emails
def main():
    url = 'http://tieba.baidu.com/p/2314539885'
    content = getPageContent(url)
    pageCount = parser_page(content)
    emails = parser_all_page(pageCount)
    print(emails)
    with open('tiebaEmail.txt', 'w') as f:
        for tieba in chain(*emails):
            f.write(tieba + '\n')
main()

执行的结果:
在这里插入图片描述
可以看到爬取到了808个邮箱地址,节省了大量的时间

  1. 爬取指定贴吧页图片
    原理是跟爬取邮箱地址是一样的,先爬取总页数,再将每一页的吧主发布的图片都爬取出来
import re
from urllib.request import urlopen

def getContent(url):
    with urlopen(url)as f:
        return f.read()

def parser_get_img_url(content):
    pattern = r'<img class="BDE_Image".*?src="(http://.*?\.jpg)".*?>'
    imgUrl = re.findall(pattern, content.decode('utf-8'))
    return imgUrl

def main():
    url = 'http://tieba.baidu.com/p/5255105555'
    content = getContent(url)
    imgLi = parser_get_img_url(content)
    for index, img in enumerate(imgLi):
        content = getContent(img)
        with open('img/%s.jpg' % (index + 1), 'wb')as f:
            f.write(content)
            print('第%s个图片下载成功...' % (index + 1))

main()

执行的结果:
在这里插入图片描述

  1. 反爬虫第一步—伪装浏览器
    如果还像平时那样爬取一些网站的话,一些网站是不允许这样做的,会拒绝你的访问
    比如:
    中国的一些银行信息
url = "http://www.cbrc.gov.cn/chinese/jrjg/index.html"
with urlopen(url) as html:
    content = html.read().decode('utf-8')
    print(content)

执行的结果:
在这里插入图片描述
403:拒绝访问
这时就要想办法了,它是不会拒绝浏览器访问的,依次我们可以伪装成浏览器进行爬取信息

url = "http://www.cbrc.gov.cn/chinese/jrjg/index.html"
# 如何伪装成浏览器访问?
# 1. 定义一个真实浏览器的代理名称
user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0"
# 2. 写到请求页面的header里面去
req = request.Request(url,headers={'User-Agent': user_agent} )
#  3. 打开网页, 获取内容
print(urlopen(req).read().decode('utf-8'))

执行的结果:
在这里插入图片描述
这样我们就可以对一些禁止爬取的网站进行爬取了

猜你喜欢

转载自blog.csdn.net/weixin_42668123/article/details/83019293
今日推荐