zoukankan      html  css  js  c++  java
  • Python爬虫之re正则

    1. 基本规则

    # 元字符:  
        # . ^ $ * + ? { } [ ] | ( ) 
    
    # 字符类型匹配:
        #  .  表示匹配任意一个字符(换行符除外)
        #  [asdf]  表示匹配中括号里面的任意一个字母一次 
        #  [a-z]  表示匹配a-z中的任意一个字母    [0-9] 表示匹配0-9中的任意一个数字
        #  [^0-9] 中括号中有^符号,表示非,除---之外,这里表示除0-9之外的任意字符
    
        # d 匹配数字,即 [0-9]
        # D 匹配⾮数字,即不是数字 [^0-9]
        # s 匹配空⽩,即 空格,tab键 [	
    
    fv]
        # S 匹配⾮空⽩ [^	
    
    fv]
        # w 匹配单词字符,即a-z、A-Z、0-9、_  [a-zA-Z0-9_]
        # W 匹配⾮单词字符  [^[a-zA-Z0-9_]]
        #  匹配一个特殊字符边界,比如 空格、&、# 等
    
    # 定位:
        #  ^  表示起始定位
        #  $  表示结束定位
    
    # 匹配次数:
        #  *  表示任意次
        #  +  至少1次 [1,+oo]
        #  ?  匹配0次或者1次
        #  {a,b}  匹配指定的次数范围,如 {0,}相当于匹配任意次 ,{6} 表示匹配6次
    
    # 分组 & 后向引用 & 别名:
        # (ab)   将括号中字符作为⼀个分组
        # 
    um   引⽤分组num匹配到的字符串
        # (?P<name>)  分组起别名
        # (?P=name) 引⽤别名为name分组匹配到的字符
    
    # |  匹配左右任意⼀个表达式

    2. findall

    2.1 贪婪匹配&惰性匹配

    1)贪婪模式

    • findall默认就是贪婪模式,其会尽可能多的匹配
    • findall会将所有匹配符合的内容保存到一个列表中
    import re   # 导入re模块
    
    # findall方法第一个参数是匹配的规则,第二个参数是要匹配的字符串
    # findall会将所有匹配符合的内容保存到一个列表中
    print(re.findall("hgzero", "thisishgzero"))
      # 输出:[hgzero]
    data1 = re.findall("hg", "hgzerohgwzh") print(data1)   # 输出:['hg', 'hg']

    2)惰性模式

    • 惰性模式就是尽可能少的去匹配
    data1 = re.findall("hg*", "hggggg")  # 贪婪模式
    data2 = re.findall("hg*?", "hggggg") # 惰性模式,后面的那个问号就表示惰性模式
    print(data1)  # 输出:['hggggg']
    print(data2)  # 输出:['h']

    2.2 字符串转义流程

    字符串转义的流程:字符串 --> python解释器转义 --> re模块的转义

    # 转义
    ret1 = re.findall("www.baidu", "www.baidu")   # 这里面的 . 会代指任意字符(除
    外)
    ret1 = re.findall("www.baidu", "www.baidu")  # 这里面,反斜杠的添加会让 . 符号失去元字符代指的意义,从而使其就表示普通的点 . 符号
    
    # 字符串转义的流程:  字符串---> python解释器转义---> re模块的转义 
    ret2 = re.findall(r"I", "I hIo Ion")    # 这里面的r ,表示在python层次不使用转义字符,直接将其传递给re模块
    ret3 = re.findall("I\\b", "I hIo Ion")  # 这里适用4个 , 表示在python解释器层次转义成2个 , 然后再将其传入re模块进行转义
    
    
    re.findall("I\b", "I what")   # 这样使\b在python层次被转义成传递给re模块
    re.findall(r"I", "I what")   # 这样在前面加上r ,可以让python不转义字符串内容,而直接传递给re

    3. search

     search会将匹配到的结果保存到一个对象中,且只匹配第一个对象。

    用search取到的对象必须要用group取值。

    # search会将匹配到的结果保存到一个对象中,且只匹配第一个对象
    sear = re.search("d+", "fasdfsaf345kdf89")  # search返回的只是一个对象,且只返回找到的第一个
    retu = sear.group()   # 用search取到的对象必须要用group取值
    
    # 可以用?P<name>的形式给某一部分命名别名
    re.search("(?P<name>[a-z]+)(?P<age>d+)", "hgzero21wzh23hg26").group("name")
    re.search("(?P<name>[a-z]+)(?P<age>d+)", "hgzero21wzh23hg26").group("age")
    
    # 可以一次获取多个值
    pattern = re.compile(r"(?P<prv>[d]+[.]?[d]*)(?P<typ>[-])(?P<after>[d]+[.]?[d]*)")
    prv, typ, aft = pattern.search(calc_str).group("prv", "typ", "after")  # 获取三个值,这三个值会被保存在一个元组中

    4. match

    match只从开始开始匹配,且只匹配一次,返回一个对象,若没匹配到则什么都不返回

    # match只从开始开始匹配,且只匹配一次,返回一个对象,若没匹配到则什么都不返回
    re.match("d+", "234fda") 

    5. split

    split会将字符串按照某字符分割,然后保存为一个列表

    # split会将字符串按照某字符分割
    re.split(" ", "hello abc what")    # 将字符串按照空格分割,保存到一个列表中
    re.split("[ |]", "hello welcome|hi hgzero") # 将字符串按照空格或者|进行分割后保存到一个列表中
    
    re.split("[ab]", "abc")  # 先按照a分割,左边形成一个空,然后将得到的bc再按照b分割,左边又得到一个空
    # 打印结果为  ['', '', 'c']

    6. sub

    sub可以完成字符串的替换功能

    # sub可以完成字符串的替换功能
    re.sub("d+", "A", "welcome666hgzero987")   # 将第三个参数中的字符串中的数字转换成A
    # 这里面的第四个参数可以限定匹配替换的次数
    
    re.subn("d", "A", "welcome666hgzero987")  
    # 将匹配到的内容放在一个元组里,结果中的第二个值为匹配替换的次数
    # 打印结果为   ('welcomeAAAhgzeroAAA', 6)
    result = re.sub(r"([d+-*/.]*)", value, calc_str, 1)  # 将calc_str中被正则匹配到的内容替换为value,且只替换第一次匹配到的

    7. compile

    compile可以事先定义好规则,保存为一个对象,然后后面可以直接使用这个对象而无需再定义规则

    # compile可以事先定义好规则,保存一个对象,然后后面可以直接使用这个对象而无需再定义规则
    com = re.compile("d+")
    com.findall("welcome666hgzero987")
    
    # 使用示例
    pattern = re.compile(r"((?P<init>[d+-*/.]*))")
    init_str = pattern.search(calc_str).group("init")
    
    # re.S,可以让点号 . 匹配所有特殊字符,包括换行 
     等
    re.compile('<dd>.*?>(?P<index>d+)<.*?data-src="(?P<imglink>.*?)".*?</dd>', re.S)

    8. finditer

    finditer可以将得到的数据保存到一个迭代器中

    # finditer可以将得到的数据保存到一个迭代器中
    ret = re.finditer("d", "welcome666hgzero987")
    next(ret).group()    # 可以通过next函数加上group调用迭代器中的内容
    
    
    re.findall("www.(baidu|163).com", "www.baidu.com")  # findall会优先将分组中的内容返回
    # 这里的返回结果为  ['baidu']
    re.findall("www.(?:baidu|163).com", "www.baidu.com")  #  【在分组中加上 ?: 可以去掉分组的优先级】

    9. 正则小练习

    写一个计算器,思路:

    1. 先去除空格
    2. 对特殊字符报错(字母、加减乘除之外的字符)
    3. 括号都是成对的, 要从最内部的括号开始计算, 由内而外, 计算后得出结果
    4. 将3步骤得到的结果去替换3步骤匹配到的括号及内部的内容, 然后得到一个新的字符串
    5. 重复执行3步骤和4步骤, 最终得到一个不包含任何括号的字符串, 计算这个字符串表达式就是最终的结果
    # 计算器  3+ ((1* 5*3) + ( (3* (8/2)/2) +2 ) *2) *2 + 4
    
    import re
    
    def del_space(calc_str):
        """
        去除整个字符串中的空格
        :param calc_str: 原始字符串
        :return: 去除空格后的字符串
        """
        data = calc_str.replace(" ", "")
        return data
    
    def check_special(calc_str):
        """
        检测字符串中是否有字母及非运算的特殊字符,
        :param calc_str: 原始字符串
        :return: True or False, 若为True,则表示存在特殊字符
        """
        pattern = re.compile(r"[^d+-*/()]")
        check = pattern.findall(calc_str)
        return check
    
    def calculate(calc_str):
        """
        对字符串中的表达式进行计算
        :param calc_str: 数学计算表达式类型的字符串
        :return: 计算后的结果
        """
        while True:
            if calc_str.find("*") != -1:
                pattern = re.compile(r"(?P<prv>[d]+[.]?[d]*)(?P<typ>[*])(?P<after>[d]+[.]?[d]*)")
                prv, typ, aft = pattern.search(calc_str).group("prv", "typ", "after")
                # re.sub("%s%s%s" % (prv, typ, aft), str(value), calc_str)
                calc_str = calc_str.replace("%s%s%s" % (prv, typ, aft), str(float(prv) * float(aft)))
                continue
            elif calc_str.find("/") != -1:
                pattern = re.compile(r"(?P<prv>[d]+[.]?[d]*)(?P<typ>[/])(?P<after>[d]+[.]?[d]*)")
                prv, typ, aft = pattern.search(calc_str).group("prv", "typ", "after")
                calc_str = calc_str.replace("%s%s%s" % (prv, typ, aft), str(float(prv) / float(aft)))
                continue
            elif calc_str.find("+") != -1:
                pattern = re.compile(r"(?P<prv>[d]+[.]?[d]*)(?P<typ>[+])(?P<after>[d]+[.]?[d]*)")
                prv, typ, aft = pattern.search(calc_str).group("prv", "typ", "after")
                calc_str = calc_str.replace("%s%s%s" % (prv, typ, aft), str(float(prv) + float(aft)))
                continue
            elif calc_str.find("-") != -1:
                pattern = re.compile(r"(?P<prv>[d]+[.]?[d]*)(?P<typ>[-])(?P<after>[d]+[.]?[d]*)")
                prv, typ, aft = pattern.search(calc_str).group("prv", "typ", "after")
                calc_str = calc_str.replace("%s%s%s" % (prv, typ, aft), str(float(prv) - float(aft)))
                continue
            else:
                break
        return calc_str
    
    def replace_expr(calc_str):
        """
        找到最内部的括号, 计算出值后将值替换到字符串中
        :param calc_str: 纯粹的要计算出结果的字符串
        :return: 当前字符串最内部的值
        """
        try:  # re的search方法如果匹配不到内容就会报错
            pattern = re.compile(r"((?P<init>[d+-*/.]*))")
            init_str = pattern.search(calc_str).group("init")
            value = str(calculate(init_str))
            result = re.sub(r"([d+-*/.]*)", value, calc_str, 1)  # 只替换第一次匹配到的
            return result
        except Exception:
            return None
    
    while True:
        origin_str = input("请输入要计算的内容: ")
        calc_str = del_space(origin_str)
        if check_special(calc_str):
            print("存在特殊字符, 请检查后再计算!")
            continue
        while True:
            str_init = replace_expr(calc_str)
            if str_init:
                calc_str = str_init
            else:
                value = calculate(calc_str)
                print("最终的结果为: ", value)
                break
    
    # 测试: calc_strx = "3+ ((1* 5*3) + ( (3* (8/2)/2) +2 ) *2) *2 + 4 "
    # 正确结果为:69

    10. requests+re正则爬虫实战

    requests+re正则爬取猫眼电影Top100电影信息

    import re
    import requests
    import urllib3
    import json
    
    # 忽略警告
    urllib3.disable_warnings()
    
    def get_one_page(url, headers):
        try:
            response = requests.get(url, headers=headers, verify=False)
            if response.status_code == 200:
                return response.text
            else:
                return None
        except Exception as e:
            print(e)
            return None
    
    def get_video_info(html_text):
        pattern = re.compile('<dd>.*?>(?P<index>d+)<.*?data-src="(?P<imglink>.*?)".*?data-val.*?">(?P<name>.*?)<.*?'
                             'star">(?P<star>.*?)<.*?releasetime">(?P<releasetime>.*?)<.*?'
                             'integer">(?P<x>.*?)<.*?fraction">(?P<y>.*?)<.*?</dd>', re.S)
        item = re.findall(pattern, html_text)
        for i in item:
            yield {
                "序号": i[0],
                "电影名": i[2],
                "主演": i[3].strip()[3:],
                "上映时间": i[4][5:],
                "评分": i[5]+i[6],
                "图像链接": i[1],
             }
    
    def save_as_file(data):
        with open("movie_info.txt", "a+", encoding="utf-8") as f:
            info = json.dumps(data, ensure_ascii=False)  # 不以ascii码方式显示
            f.write(info + "
    ")
    
    def main(n):
        url = "http://maoyan.com/board/4?offset=" + str((n-1)*10)
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36",
            "Referer": "http://maoyan.com/board",
        }
        html_text = get_one_page(url, headers)
        for i in get_video_info(html_text):
            print(i)
            save_as_file(i)
    
    if __name__ == '__main__':
        for i in range(1, 11):
            main(i)

  • 相关阅读:
    Server基本语句的用法
    C#本地文件下载以及FTP文件服务下载(以Pdf文件为例)
    一步一步理解AdaBoosting(Adaptive Boosting)算法
    Ubus简单理解
    基于SSL的select编程Linux系统
    SSL协议理解
    802.11r协议理解
    基于MQTT协议的云端proxy远程登陆
    基于mosquitto的MQTT客户端实现C语言
    模拟telnet协议C语言客户端程序
  • 原文地址:https://www.cnblogs.com/hgzero/p/14125634.html
Copyright © 2011-2022 走看看