案例要求
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()方法记录的索引进行字符串的截取,返回截取的小串,该小串就是符合正则表达式要求的子串 |
代码实现
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();}
}
