java爬虫

it2022-05-05  171

都说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 就是一个程序中内嵌的浏览器,可以做到浏览器的绝大多数功能。

欢迎关注我的个人公众号

   


最新回复(0)