爬虫
爬取本地数据
-
在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()
- 使用find()方法开始读取,寻找是否有满足规则的小串
//找出里面的所有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" }
- regex = ":"
捕获分组和非捕获分组
分组
- 带小括号的就是分组
- 每组是有组号的,也就是序号
- 从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) |