案例要求
https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d&from=kg0
http://www.haoming8.cn/baobao/10881.html
http://www.haoming8.cn/baobao/7641.html
上面三个网址分别表示百家姓,男生名字,女生名字,如图:
要求:
获取上述网址中的内容,利用正则表达式爬取姓氏和名字信息,并生成不重复的10个男生的姓名和10个女生的姓名 ,将生成的姓名保存到本模块下的a.txt文件中。
关于网络的方法
URL网址对象
|-------------------------|---------------------|
| 构造方法 | 说明 |
| public URL(String spec) | 利用记录网址的字符串创建一个网址的对象 |
|---------------------------------------|------------------------------------|
| 成员方法 | 说明 |
| public URLConnection openConnection() | 网址对象调用该方法让程序连接网址,返回程序和URL之间的通信链接对象 |
URLConnection通信链接对象
|-------------------------------------|--------------|
| 成员方法 | 说明 |
| public InputStream getInputStream() | 得到连接网址的字节输入流 |
关于爬虫的方法
Pattern正则表达式对象
|---------------------------------------------|----------------------------|
| 构造方法 | 说明 |
| public static Pattern compile(String regex) | 获取正则表达式的对象,传递的是表示正则表达式的字符串 |
|------------------------------------|--------------------------------------|
| 成员方法 | 说明 |
| public Matcher matcher(String str) | 正则表达式对象调用该方法获取文本匹配器的对象,传递的是需要进行查找的大串 |
Matcher文本匹配器对象
|-----------------------|------------------------------------------------------------------------------|
| 成员方法 | 说明 |
| public boolean find() | 让文本匹配器从头开始读取大串,寻找是否有满足正则表达式的子串。如果没有,方法返回false,如果有,返回true。在底层记录子串的起始索引和结束索引+1 |
| public String group() | 方法底层会根据find()方法记录的索引进行字符串的截取,返回截取的小串,该小串就是符合正则表达式要求的子串 |
代码实现
java
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test1 {
public static void main(String[] args) throws IOException {
//记录网址
String familyNameWeb = "https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d&from=kg0";
String boyNameweb = "http://www.haoming8.cn/baobao/10881.html";
String girlNameweb = "http://www.haoming8.cn/baobao/7641.html";
//调用方法网络爬取网址内容并以字符串形式返回
String familyNameStr = webCrawler(familyNameWeb);
String boyNameStr = webCrawler(boyNameweb);
String girlNamewebStr = webCrawler(girlNameweb);
//利用正则表达式获取网址中的姓氏和名字
//按照所需数据在网址内容中的布局不同设置不同的正则表达式
//百家姓网址中所需的百家姓数据都是4个中文一组后面跟逗号或句号,且只获取符合前的内容
//中文的正则表达式为[\u4E00-\u9FA5]
ArrayList<String> familyNameTempList = getData(familyNameStr, "([\\u4E00-\\u9FA5]){4}(?=,|。)");
//男生名字网址中所需的名字数据都是2个中文一组后面跟顿号或句号,且只获取符合前的内容
ArrayList<String> boyNameTempList = getData(boyNameStr, "([\\u4E00-\\u9FA5]){2}(?=、|。)");
//女生名字网址中所需的名字数据都是2个中文加1个空格4组后面跟2个中文,都获取
ArrayList<String> girlNameTempList = getData(girlNamewebStr,
"(([\\u4E00-\\u9FA5]){2} ){4}[\\u4E00-\\u9FA5]{2}");
//得到了初始数据集合,将其优化方便使用
//System.out.println(familyNameTempList);
//[赵钱孙李, 周吴郑王, 冯陈褚卫, 蒋沈韩杨,......
//修改百家姓集合为:只保留前410个单姓,并且每一个姓氏占一个索引
ArrayList<String> familyNameTemp2List = new ArrayList<>();
ArrayList<String> familyNameList = new ArrayList<>();
for (String s : familyNameTempList) {
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
familyNameTemp2List.add(c + "");
}
}
//只保留前410个单姓
for (int i = 0; i < 410; i++) {
familyNameList.add(familyNameTemp2List.get(i));
}
//System.out.println(familyNameList);
//[赵, 钱, 孙, 李, 周, 吴, 郑, 王,......
//System.out.println(boyNameTempList);
//[大气, 美好, 特色, 大气, 美好, 特色, 月星, 弘城, 雨国, 思明,.....
//修改男生名字集合为:去除重复,每一个名字占一个索引
ArrayList<String> boyNameList = new ArrayList<>();
for (String s : boyNameTempList) {
if (!boyNameList.contains(s)) {
boyNameList.add(s);
}
}
//System.out.println(boyNameList);
//[大气, 美好, 特色, 月星, 弘城, 雨国, 思明, ......
//System.out.println(girlNameTempList);
//[彤舞 芊静 艾丝 惠蕙 语月, 依莹 瑶馨 曼珍 逸云 微婉,.....
//修改女生名字集合为:去除重复,每一个名字占一个索引
ArrayList<String> girlNameList = new ArrayList<>();
for (String s : girlNameTempList) {
String[] arr = s.split(" ");
for (int i = 0; i < arr.length; i++) {
girlNameList.add(arr[i]);
}
}
//System.out.println(girlNameList);
//[彤舞, 芊静, 艾丝, 惠蕙, 语月, 依莹, ......
//调用方法根据准备好的数据分别获取不重复的10个男生名字和女生名字
//参数为坐标的数据和男女要生成名字的数量
ArrayList<String> nameList = getName(familyNameList, boyNameList, girlNameList, 10, 10);
//利用缓冲字符输出流写到本模块下的a.txt文件中
BufferedWriter bw = new BufferedWriter(new FileWriter("day05\\a.txt"));
for (String s : nameList) {
bw.write(s);
bw.newLine();
}
bw.close();
}
private static ArrayList<String> getName(ArrayList<String> familyNameList, ArrayList<String> boyNameList,
ArrayList<String> girlNameList, int bCount, int gCount) {
//定义集合存储生成的不重复的男生名字
HashSet<String> boyList = new HashSet<>();
//生成男生名字
while (true) {
//存够数量跳出
if (boyList.size() == bCount) {
break;
}
//打乱集合中的内容
Collections.shuffle(familyNameList);
Collections.shuffle(boyNameList);
//将打乱后的集合的0索引位置的姓和名拼接并标注男生,添加到男生名字集合中
boyList.add(familyNameList.get(0) + boyNameList.get(0) + "-男");
}
//定义集合存储生成的不重复的女生名字
HashSet<String> girlList = new HashSet<>();
while (true) {
//存够数量跳出
if (girlList.size() == gCount) {
break;
}
//打乱集合中的内容
Collections.shuffle(familyNameList);
Collections.shuffle(girlNameList);
//将打乱后的集合的0索引位置的姓和名拼接并标注女生,添加到女生名字集合中
girlList.add(familyNameList.get(0) + girlNameList.get(0) + "-女");
}
//定义集合存储生成的名字
ArrayList<String> nameList = new ArrayList<>();
//将男女名字集合中的数据放到一个集合中,方便返回
for (String s : boyList) {
nameList.add(s);
}
for (String s : girlList) {
nameList.add(s);
}
return nameList;
}
//正则表达式
private static ArrayList<String> getData(String str, String regex) {
//定义集合存储符合正则表达式的数据
ArrayList<String> list = new ArrayList<>();
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);
while (m.find()) {
String s = m.group();
list.add(s);
}
return list;
}
//网络爬取
private static String webCrawler(String web) throws IOException {
//获取网址对象
URL url = new URL(web);
//让程序连接网址
URLConnection uc = url.openConnection();
//读取网址内的数据:
//得到得到连接网址的字节输入流
InputStream is = uc.getInputStream();
//利用转换流将字节流转换为字符流方便读中文
InputStreamReader isr = new InputStreamReader(is);
//定义StringBuilder用于拼接读到的数据
StringBuilder sb = new StringBuilder();
//开始读
int b;
while ((b = isr.read()) != -1) {
sb.append((char) b);
}
//读完关流
isr.close();
//返回读到的数据
return sb.toString();
}
}
