这是 Java 爬虫系列文章的第一篇,第一篇是关于 Java 爬虫入门内容,在该篇中我们以采集开源情报网站中的ip数据为例,需要提取的内容如下图所示:Statistics | AbuseIPDB
我们需要提取图中圈出来的内容及其对应的链接,在提取的过程中,我们会使用两种方式来提取,一种是 Jsoup 的方式,另一种是 httpclient + 正则表达式的方式,这也是 Java 网络爬虫常用的两种方式,你不了解这两种方式没关系,后面会有相应的使用手册。在正式编写提取程序之前,我先交代一下 Java 爬虫系列博文的环境,该系列博文所有的 demo 都是使用 SpringBoot 搭建的,不管你使用哪种环境,只需要正确的导入相应的包即可。
我们先来使用 Jsoup 的方式提取新闻信息,如果你还不知道 Jsoup ,请参考 jsoup: Java HTML parser, built for HTML editing, cleaning, scraping, and XSS safety
先建立一个 Springboot 项目,名字就随意啦,在 pom.xml 中引入 Jsoup 的依赖
org.jsoup jsoup 1.15.2
好了,接下来我们一起分析页面吧,在列表页中,我们利用 F12 审查元素查看页面结构,经过我们分析发现列表新闻在 标签下,每一条ip数据都是一个
li
标签,分析结果如下图所示:
由于我们前面已经知道了 css 选择器,我们结合浏览器的 Copy 功能,编写出我们 a
标签的 css 选择器代码:aside.text-center > ol > li > b > a ,一切都准备好了,我们一起来编写 Jsoup 方式提取信息的代码:
/**
* 取指定标签下的ip
* @throws Exception
*/
@Test
public void testSelect() throws Exception {
String url="https://www.abuseipdb.com/statistics";
Document doc = Jsoup.connect(url).get();
//根据标签名获取元素
Elements els = doc.select("b");
for (Element e : els) {
//System.out.println("b:"+e.text());
Elements a = e.select("a");
System.out.println("a:"+a.text());
}
/*//根据id获取元素
String text = doc.select("#city_bj").text();
//System.out.println(text);
//根据class获取元素
String text1 = doc.select(".class_a").text();
//System.out.println(text1);
//根据属性获取元素
String text2 = doc.select("[abc]").text();
//System.out.println(text2);
//根据属性值获取元素
String text3 = doc.select("[class=s_name]").text();
System.out.println(text3);*/
}
使用 Jsoup 方式提取还是非常简单的,就5、6行代码就完成了,关于更多 Jsoup 如何提取节点信息的方法可以参考 jsoup 的官网教程。我们编写 单元测试方法,来看看 方法是否正确。
执行 单元测试方法,得到如下结果:
从结果中可以看出,我们已经正确的提取到了我们想要的信息,如果你想采集详情页的信息,只需要编写一个采集详情页的方法,在方法中提取详情页相应的节点信息,然后将列表页提取的链接传入提取详情页方法即可。
上面我们使用了 Jsoup 方式正确提取了开源情报数据,接下来我们使用 httpclient + 正则表达式的方式来提取,看看使用这种方式又会涉及到哪些问题?httpclient + 正则表达式的方式涉及的知识点还是蛮多的,它涉及到了正则表达式、Java 正则表达式、httpclient。如果你还不知道这些知识,可以点击下方链接简单了解一下:
正则表达式:正则表达式
Java 正则表达式:Java 正则表达式
httpclient:httpclient
我们在 pom.xml 文件中,引入 httpclient 相关 Jar 包
org.apache.httpcomponents
httpclient
4.5.10
org.apache.httpcomponents
httpcore
4.4.10
org.apache.httpcomponents
httpmime
4.5.10
关于开源情报页面,我们在使用 Jsoup 方式的时候进行了简单的分析,这里我们就不在重复分析了。对于使用正则表达式方式提取,我们需要找到能够代表ip列表的结构体,比如:
这段结构体,每个ip数据只有链接和标题不一样,其他的都一样,而且
是ip列表特有的。最好不要直接正则匹配 a
标签,因为 a
标签在其他地方也有,这样我们就还需要做其他的处理,增加我们的难度。现在我们了解了正则结构体的选择,我们一起来看看 httpclient + 正则表达式方式提取的代码:
public static List doSpider(TipIntelligenceConf conf,String sourceType,String url){
List resultList = new ArrayList<>();
CloseableHttpResponse closeableHttpResponse = null;
try {
closeableHttpResponse = HttpAPIService.doGetAndProxy(url, null, conf);
if (closeableHttpResponse == null){
return null;
}
int statusCode = closeableHttpResponse.getStatusLine().getStatusCode();
log.info("通过代理GET请求获取结果的状态码:{}", statusCode);
if (Constants.SUCCESS_CODE == statusCode) {
//获取返回对象
HttpEntity entity = closeableHttpResponse.getEntity();
//通过EntityUtils获取返回内容
String result = EntityUtils.toString(entity, "UTF-8");
String str = JSONUtil.toJsonStr(result);
log.info("响应结果:"+str);
//爬取结果不为空,则分别对不同情报源处理ioc数据
if (StringUtils.isNotEmpty(str)){
if (Constants.OPEN_MYIPV4.equals(sourceType)){
//去重后的数据入mysql
Map> ipTypeMap = getIpMap(str);
List ipv4List = ipTypeMap.get("ipv4");
resultList = ipv4List;
}
if (Constants.OPEN_MYIPV6.equals(sourceType)){
Map> ipTypeMap = getIpMap(str);
List ipv6List = ipTypeMap.get("ipv6");
resultList = ipv6List;
}
if (Constants.OPEN_ABUSEIPDB_IP.equals(sourceType)){
List ipv4TopData = getIpv4TopData(str);
resultList = ipv4TopData;
}
if (Constants.OPEN_SEKURIPY_IP.equals(sourceType)){
List allIpv4 = getAllIpv4(str);
resultList = allIpv4;
}
if (Constants.OPEN_KRISKINTEL_DOMAIN.equals(sourceType)){
List domains = getDomain(str);
resultList = domains;
}
}
return resultList;
}else {
System.out.println("爬取开源情报,请求没成功!");
}
} catch (Exception e) {
e.printStackTrace();
log.error("爬取数据出现异常!");
}finally {
try {
if (closeableHttpResponse != null) {
closeableHttpResponse.close();
}
} catch (Exception e) {
e.printStackTrace();
log.error("————closeableHttpResponse close error!————");
}
}
return null;
}
上述代码隐藏了正则表达式和数据处理过程,从代码的行数可以看出,比 Jsoup 方式要多不少,代码虽然多,但是整体来说比较简单,在上面方法中我做了一段特殊处理,我先替换了 httpclient 获取的字符串 body 中的换行符、制表符、回车符,因为这样处理,在编写正则表达式的时候能够减少一些额外的干扰。
执行 单元测试方法,得到如下结果:
使用 httpclient + 正则表达式的方式同样正确的获取到了列表新闻的标题和详情页链接。到此 Java 爬虫系列博文第一篇就写完了,这一篇主要是 Java 爬虫的入门,我们使用了 jsoup 和 httpclient + 正则的方式提取了开源情报网站的ip和链接。当然这里还有很多没有完成,比如采集ip信息存入数据库等。
希望以上内容对你有所帮助,下一篇是模拟登陆相关的,如果你对 Java 爬虫感兴趣,不妨关注一波,一起学习,一起进步。