都说python比java写爬虫更容易,我特意用python写了爬虫,对比java写爬虫,确实python更容易,主要是完善的支持库和语法特性占了优势。在GitHub上java爬虫有几个项目还是很不错的,有兴趣的可以去看看。
一、列举爬虫
爬虫主要是依赖于基础的tcp协议或者高级http协议,根据不同的工作场景分为2种:
1、数据爬虫
爬取方式:主要是寻找服务端接口地址进行模拟请求获得数据,主要适用于请求回来是一串数据而不是一个网页的场景。基本要素是伪装用户骗取服务器信任得到数据。
防范方式:
a、服务端进行严格的授权策略。具体做法是生成短暂过期token,通常用于cookie中。
b、服务端进行频繁业务请求记录。比如1秒内请求了10几次,服务器会将此ip锁住一段时间拒绝提供任何服务。
c、请求头检测。服务端对请求头进行检测,鉴定是否为爬虫,这种方式检测成爬虫的几率太小,爬虫程序稍微伪装一下即可。
实现方案:
推荐使用vertx,详情查阅vertx文档。
2、页面爬虫
爬取方式:等待页面与js渲染完成后根据返回的网页xml节点进行解析,主要适用于请求回来是一个网页而不是一串数据的场景。基本要素是根据网页节点做数据解析,得到页面上需要的数据。
防范方式:
a、使用自动生成ui的前端框架,增加页面xml的复杂度,提高解析的难度。
实现方案:
htmlunit
在此我只想介绍 页面爬虫的实现htmlunit。数据爬虫的实现太多了
二、介绍htmlunit
htmlunit是一个拿来做html测试的东西,它可以模拟一个浏览器,在页面上的所有行为完全可以用代码控制。
官网 http://htmlunit.sourceforge.net/
三、实现一个页面爬虫
1、maven支持
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.34.1</version>
</dependency>
2、抓取页面
/访问的目标网址
private static String TARGET_URL = "https://www.naifen123.cn/item-2266.html";
public static void main(String[] args) throws FailingHttpStatusCodeException, IOException {
// 模拟一个浏览器
WebClient webClient = new WebClient(BrowserVersion.CHROME);
// 设置webClient的相关参数
webClient.setCssErrorHandler(new SilentCssErrorHandler());
//设置ajax
webClient.setAjaxController(new NicelyResynchronizingAjaxController());
//设置支持js
webClient.getOptions().setJavaScriptEnabled(false);
//CSS渲染禁止
webClient.getOptions().setCssEnabled(false);
//超时时间
webClient.getOptions().setTimeout(50000);
//设置js抛出异常:false
webClient.getOptions().setThrowExceptionOnScriptError(false);
//允许重定向
webClient.getOptions().setRedirectEnabled(true);
//允许cookie
webClient.getCookieManager().setCookiesEnabled(true);
// 模拟浏览器打开一个目标网址
HtmlPage page = webClient.getPage(TARGET_URL);
// 等待JS驱动dom完成获得还原后的网页
webClient.waitForBackgroundJavaScript(10000 * 3);
//业务
service(page);
webClient.close();
System.out.println("Success!");
}
private static void service(HtmlPage page) {
System.out.println("******** 业务开始 ***********************************");
//标题头
HtmlHeading1 topName = (HtmlHeading1) page.getByXPath("//h1[@class='q_left aqnfxq1_tl_lf']").get(0);
//配方含量
//中国标准-主要配方
HtmlDivision formulaDiv1 = (HtmlDivision) page.getByXPath("//div[@id='bzdiv_3']/div[@class='nf123_yynl clearfix']").get(0);
//主要配方
Map<DomText, List<DomNode>> formulaMap1 = new HashMap<>();
List<DomNode> formula = new ArrayList<>(formulaDiv1.getChildNodes());
DomText key = (DomText) formula.get(0);
formula.remove(0);
formulaMap1.put(key, formula);
//中国标准-主要配方详情表
List<HtmlTable> formulaTables = page.getByXPath("//div[@id='bzdiv_3']/div[@class='nf123_xxyypf clearfix']/table");
List<List<String>> formulaSet = new ArrayList<>();
formulaTables.forEach(t -> {
t.getRows().forEach(r -> {
if (r.asText().trim().length() > 0) {
List<String> com = new ArrayList<>();
for (int i = 0; i < 3; i++) {
com.add(r.getCells().get(i).asText());
}
formulaSet.add(com);
}
});
});
//原料分析
//原料注意
List<HtmlDivision> materialDiv1 = page.getByXPath("//div[@class='aqnfxqb_lf_b']/div[@class='aqnfxq3_1_2 aqnfxq5_2']");
Map<String, String> material1 = new HashMap<>();
if (materialDiv1 != null && !materialDiv1.isEmpty()) {
HtmlDivision materialDiv = materialDiv1.get(0);
materialDiv.getChildNodes().forEach(n -> {
if (n.asText().trim().length() > 0) {
material1.put(n.getChildNodes().get(0).asText(), n.getChildNodes().get(1).asText());
}
});
}
List<HtmlDivision> materialDiv2 = page.getByXPath("//div[@class='aqnfxqb_lf_b']/div[@class='aqnfxq5_4']");
List<String> material2 = new ArrayList<>();
if (materialDiv2 != null && !materialDiv2.isEmpty()) {
HtmlDivision materialDiv = materialDiv2.get(0);
String str = materialDiv.asText().trim();
if (str.length() > 0) {
str = str.replaceAll("。", "");
String[] com = str.split(",");
System.out.println("------" + com.toString());
material2.addAll(Arrays.stream(com).collect(Collectors.toList()));
}
}
System.out.println(topName.asText());
System.out.println("原料注意:");
material1.entrySet().forEach(m -> {
System.out.println(m.getKey() + " : " + m.getValue());
});
System.out.println("原料详情:");
material2.forEach(m -> {
System.out.println(m);
});
}
我这里只演示了抓取页面上的数据,htmlunit还提供了很多功能,比如模拟点击,悬停,选择、表单获取,登录,操作cookie、增加html标签元素等等,可以理解成 htmlunit 就是一个程序中内嵌的浏览器,可以做到浏览器的绝大多数功能。
![](https://i-blog.csdnimg.cn/blog_migrate/99947f7b93de19315e9df8499028894e.jpeg)