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"等 |