Java 爬虫

475 阅读5分钟

爬虫

爬取本地数据

  • java.util.regex包中有Pattern类,这个类就表示正则表达式,获取这个对象就相当于获取正则表达式的对象

    • 通过静态方法compile()获取正则表达式对象

      • public static Pattern compile(String regex)
    • 获取获取文本匹配器对象

      • public Matcher matcher(CharSequence input)
  • java.util.regex包中有Matcher类,这个类表示文本匹配器,作用是按照正则表达式去读取字符串,从头开始读取

    • 使用find()方法开始读取,寻找是否有满足规则的小串
      • 如果没有,方法返回false
      • 如果有,返回true,在底层记录子串的起始索引和结束索引+1
      • public boolean find()
    • 使用group()方法截取字符串
      • 方法底层会根据find()方法记录的索引进行字符串的截取
      • 方法的底层原理与Sting类中的subString(int beginLindex, int endIndex)方法相同
      • 会将截取的小串进行返回
      • public String group()
//找出里面的所有JavaXX
String str = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,因为这两个是长期支持版本,下一个长期支持版木是Java17,相信在未来不久Java17也会逐渐登上历史舞台";

//获取正则表达式对象
Pattern p = Pattern.compile("Java\\d{0,2}");
//获取正则表达式对象
//理解: m:文本匹配器对象 str:大串 p:规则
//m要在str中找符合p规则的小串
Matcher m = p.matcher(str);
//开始读取
boolean b = m.find();
String s1 = m.group();
sout(s1);
//直到这里文本匹配器只读取到了第一个Java
//停在了第一个Java的后面
//所以后面还要调用find()
b = m.find();
String s2 = m.group();
sout(s2);
......
  • 使用循环改进代码
Pattern p = Pattern.compile("Java\\d{0,2}");
Matcher m = p.matcher(str);
while (m.find()) {
    String s = m.group();
    sout(s);
}

爬取网络数据

//将连接:https://m.sengzan.com/jiaoyu/29104.html?ivk_sa=1025883i 中所有的身份证号码都爬取出来

//创建一个URL对象
URL url = new URL("https://m.sengzan.com/jiaoyu/29104.html?ivk_sa=1025883i");
//连接上这个网址  保证网络畅通
URLConnection conn = url.openConnection();
//创建一个对象去读取网络中的数据
BufferedReader br = new BufferedReader(new(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()) {
        sout(matcher.group());
    }
    br.close();
}

带条件爬取

  • 爬取版本号为8,11,17的java文本,但是只要java,不显示版本号

    • String regex = "Java(?=8|11|17)";
      • ? 理解为前面的数据Java
      • = 表示在Java后面要跟的数据
      • 但是在获取的时候,只获取前半部分
  • 爬取版本号为8,11,17的java文本,正确爬取结果为Java8,Java11,Java17

    • String regex = "Java(?:8|11|17)";
      • : 表示在Java后面要跟的数据
      • 在获取的时候获取全部
  • 爬取除了版本号为8,11,17的java文本

    • String regex = "Java(?!8|11|17)";
      • ! 表示在Java后面要跟的数据
      • 但是在获取的时候,满足条件的全部不获取

贪婪爬取和非贪婪爬取

  • 贪婪爬取:在爬取数据时尽可能的多获取数据
  • 非贪婪爬取:在爬取数据时尽可能的少获取数据
  • Java中默认贪婪爬取
  • 如果我们在数量词+ * 后面加上问号,那么此时就是非贪婪爬取
//abbbbbbbbbbbbbaaaaaaaaaaaa
// 对ab+ 进行贪婪爬取和非贪婪爬取
//贪婪爬取
String regex1 = "ab+";
//非贪婪爬取
String regex2 = "ab+?";

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

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

    • 首先在String类中的replaceAll(String regex, String newStr)方法中,创建了newStr的文本匹配器的对象,并用这个对象去调用Matcher类中的replaceAll(String replacement)方法,最后进行返回

      • return Pattern.compile(regex).matcher(this).replaceAll(replacement)
    • 在Matcher类中的replaceAll(String replacement)方法中,首先创建了StringBuilder对象用于字符串的拼接,然后使用do while循环进行字符串的拼接,最后使用toString()方法进行返回

//Matcher类中的replaceAll(String replacement)方法
public String replaceAll(String replacement) {
        reset();
        boolean result = find();
        if (result) {
            StringBuilder sb = new StringBuilder();
            do {
                appendReplacement(sb, replacement);
                result = find();
            } while (result);
            appendTail(sb);
            return sb.toString();
        }
        return text.toString();
    }
  • split(String regex)
    • 根据给定正则表达式的匹配拆分字符串
    • 对于字符串"boo:and:foo"
      • regex = ":"
        • { "boo", "and", "foo" }
      • regex = "o"
        • { "b", "", ":and:f" }

捕获分组和非捕获分组

分组

  • 带小括号的就是分组
  • 每组是有组号的,也就是序号
    • 从1开始,连续不间断
    • 以左括号为基准,最左边的是第一组,其次为第二组,以此类推

捕获分组

  • 捕获分组就是把这一组的数据捕获出来,再用一次

    • 需求1:判断一个字符串的开始字符和结束字符是否一致?只考虑一个字符

      • String regex1 = "(.).+\ \1";
      • \ \组号:表示将第X组的内容再拿出来用一次
    • 需求2:判断一个字符串的开始部分和结束部分是否一致?可以有多个字符

      • String regex2 = "(.+).+\ \1";
    • 需求1:判断一个字符串的开始字符和结束字符是否一致?开始部分内部每个字符也需要一致

      • String regex3 = "((.)\ \2*).+\ \1";
      • 以左括号为基准,最左边的是第一组,其次为第二组,以此类推
  • 正则内部使用:\ \组号

  • 正则外部使用:$组号

//需求:
//将字符串:我要学学编编编编编编程程程程程程
//替换为我要学编程

String str = "我要学学编编编编编编程程程程程程";
String result = str.replaceAll("(.)\\1+", $1);
// (.)表示把重复内容的第一个字符看作一组
// \\1表示第一字符再次出现
// +表示至少一次
// $1表示把正则表达式中第一组内容再拿出来用

非捕获分组

  • 分组之后不需要再用本组数据,仅仅是把数据括起来
符号含义举例
(?:正则)获取所有,更多使用这个Java(?:8|11|17)
(?=正则)获取前面部分Java(?=8|11|17)
(?!正则)获取不是指定内容的前面部分Java(?!8|11|17)