2.5 集合
List和Set集合

List集合的特征是其他元素以线性方式存储,集合中可以存放重复对象。相对而言,Set集合中的元素不按特定的方式排序,并且没有重复对象。在网络爬虫中,可以使用List<String>存储待采集的URL列表

//List集合创建
        List<String> urllist = new ArrayList<String>();
        //集合元素的添加
        urllist.add("https://movie..******../com/subject/2546131");
        urllist.add("https://movie..******../com/subject/2564562");
        //第一种方式遍历集合
        for (String url:urllist){
            System.out.println(url);
        }
        //第二种方式遍历集合
        for (int i=0;i<urllist.size();i++){
            System.out.println(i+":"+urllist.get(i));
        }
        //第三种方式遍历集合
        Iterator<String> it = urllist.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }

 Set集合存储不重复的url

//set集合的初始化
        Set<String> set = new HashSet<String>();
        set.add("https://movie..*****../com/subject/156131223");
        set.add("https://movie..*****../com/subject/156131223");
        set.add("https://movie..*****../com/subject/212634564");
        //Set集合遍历
        Iterator<String> setIt = set.iterator();
        while (setIt.hasNext()){
            System.out.println(setIt.next());
        }

打印结果:
https://movie..*****../com/subject/156131223 https://movie..*****../com/subject/212634564

可以看出Set集合自动过滤了重复的元素

 Map集合

Map对象是把键对象和值对象进行映射的集合,它的每个元素都包含一个键对象key和值对象value。其中,键对象不可以重复。Map集合不仅在网络爬虫中常用,也在文本挖掘算法编写中使用。在网络爬虫中,可以使用Map集合过滤一些重复数据,但并不建议使用Map集合对大规模数据去重,原因是Map集合有空间大小的限制。例如使用网路爬虫采集帖子时,可能遇到置顶帖,而置顶帖在其他页面中也会出现,使用Map集合可以过滤已采集的置顶帖。

//Map集合的初始化
        Map<String,Integer> map = new HashMap<String,Integer>();
        //值的添加,这里假设是爬虫中的产品id及每个产品id对应的销售量
        map.put("jd1515",100);
        map.put("jd1516",300);
        map.put("jd1515",100);
        map.put("jd1517",200);
        map.put("jd1518",100);
        //第一中方法遍历map集合
        for (String key:map.keySet()){
            Integer value = map.get(key);
            System.out.println("key="+key+";"+"value="+value);
        }
        //第二种方法遍历map集合
        Iterator<Map.Entry<String,Integer>> entries = map.entrySet().iterator();
        while (entries.hasNext()){
            Map.Entry<String, Integer> entry = entries.next();
            System.out.println("key="+entry.getKey()+",value="+entry.getValue());
        }
        //第三种方法遍历map集合
        for (Map.Entry<String,Integer> entry:map.entrySet()){
            System.out.println("key="+entry.getKey()+",value="+entry.getValue());
        }

Queue集合

Queue(队列)集合使用链表结构存储数据,它只允许在表的前端进行删减操作,而在表的后端进行插入操作,即表的两端操作。Queue集合常见的操作方法包括添加元素,移除头元素和判断队列是否为空等。

       

抛出异常

返回特殊值

添加元素       

add(e)

offer(e)

移除元素

remove()

poll()

获取对头元素但不移除对头元素

element()

peek()

 在网络爬虫中,Queue集合常用来存放待采集的url

/*
        队列常用操作,add()方法和remove()方法在失败的额时候会抛出异常(不推荐)
         */
        Queue<String> urlQueue = new LinkedList<>();
        //添加元素
        urlQueue.offer("https://movie..*****../com/subject/123412");
        urlQueue.offer("https://movie..*****../com/subject/123414");
        urlQueue.offer("https://movie..*****../com/subject/123415");
        //遍历队列的元素
        for (String url:urlQueue){ //遍历队列的元素
            System.out.println(url);
        }
        //返回对头元素,并在队列中删除
        System.out.println("第一个url为:"+urlQueue.poll());
        System.out.println(urlQueue);
        System.out.println("=====================================================");
        //获取队头元素但不移除队头元素
        System.out.println("第一个元素为:"+urlQueue.element());
        System.out.println("第一个元素为:"+urlQueue.peek());
        System.out.println(urlQueue);
        System.out.println("=====================================================");
        //判断队列是否为空
        if (urlQueue.isEmpty()){
            System.out.println("队列为空");
        }else {
            System.out.println("队列不为空,还有"+urlQueue.size()+"个元素");
        }

2.6对象和类

Java是一门面向对象的语言,核心思想是将数据与对数据的操作封装在一起,这里涉及两个重要的概念。对象和类。类封装了一类对象的状态和操作方法。对象是类的具体实例

@Data
public class CrawlerKeyword {
    private Integer id;
    private String keyword;
    private String states;
}

在CrawlerKeyword类中,包含了对变量的操作方法,set()和get()。我上面的代码中并没有set和get方法,我使用的是@Data注解,@Data注解中包含了set()和get()。set()为对象赋值,get()获取对象的值.

//创建对象1
        CrawlerKeyword crawlerKeyword1 = new CrawlerKeyword();
        crawlerKeyword1.setKeyword("身份证");
        crawlerKeyword1.setStates("未采集");
        crawlerKeyword1.setId(1001);
        //创建对象2
        CrawlerKeyword crawlerKeyword2 = new CrawlerKeyword();
        crawlerKeyword2.setKeyword("户口本");
        crawlerKeyword2.setStates("未采集");
        crawlerKeyword2.setId(1002);
        List<CrawlerKeyword> list = new ArrayList<>();
        list.add(crawlerKeyword1);
        list.add(crawlerKeyword2);
        for (CrawlerKeyword crawlerKeyword:list){
            String keyword = crawlerKeyword.getKeyword();
            Integer id = crawlerKeyword.getId();
            String states = crawlerKeyword.getStates();
            System.out.println("id为:"+id+";关键词为:"+keyword+";状态为:"+states);
        }

打印结果为:
id为:1001;关键词为:身份证;状态为:未采集
id为:1002;关键词为:户口本;状态为:未采集

2.7String类

Java使用java.lang包中的String类来创建字符串变量。另外,String类提供了字符串的一系列方法

方法

返回值类型

描述

length()

int

获取字符串长度

equals()

boolean

判断两个字符串是否相同

concat()

String

连接两个字符串

contains()

boolean

判断当前字符串是否包含字符串s

substring()

String

从beginIndex处截取最后所得的字符串

substring()

String

从beginIndex处到endIndex处截取所得到的的是字符串

indexof()

int       

从字符串头位置开始检索字符串s,返回首次出现s的位置,如果没有检索到返回-1        

starts With

boolean

判断你字符串的前缀是否位prefix

starts With

boolean

判断字符串从指定索引开始的子字符串前缀是否为prefix,没有检索到返回-1

ends With

boolean

判断字符串是否以制定后缀结束

trim()

String

去除字符串首尾空格

toLowerCase()

String

将字符串中的所有字符都转换为小写

toUpperCase()

String

将字符串中所有的字符串都转换为大写

String url = " https://www.****.com/ ";
        String urlTrim = url.trim();//去除字符串中的首尾空格
        System.out.println(urlTrim);
        //获取字符串长度
        System.out.println("字符串长度为:"+urlTrim.length());
        //转化为大写
        System.out.println(urlTrim.toUpperCase(Locale.ROOT));
        //判断字符串是否相同
        System.out.println(urlTrim.equals("www"));
        //判断字符串是否包含
        System.out.println(urlTrim.contains("www"));
        //也可以采用+的形式
        String urlConcat = urlTrim.toLowerCase().concat("crawler");
        System.out.println("urlConcat:"+urlConcat);
        //从第二个字符串截取到最后
        String urlSubString = urlConcat.substring(1,urlConcat.length());
        System.out.println(urlSubString);
        //寻找某个字符的位置
        int index = urlSubString.indexOf("a");
        System.out.println("字符a第一次出现的位置:"+index);
        //是否以某个字符为前缀
        boolean urlStartWith = urlConcat.startsWith("https");
        System.out.println(urlStartWith);
        //是否以某个字符为后缀
        boolean urlEndWith = urlConcat.endsWith("crawler");
        System.out.println(urlEndWith);

 另外,调用java.lang包的其他类中方法,可以实现String数据类型和其他数据类型的转换。

 2.8日期和时间的处理

java.util包提供的处理日期和时间的类有SimpleDateFormat,DateFormat,Date以及Calendar,其中SimpleDateFormat和DateFormat类用来实现日期和时间的格式化,常使用的方法有format(Date date)和parse(String source);Date类主要处理指定格式的时间;Calendar类主要用于年月日类型数据的转换

public static void main(String[] args) {
        System.out.println(parseStringTime("2022-10-01 12:23","yyyy-MM-dd HH:mm","yyyy-MM-dd" +
                " HH:mm:ss"));
        System.out.println(parseStringTime("2018-06-19","yyyy-MM-dd","yyyy-MM-dd HH:mm:ss"));
    }

    public static String parseStringTime(String inputTime,String inputTimeFormat,String outTimeFormat){
        String outputDate = null;
        try{
            //日期格式及解析时间
            Date inputDate = new SimpleDateFormat(inputTimeFormat).parse(inputTime);
            //转化成新的形式的字符串
            outputDate = new SimpleDateFormat(outTimeFormat).format(inputDate);
        }catch (Exception e){
            e.printStackTrace();
        }
        return outputDate;
    }

一些网站的时间格式为UNIX时间戳,为了将UNIX时间戳转化为指定格式的时间,需要构造一个通用的方法

public static void main(String[] args) {
        //获取时间戳,除以1000.因为获取到的为毫秒
        String dateStr = Long.toString(System.currentTimeMillis()/1000L);
        //时间戳
        System.out.println(dateStr);
        String newdate = TimeStampToDate(dateStr,"yyyy-MM-dd HH:mm:ss");
        //打印转换后的时间
        System.out.println(newdate);
    }
    //将UNIX时间戳转化成指定格式的时间
    public static String TimeStampToDate(String timestampString,String formats){
        Long timestamp = Long.parseLong(timestampString)*1000;
        String date = new SimpleDateFormat(formats, Locale.CHINA).format(new Date(timestamp));
        return date;
    }

2.9正则表达式

正则表达式指由普通字符(如英文字母/数字等)一级特殊字符(如字符\D)组成的字符串模式。正则表达式可以理解成一套模板,以这套模板可以匹配的字符串。例如从下面HTML片段中提取用户id

<a href ="//i.*****.com.cn/754964" target="_blank" class="blinkbalck">尊少来自沈阳

                                                    常见元字符及其含义

元字符

郑泽斌表达式写法

含义

\d

\\d

代表0-9中的任意数字

\D

\\D

代表任何一个非数字字符

\S

\\S

代表非空格类字符

\s

\\s

代表空格类字符

\p{Lower}

\\p{Lower}

代表小写英文字母

\p{Upper}

\\p{Upper}

代表大写英文字母

\p{Punct}

\\p{Punct}

代表标点符号

\p{Blank}

\\p{Blank}

代表空格或制表符(\t)

在正则表达式中,常用方括号起若干字符表示一个元字符,该元字符匹配的是括号内任意一个字符。例如String regex ="[abc]123"匹配的是"a123","b123","c123"。如下表列举了一些包含方括号的元字符

                                                      包含方括号的元字符

写法

含义

[abc]

a或b或c

[^abc]

除去abc的任何字符

[a-z]

表示a~z的任意一个字符

[a-zA-Z]

所有的英文字母(从小写a到z,从A-Z)

[1-9]

表示1-9中的任何一个数字

[a-d1-3]

字母a-d和数字1-3

public static void main(String[] args) {
      String str1 ="a1b2c3dAZ4";
        String strReplace1 = str1.replaceAll("[abc]", "");
        System.out.println("使用元字符[abc]匹配的结果:"+strReplace1);
        String strReplace2 = str1.replaceAll("[^abc]", "");
        System.out.println("使用元字符[^abc]匹配的结果:"+strReplace2);
        String strReplace3 = str1.replaceAll("[a-zA-Z]", "");
        System.out.println("使用元字符[a-zA-Z]匹配的结果:"+strReplace3);
        String strReplace4 = str1.replaceAll("[1-9]", "");
        System.out.println("使用元字符[1-9]匹配的结果:"+strReplace4);
        String strReplace5 = str1.replaceAll("[a-d1-3]", "");
        System.out.println("使用元字符[a-d1-3]匹配的结果:"+strReplace5);
    }

运行结果:

使用元字符[abc]匹配的结果:123dAZ4
使用元字符[^abc]匹配的结果:abc
使用元字符[a-zA-Z]匹配的结果:1234
使用元字符[1-9]匹配的结果:abcdAZ
使用元字符[a-d1-3]匹配的结果:AZ4

为了适应匹配的不确定性,正则表达式支持限定符的概念。限定符定义了某些元素可以出现的频次。例如,X{n,m}表示X出现n到m次的字符都可以匹配到。使用"ac{1,3}" 可以匹配"ac","acc"和"accc"。下表列举了常用的限定符

写法

含义

X{n}

X确定出线n次,如"a{2}"不能匹配"cab"中的"a",但能匹配"caab"中的两个"a".

X{n,}

X至少出现n次,如"a{2,}"不能匹配"cab"中的"a",但能匹配"caaab"中的3个"a"

X{n,m}

X出现n到m次,如"ac{1,3}"可以匹配"ac","acc","accc"

X?

X出现0次或1次,如"ac[12]?"能匹配"ac","ac1","ac2"

X*

X出现0次或多次,如"ac*"能匹配"a","ac"和"acc"等

X+

表示X出现1次或多次,如"ac+"能匹配到"ac","acc"和"accc"等