Java—— 网络爬虫

案例要求

复制代码
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();
    }
}
相关推荐
草莓熊Lotso5 分钟前
【自定义类型-结构体】--结构体类型,结构体变量的创建和初始化,结构体内存对齐,结构体传参,结构体实现位段
c语言·开发语言·经验分享·笔记·其他
旋风菠萝14 分钟前
八股--SSM(2)
java·开发语言·数据库·八股·八股文·复习
攻心的子乐23 分钟前
Flyweight(享元)设计模式 软考 享元 和 代理属于结构型设计模式
java·开发语言
设计师小聂!31 分钟前
Seata分布式事物案例及详解
java·spring·spring cloud
编程乐学(Arfan开发工程师)32 分钟前
16、最佳实践-SpringBoot应用如何编写
java·spring boot·后端
取个好名称1 小时前
适合初学者的 Blender 第二部分
java·前端·blender
君的名字1 小时前
怎么判断一个Android APP使用了Qt 这个跨端框架
android·开发语言·qt
xuanjiong1 小时前
苍穹外卖day1实战,Idea中Lombok编译时“找不到符号”,更改JDK版本最全流程,作者亲身尝试
java·ide·intellij-idea
Clown951 小时前
Go语言爬虫系列教程(三)HTML解析技术
爬虫·go·goquery
不秃的开发媛1 小时前
JFace中MVC的表的单元格编辑功能的实现
java·开发语言·mvc