Java的正则表达式和爬虫

正则表达式Regex和爬虫

  • 正则表达式(Regular Expression,简称 Regex)是一种文本匹配的模式,用于对字符串进行搜索、匹配、提取、替换等操作。它广泛应用于字符串处理领域,例如文本解析、验证用户输入、文件内容查找等。

作用

  1. 校验字符串是否满足规则
  2. 在一段文本中查找满足要求的内容

一、正则表达式语法:

在这里插入图片描述

正则表达式在字符串方法中的使用:

方法名 说明
public boolean matches(String regex) 判断字符串是否满足正则表达式的规则
public String replaceAll(String regex,String newStr) 按照正则表达式的规则进行替换
public String[] split(String regex) 按照正则表达式的规则切割字符串

方法2、3的演示

public class RegexDemo07 {
    public static void main(String[] args) {
        // 正则表达式在字符串方法中的使用

        // 有一段字符串:小诗诗dqwefqwfqwfwq12312小丹丹dqwefqwfqwfwq12312小惠惠
        // 要求1:把字符串中三个姓名之间的字母替换为vs
        // 要求2:把字符串中的三个姓名切割出来

        String str = "小诗诗dqwefqwfqwfwq12312小丹丹dqwefqwfqwfwq12312小惠惠";
        String str1 = str.replaceAll("[\\w&&[^_]]+", "VS");
        System.out.println(str1);	// 输出: 小诗诗VS小丹丹VS小惠惠
        // 细节:
        // 方法在底层跟之前一样也会创建文本解析器的对象
        // 然后从头开始去读取字符串中的内容,只要有满足的,那么就用第二个参数去替换

        // 在所指定的文本处分割
        String[] strarr = str.split("[\\w&&[^_]]+");
        for (int i = 0; i < strarr.length; i++) {
            System.out.println(strarr[i]);	// 输出: 小诗诗 小丹丹 小惠惠
        }
    }
}

详细示例

public class RegexDemo01 {
    public static void main(String[] args) {
        // 验证手机号码: 13815644862 19748524761
        String regex1 = "1[3-9]\\d{9}";
        // 验证分成三部分:
        // 第一部分: 1 表示手机号码只能以1开头
        // 第二部分: [3-9] 表示手机号码第二位只能是3-9之间的
        // 第三部分: \\d{9} 表示任意数字可以出现9次,也只能出现9次
        System.out.println("13546874556".matches(regex1));  // 输出: true
        System.out.println("12546874556".matches(regex1));  // 输出: false
        System.out.println("62546874556".matches(regex1));  // 输出: false
        System.out.println("125468745565".matches(regex1)); // 输出: false

        // 验证座机号码: 020-2324242 02122442 027-42424 0712-3242434
        String regex2 = "0\\d{2,3}-?\\d{4,9}";
        System.out.println("020-2324242".matches(regex2));  // 输出: true
        System.out.println("02122442".matches(regex2));     // 输出: true
        System.out.println("0712-3242434".matches(regex2)); // 输出: true
        System.out.println("07123-3242434".matches(regex2));// 输出: false

        // 验证邮箱号码:
        // 3232323@qq.com zhangsan@itcast.cnn dlei0009@163.com dlei0009@pci.com.cn
        // 细节:
        // 要显示.需要加转义字符——因为.是预定义字符,代表任何字符 \\.
        // .com这类的可以出现一次或两次,这时候可以使用()进行分组,可以在后面规定出现的次数

        String regex3 = "\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2}";
        System.out.println("3232323@qq.com".matches(regex3));       // 输出: true
        System.out.println("zhangsan@itcast.cnn".matches(regex3));  // 输出: true
        System.out.println("dlei0009@163.com".matches(regex3));     // 输出: true
        System.out.println("dlei0009@pci.com.cn".matches(regex3));  // 输出: true
        // 在实际的开发中,很少会自己写正则表达式
        // 百度一个类似的,自己改成公司要求的

        // 24小时的正则表达式
        String regex4 = "([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d";
        System.out.println("23:11:11".matches(regex4)); // 输出: true

        // 身份证号码的简单校验
        String regex5 = "[1-9]\\d{16}(\\d|X|x)";
        // String regex5 = "[1-9]\\d{16}(\\d|[Xx])";
        // String regex5 = "[1-9]\\d{16}(\\d|(?i)x)";
        System.out.println("41080119930228457x".matches(regex5));   // 输出: true
        System.out.println("510801197609022309".matches(regex5));   // 输出: true
        System.out.println("15040119810705387X".matches(regex5));   // 输出: true
        System.out.println("130133197204039024".matches(regex5));   // 输出: true
        System.out.println("430102197606046442".matches(regex5));   // 输出: true

        // 忽略大小写的书写方式
        // 在匹配的时候忽略abc大小写
        String regex6 = "(?i)abc";
        // 忽略(?i)后面的大小写
        System.out.println("------------------------");
        System.out.println("abc".matches(regex6));  // 输出: true
        System.out.println("Abc".matches(regex6));  // 输出: true
        System.out.println("ABC".matches(regex6));  // 输出: true
        // 如果要仅忽略b的大小写
        String regex7 = "a((?i)b)c";
        System.out.println("------------------------");
        System.out.println("abc".matches(regex7));  // 输出: true
        System.out.println("ABc".matches(regex7));  // 输出: false
        System.out.println("aBc".matches(regex7));  // 输出: true

        // 身份证号码的复杂校验
        //前面6位:省份,市区,派出所等信息 第一位不能是0,后面5位是任意数字
        //年的前半段:18 19 20
        //年的后半段:任意数字出现两次
        //月份:01~09 10 11 12
        //日期:01~31
        //后面四位:任意数字出现3次 最后一位可以是数字也可以是大写x或者小写x
        String regex8 = "[1-9]\\d{5}(18|19|20)\\d{2}(0\\d|1[0-2])([0-2]\\d|3[01])\\d{3}(\\d|(?i)x)";
        System.out.println("41080119930228457x".matches(regex8));
        System.out.println("510801197609022309".matches(regex8));
        System.out.println("15040119810705387X".matches(regex8));
        System.out.println("130133197204039024".matches(regex8));
        System.out.println("430102197606046442".matches(regex8));
    }
}

在这里插入图片描述

在这里插入图片描述

二、爬虫:

  • 本地爬虫
  • 网络爬虫

原理:通过正则表达式从文本中爬取指定的数据。

1、本地爬虫

  • 从一个字符串中爬取指定的数据

需要用到regex包下的两个类PatternMatcher

  • Pattern——表示正则表达式
  • Matcher——文本匹配器,按照正则表达式的规则去读取字符串,从头开始读取

示例

	// 爬取数据
        String str = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,"+
                    "因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";	// 从这段文本中获取JavaXX的数据

        // 获取正则表达式对象
        // 调用Pattern中的静态方法来创建Pattern对象————Pattern.compile()
        Pattern p = Pattern.compile("Java\\d{0,2}");

        // 获取文本匹配器对象
        // 调用Pattern中的matcher方法传递所要查询的源文本,返回的类型就是Matcher
        Matcher m = p.matcher(str);
        // m: 文本匹配器对象
        // str: 所要查询的源文本
        // p: 规则
        // 即: m要在str中找到符合p规则的小串

        // 拿着文本匹配器从开头开始读取,寻找是否有满足规则的子串
        // 如果没有,方法就返回false
        // 如果有,则返回true,并在底层记录子串的起始索引和结束索引+1

        // boolean b = m.find();
        // 方法底层会根据find方法记录的索引进行字符串的截取
        // subString(起始索引, 结束索引);包头不包尾
        while(m.find()) {
            String s = m.group();
            System.out.println(s);
        }

注意事项

  • 文本匹配器每调用一次find()方法,它捕获到的指定格式文本也会搜索下一个
  • find()方法的返回值为布尔类型,也可用于判断是否有想要获取的文本
  • 当调用Matcher中的group()方法时,底层会返回截取到的子字符串

2、网络爬虫

  • 从网站中爬取想要的数据

这里准备了一个带随机生成身份证号的网站:https://yapi.pro/mock/583732/getsource

示例代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexDemo03 {
    public static void main(String[] args) throws IOException {
        // 网络爬虫
        // 从网站中爬取想要的数据

        // 创建一个URL对象
        URL url;
        try {
            url = new URL("https://yapi.pro/mock/583732/getsource");
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }

        // 连接上这个网站————保证网络是畅通的
        URLConnection conn = url.openConnection();

        // 创建一个对象去读取网站中的数据
        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String line;

        // 获取正则表达式的对象 Pattern
        String regex = "[1-9]\\d{17}";
        Pattern pattern = Pattern.compile(regex);
        // 在读取的时候每次读一整行
        while((line = br.readLine()) != null) {
            // 拿着文本匹配器的对象matcher按照pattern的规则去读取当前的这一行信息
            Matcher matcher = pattern.matcher(line);
            while(matcher.find()) {
                System.out.println(matcher.group());
            }
        }

        br.close();
    }
}

运行后即可爬取这个网站中的身份证号码


3、爬虫的深入:

3.1 带条件的爬取(非捕获分组)
符号 含义 举例
(? : 正则) 获取所有 Java(?:8|11|17)
(? = 正则) 获取前面的部分 Java(?=8|11|17)
(? ! 正则) 获取不是指定内容的前面部分 Java(?!8|11|17)

演示代码

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexDemo05 {
    public static void main(String[] args) {
        // 带条件的爬取数据
        String str = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,"+
                "因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";
        // 需求1:爬取版本号为8,11,17的Java文本,但是只要Java,不显示版本号。
        // 需求2:爬取版本号为8,11,17的Java文本。正确爬取结果为:Java8 Java11 Java17 Java17
        // 需求3:爬取除了版本号为8,11,17的Java文本

        // 1.定义正则表达式
        // ?理解为前面的数据Java
        // =表示在Java后面跟随的数据
        // 但是在获取的时候,只能获取前半部分
        String regex1 = "Java(?=8|11|17)";

        // :表示获取整体所有的值
        String regex2 = "Java(?:8|11|17)";
//        String regex2 = "Java(8|11|17)";

        // !表示去除
        String regex3 = "Java(?!8|11|17)";
        Pattern p1 = Pattern.compile(regex3);
        Matcher m1 = p1.matcher(str);

        while(m1.find()) {
            System.out.println(m1.group());
        }
    }
}
3.2贪婪爬取和非贪婪爬取
  • 贪婪爬取:在爬取数据的时候尽可能的多获取数据
  • 非贪婪爬取:在爬取数据的时候尽可能少的获取数据

在Java当中,默认就是贪婪爬取,如果我们在数量词+ *的后面加上?,那么此时就是非贪婪爬取

演示代码

public class RegexDemo06 {
    public static void main(String[] args) {
        String str = "Java自从95年问世以来,abbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa" +
                "经历了很多版本,目前企业中用的最多的是Java8和Java11,"+
                "因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";
        // 贪婪爬取和非贪婪爬取


        // 创建正则表达式对象
        // 贪婪爬取
        Pattern p1 = Pattern.compile("ab+");	// 会在下面输出: abbbbbbbbbb
        // 非贪婪爬取
        Pattern p2 = Pattern.compile("ab+?");	// 会在下面输出: ab
        Matcher m = p2.matcher(str);
        while(m.find()) {
            System.out.println(m.group());
        }

    }
}
3.3分组

在这里插入图片描述

  • 分组就是一个小括号
  • 分组是有组号的,也就是序号。
    • 规则一:从1开始,连续不间断
    • 规则二:以左括号为基准,最左边的是第一组,其次为第二组,以此类推(嵌套小括号也是,只看左括号的顺序,在最左边就是第一个)
捕获分组和非捕获分组
  • 捕获分组:(可以获取每组中的内容反复使用)
    • 后续还要继续使用本组的数据。
    • 正则内部使用:\\+组号
    • 正则外部使用:$组号
  • 非捕获分组:(分组之后不需要再用本组数据,仅仅是把数据括起来)
    • 就是上面的三种((?:)(?=)(?!))带条件爬取数据(若要使用这些分组的数据会报错)

捕获分组的两个代码实例

1、正则内部使用分组:

public class RegexDemo08 {
    public static void main(String[] args) {
        // 需求1:判断一个字符串的开始字符和结束字符是否一致?只考虑一个字符
        // 举例:a123a   b456b   17891   &abc&   a123b(false)
        //  \\+组号: 表示把第x组的内容再出来用一次
        String regex1 = "(.).+\\1";
        System.out.println("a123a".matches(regex1));    // true
        System.out.println("b456b".matches(regex1));    // true
        System.out.println("17891".matches(regex1));    // true
        System.out.println("&abc&".matches(regex1));    // true
        System.out.println("a123b".matches(regex1));    // false
        System.out.println("----------------------------------");

        // 需求2:判断一个字符串的开始部分和结束部分是否一致?可以有多个字符
        // 举例:abc123abc   b456b   123789123   &!@abc&!@   abc123abd(false)
        String regex2 = "(.+).+\\1";
        System.out.println("abc123abc".matches(regex2));    // true
        System.out.println("b456b".matches(regex2));    // true
        System.out.println("123789123".matches(regex2));    // true
        System.out.println("&!@abc&!@".matches(regex2));    // true
        System.out.println("abc123abd".matches(regex2));    // false
        System.out.println("----------------------------------");

        // 需求3:判断一个字符串的开始部分和结束部分是否一致?开始部分内部每个字符也需要一致
        // 举例:aaa123aaa   bbb456bbb   111789111   &&abc&&
        String regex3 = "((.)\\2).+\\1";
        System.out.println("aaa123aaa".matches(regex3));    // true
        System.out.println("bbb456bbb".matches(regex3));    // true
        System.out.println("111789111".matches(regex3));    // true
        System.out.println("&&abc&&".matches(regex3));    // true
        System.out.println("&&abc&*".matches(regex3));    // false
        System.out.println("----------------------------------");

    }
}

2、正则外部使用分组:

ublic class RegexDemo09 {
    public static void main(String[] args) {
        // 捕获分组
        /* 需求:
            将字符串:我要学学编编编编程程程程程程
            替换为:我要学编程
        */
        // 把重复的内容替换为单个的
        String str = "我要学学编编编编程程程程程程";
        String result = str
上一篇:Spring Security使用基本认证(Basic Auth)保护REST API


下一篇:蓝牙电话-如何自动切换手机SIM卡(预研方向)