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)

  • 相关阅读:
    【java】对象赋值给另一个对象
    spring boot系列(五)spring boot 配置spring data jpa (查询方法)
    Spring Data JPA 查询
    Spring Data JPA 介绍
    OpenID简介
    OAUTH协议介绍
    URL encoding(URL编码)
    RESTful 介绍
    spring boot系列(四)spring boot 配置spring data jpa (保存修改删除方法)
    spring boot 启动报 java.lang.NoClassDefFoundError: ch/qos/logback/core/spi/LifeCycle 错误
  • 原文地址:https://www.cnblogs.com/hgzero/p/14125634.html
Copyright © 2011-2022 走看看