学习用java基于webMagic+selenium+phantomjs实现爬虫Demo爬取淘宝搜索页面

本文主要是介绍学习用java基于webMagic+selenium+phantomjs实现爬虫Demo爬取淘宝搜索页面,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

学习用java基于webMagic+selenium+phantomjs实现爬虫Demo爬取淘宝搜索页面

  由于业务需要,老大要我研究一下爬虫。

  团队的技术栈以java为主,并且我的主语言是Java,研究时间不到一周。基于以上原因固放弃python,选择java为语言来进行开发。等之后有时间再尝试python来实现一个。

       本次爬虫选用了webMagic+selenium+phantomjs,选用他们的原因如下:

  1.  webMagic(v:0.73),一个轻量级的Java爬虫框架(git地址:https://github.com/code4craft/webmagic,主页地址:http://webmagic.io/):

    • 时间和个人水平关系,放弃重复造轮子实现一套,固上网找一个框架(技术不好限制了我的风骚想法)
    • webMagic是一款国人实现的轻量级爬虫框架,注释肯定是中文(英文不好限制了我的选择范围)
    • 文档不少,而且用的人多,有问题网上好找解答
  2. selenium,一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样,配合chromeDriver就可以通过代码对谷歌浏览器为所欲为了,阶段测试时候需要驱动谷歌浏览器的chromeDriver(附上下载地址:http://npm.taobao.org/mirrors/chromedriver/)

    • 因为本次爬取目标是淘宝网,淘宝使用的Js渲染加大了爬取的难度,如果根据接口去爬取,需要搞清楚各种接口参数在哪里,非常麻烦,而且可能某些参数实在找不到,所以选用selenium直接用浏览器渲染,降低了找不到参数的引起尴尬场面的风险
    • 但是用浏览器爬取必然会大大降低爬取效率,如果简单网站还是不建议使用这种方式
  3. phantomjs,一个基于webkit内核的无头浏览器,即没有UI界面,即它就是一个浏览器,只是其内的点击、翻页等人为相关操作需要程序设计实现。(附上下载地址:http://phantomjs.org/):

    • 如果每次爬取都要开启一个chrome浏览器,那简直就是内存噩梦,所以采用phantomjs来充当浏览器

   说完框架的选择, 我们先贴上设计图:

 

 然后我们再贴上使用的jar包:

复制代码
 1      <dependency>
 2             <groupId>org.seleniumhq.selenium</groupId>
 3             <artifactId>selenium-java</artifactId>
 4             <version>3.0.1</version>
 5         </dependency>
 6         <dependency>
 7             <groupId>org.seleniumhq.selenium</groupId>
 8             <artifactId>selenium-chrome-driver</artifactId>
 9             <version>3.0.1</version>
10         </dependency>
11 
12         <dependency>
13             <groupId>org.seleniumhq.selenium</groupId>
14             <artifactId>selenium-remote-driver</artifactId>
15             <version>3.0.1</version>
16         </dependency>
17 
19         <dependency>
20             <groupId>com.codeborne</groupId>
21             <artifactId>phantomjsdriver</artifactId>
22             <version>1.2.1</version>
23         </dependency>
24 
25         <dependency>
26             <groupId>org.apache.commons</groupId>
27             <artifactId>commons-exec</artifactId>
28             <version>1.3</version>
29         </dependency>
复制代码

  1、先让我们用chromeDriver测试一波selenium:

复制代码
package com.chinaredstar.jc.crawler.biz.test.chrome;import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;import java.io.File;
import java.io.IOException;/*** chromeDriver是谷歌的浏览器驱动,用来适配Selenium,有图形页面存在,在调试爬虫下载运行的功能的时候会相对方便* @author zhuangj* @date 2017/11/14*/
public class TestChromeDriver {private static ChromeDriverService service;public static WebDriver getChromeDriver() throws IOException {System.setProperty("webdriver.chrome.driver","C:/Users/sunlc/AppData/Local/Google/Chrome/Application/chrome.exe");// 创建一个 ChromeDriver 的接口,用于连接 Chrome(chromedriver.exe 的路径可以任意放置,只要在newFile()的时候写入你放的路径即可)service = new ChromeDriverService.Builder().usingDriverExecutable(new File("D:\\chromedriver\\qd-chromedriver_pc18\\qd-chromedriver_pc18\\chromedriver.exe")) .usingAnyFreePort().build();
        service.start();// 创建一个 Chrome 的浏览器实例return new RemoteWebDriver(service.getUrl(), DesiredCapabilities.chrome());}public static void main(String[] args) throws IOException {WebDriver driver = TestChromeDriver.getChromeDriver();// 让浏览器访问 Baidudriver.get("https://www.taobao.com/");// 用下面代码也可以实现//driver.navigate().to("http://www.baidu.com");// 获取 网页的 titleSystem.out.println(" Page title is: " +driver.getTitle());// 通过 id 找到 input 的 DOMWebElement element =driver.findElement(By.id("q"));// 输入关键字element.sendKeys("东鹏瓷砖");// 提交 input 所在的 form
        element.submit();// 通过判断 title 内容等待搜索页面加载完毕,间隔秒new WebDriverWait(driver, 10).until(new ExpectedCondition() {@Overridepublic Object apply(Object input) {return ((WebDriver)input).getTitle().toLowerCase().startsWith("东鹏瓷砖");}});// 显示搜索结果页面的 titleSystem.out.println(" Page title is: " +driver.getTitle());// 关闭浏览器
        driver.quit();// 关闭 ChromeDriver 接口
        service.stop();}}  
复制代码

看结果:

经过上网查询,测试是chrome驱动器与我本地的chrome浏览器版本对不上,chromDriver和浏览器的对应版本如下:

 

chromedriver版本 支持的Chrome版本
v2.33 v60-62
v2.32 v59-61
v2.31 v58-60
v2.30 v58-60
v2.29 v56-58
v2.28 v55-57
v2.27 v54-56
v2.26 v53-55
v2.25 v53-55
v2.24 v52-54
v2.23 v51-53
v2.22 v49-52
v2.21 v46-50
v2.20 v43-48
v2.19 v43-47
v2.18 v43-46
v2.17 v42-43
v2.13 v42-45
v2.15 v40-43
v2.14 v39-42
v2.13 v38-41
v2.12 v36-40
v2.11 v36-40
v2.10 v33-36
v2.9 v31-34
v2.8 v30-33
v2.7 v30-33
v2.6 v29-32
v2.5 v29-32
v2.4 v29-32

重新下载chromeDriver后:浏览器按照预想开启查询关闭:ide控制台打印如下:

  2、然后我们来测试一波phantomJs:

  (其实中间有一段测试失败,但是忘了是什么原因了,就不去重现了)

复制代码
package com.chinaredstar.jc.crawler.biz.test.phantomjs;import org.openqa.selenium.WebDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;/*** PhantomJs是一个基于webkit内核的无头浏览器,即没有UI界面,即它就是一个浏览器,只是其内的点击、翻页等人为相关操作需要程序设计实现;* 因为爬虫如果每次爬取都调用一次谷歌浏览器来实现操作,在性能上会有一定影响,而且连续开启十几个浏览器简直是内存噩梦,* 因此选用phantomJs来替换chromeDriver* PhantomJs在本地开发时候还好,如果要部署到服务器,就必须下载linux版本的PhantomJs,相比window操作繁琐* @author zhuangj* @date 2017/11/14*/
public class TestPhantomJsDriver {public static PhantomJSDriver getPhantomJSDriver(){//设置必要参数DesiredCapabilities dcaps = new DesiredCapabilities();//ssl证书支持dcaps.setCapability("acceptSslCerts", true);//截屏支持dcaps.setCapability("takesScreenshot", false);//css搜索支持dcaps.setCapability("cssSelectorsEnabled", true);//js支持dcaps.setJavascriptEnabled(true);//驱动支持dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,"D:\\chromedriver\\phantomjs-2.1.1-windows\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe");PhantomJSDriver driver = new PhantomJSDriver(dcaps);return  driver;}public static void main(String[] args) {WebDriver driver=getPhantomJSDriver();driver.get("http://www.baidu.com");System.out.println(driver.getCurrentUrl());}
}
复制代码

看结果:

3、我们用WebMagic把他们整合:

controller:

复制代码
 @RequestMapping("/testTaoBaoSearch")public RestResultVo testTaoBaoSearch(String keyWord){Spider spider=Spider.create(new TestTaoBaoPageProcessor(keyWord));HttpClientDownloader httpClientDownloader = new HttpClientDownloader();//免费代理不稳定老挂
//        httpClientDownloader.setProxyProvider(SimpleProxyProvider.from(new Proxy("0.0.0.0",0000)));
        spider.setDownloader(httpClientDownloader);spider.addUrl("https://s.taobao.com/search?q="+keyWord).thread(1).run();return null;}
复制代码

 

 TestTaoBaoPageProcessor:

复制代码
package com.chinaredstar.jc.crawler.biz.test.taobao;import com.chinaredstar.jc.crawler.biz.test.chrome.TestChromeDriver;
import com.chinaredstar.jc.crawler.biz.test.phantomjs.TestPhantomJsDriver;
import org.apache.commons.lang.StringUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.selector.Html;
import us.codecraft.webmagic.selector.Selectable;import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;/**** @author zhuangj* @date 2017/11/13*/
public class TestTaoBaoPageProcessor implements PageProcessor {private String keyWord;private static final String TAO_BAO_DETAIL_URL_START="https://item.taobao.com/item.htm";private static final String TIAN_MAO_DETAIL_URL_START="https://detail.tmall.com/item.htm";private Site site = Site.me().setCharset("UTF-8").setCycleRetryTimes(3).setSleepTime(3 * 1000).addHeader("Connection", "keep-alive").addHeader("Cache-Control", "max-age=0").addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0");public String getKeyWord() {return keyWord;}public void setKeyWord(String keyWord) {this.keyWord = keyWord;}public TestTaoBaoPageProcessor() {}public TestTaoBaoPageProcessor(String keyWord) {this.keyWord = keyWord;}@Overridepublic Site getSite() {return site;}@Overridepublic void process(Page page) {WebDriver driver= TestPhantomJsDriver.getPhantomJSDriver();
//        WebDriver driver= null;
//        try {
//            driver = TestChromeDriver.getChromeDriver();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
        driver.get(page.getRequest().getUrl());WebElement webElement = driver.findElement(By.id("page"));String str = webElement.getAttribute("outerHTML");Html html = new Html(str);if(isFirstPage(html)){analysisPagination(page,html);}else if(isListPage(html)){analysisListPage(page,html,driver);}else {analysisDetailPage(page,html,driver);}}private void analysisListPage(Page page, Html html, WebDriver driver) {List<String> detailPageList= html.xpath("//*[@id=\"mainsrp-itemlist\"]").$("a[id^='J_Itemlist_TLink_']").xpath("//a/@href").all();page.addTargetRequests(detailPageList);}/*** 分析分页规则* @param page* @param html*/private void analysisPagination(Page page,Html html){List<String> pageList= html.xpath("//*[@id=\"mainsrp-pager\"]/div/div/div/ul/li/a/@data-value").all();pageList = new ArrayList(new HashSet(pageList));List<String> pageParameterList=new ArrayList<>();for(String  value:pageList){pageParameterList.add("https://s.taobao.com/search?q="+getKeyWord()+"&s="+value);}page.addTargetRequests(pageParameterList);}/*** 分析详情页* @param page* @param html* @param driver*/private void analysisDetailPage(Page page,Html html,WebDriver driver){String url=page.getUrl().toString();if(url.startsWith(TAO_BAO_DETAIL_URL_START)){analysisTaoBaoDetailPage(page,html,driver);}else if(url.startsWith(TIAN_MAO_DETAIL_URL_START)){analysisTianMaoDetailPage(page,html,driver);}}/*** 分析淘宝详情页* @param page* @param html* @param driver*/private void analysisTaoBaoDetailPage(Page page,Html html,WebDriver driver){page.putField("price", html.xpath("//[@id=\"J_StrPrice\"]/em[2]/text()").toString());page.putField("shopName", html.xpath("//*[@id=\"J_ShopInfo\"]/div/div[1]/div[1]/dl/dd/strong/a/text()").toString());page.putField("title", html.xpath("*[@id=\"J_Title\"]/h3/text()").toString());}/*** 分析天猫详情页* @param page* @param html* @param driver*/private void analysisTianMaoDetailPage(Page page,Html html,WebDriver driver){page.putField("price", html.xpath("//[@id=\"J_StrPriceModBox\"]/dd/span/text()").toString());page.putField("shopName", driver.findElement(By.name("seller_nickname")).getAttribute("value"));page.putField("name", html.xpath("//[@id=\"J_DetailMeta\"]/div[1]/div[1]/div/div[1]/h1/text()").toString());}/*** 是否为列表页* @param html* @return*/private boolean isListPage(Html html) {String tmp = html.$("#mainsrp-pager").get();if (StringUtils.isNotBlank(tmp)) {return true;}return false;}/*** 列表页获取当前页码* @param html* @return*/private String getCurrentPageNo(Html html){return html.xpath("//*[@id=\"mainsrp-pager\"]/div/div/div/ul/li[contains(@class,'active')]/span/text()").toString();}/*** 判断是否列表页的第一页* @param html* @return*/private Boolean isFirstPage(Html html){return isListPage(html)&&getCurrentPageNo(html).equals("1");}}
复制代码

 数据保存到数据库:

 

这里有几点:

    • WebMagic的抽取主要用到了Jsoup和webMagic作者开发的工具Xsoup,并不懂这个语法还自己学了下。xpath如果自己抓取绝对累死,可以用chrome浏览器自带功能
    • 使用chrome抓取时候数据正常,改为phantomJs时候数据错乱,一脸懵逼。为了查找原因,就到追溯爬虫爬取过程,修改了webMagic的Request类,给他添加parentUrl属性。

    • 复制代码
      package com.chinaredstar.jc.crawler.biz.test.extend;import us.codecraft.webmagic.Request;/**** @author zhuangj* @date 2017/11/15*/
      public class RequestExtend extends Request {private String parentUrl;public RequestExtend(String url,String parentUrl) {super(url);this.parentUrl = parentUrl;}public String getParentUrl() {return parentUrl;}public void setParentUrl(String parentUrl) {this.parentUrl = parentUrl;}
      }    
      复制代码
    •  分析方法的时候写上父级的URl,方便如果有数据问题进行追溯。
    • 复制代码
      private void analysisListPage(Page page, Html html, WebDriver driver) {RequestExtend requestExtend= (RequestExtend) page.getRequest();System.out.println("parentUrl:"+requestExtend.getParentUrl());System.out.println("nowUrl:"+page.getUrl());List<String> detailPageList= html.xpath("//*[@id=\"mainsrp-itemlist\"]").$("a[id^='J_Itemlist_TLink_']").xpath("//a/@href").all();for(String deetailPage:detailPageList){RequestExtend extend=new RequestExtend("https:"+deetailPage,page.getUrl().toString());page.addTargetRequest(extend);}}
      复制代码
    • 详情页获取父节点也可以完成。
    • 复制代码
      /*** 分析淘宝详情页* @param page* @param html* @param driver*/private void analysisTaoBaoDetailPage(Page page,Html html,WebDriver driver){RequestExtend requestExtend= (RequestExtend) page.getRequest();System.out.println("parentUrl:"+requestExtend.getParentUrl());System.out.println("nowUrl:"+page.getUrl());page.putField("price", html.xpath("//[@id=\"J_StrPrice\"]/em[2]/text()").toString());String shopName=html.xpath("//*[@id=\"J_ShopInfo\"]/div/div[1]/div[1]/dl/dd/strong/a/text()").toString();if(StringUtils.isBlank(shopName)){shopName=html.xpath("//*[@id=\"header-content\"]/div[2]/div[1]/div[1]/a/text()").toString();}page.putField("shopName", shopName);page.putField("title", html.xpath("*[@id=\"J_Title\"]/h3/text()").toString());TaoBaoPo po=new TaoBaoPo();po.setParentUrl(requestExtend.getParentUrl());po.setPrice(page.getResultItems().get("price"));po.setShopName(page.getResultItems().get("shopName"));po.setTitle(page.getResultItems().get("title"));po.setUrl(page.getUrl().toString());taoBaoCrawlerService.saveProductDetail(po);}
      复制代码
    •  问题的排查结果是我查询时候keyWord是中文。chromeDriver可以处理而phantomJs不能识别,给加一个url转码:
    • 复制代码
      /*** 使用chromeDriver程序正常运行,转换成PhtanomJs后发现查询到的数据不是想要的数据,复制HTML查看页面后,* 发现搜索的数据是错乱的,搜索框上显示着???,猜测是转码的问题,经过URLEncode之后,程序正常运行。* @return*/public String getKeyWord() {try {return URLEncoder.encode(keyWord,"UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return StringUtils.EMPTY;}
      复制代码
    • 测试的时候代码越跑越慢,打开进程发现一堆phantomJs,固在process结束之后调用driver.quit()关闭phantomJs。

结语:本次demo虽然能跑起来但是还有很多很多不足,每次爬取搜索页只爬了四页,搜索的第一页也没爬,但是懒得再弄,保存数据应该用webMagic的Pipeline来处理,而且频繁的单次保存可以想办法用批量保存来替换;频繁的创建销毁driverJs并不合理,应该使用池化技术做一个DriverPool来管理;demo没有使用IP代理,容易被封,应该写一个IP代理池来管理第三方ip,不过要钱所以就算了。毕竟只是一个demo,剩余问题就不一一列举。

这篇关于学习用java基于webMagic+selenium+phantomjs实现爬虫Demo爬取淘宝搜索页面的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/237346

相关文章

Java如何从Redis中批量读取数据

《Java如何从Redis中批量读取数据》:本文主要介绍Java如何从Redis中批量读取数据的情况,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一.背景概述二.分析与实现三.发现问题与屡次改进3.1.QPS过高而且波动很大3.2.程序中断,抛异常3.3.内存消

Python使用FFmpeg实现高效音频格式转换工具

《Python使用FFmpeg实现高效音频格式转换工具》在数字音频处理领域,音频格式转换是一项基础但至关重要的功能,本文主要为大家介绍了Python如何使用FFmpeg实现强大功能的图形化音频转换工具... 目录概述功能详解软件效果展示主界面布局转换过程截图完成提示开发步骤详解1. 环境准备2. 项目功能结

SpringBoot使用ffmpeg实现视频压缩

《SpringBoot使用ffmpeg实现视频压缩》FFmpeg是一个开源的跨平台多媒体处理工具集,用于录制,转换,编辑和流式传输音频和视频,本文将使用ffmpeg实现视频压缩功能,有需要的可以参考... 目录核心功能1.格式转换2.编解码3.音视频处理4.流媒体支持5.滤镜(Filter)安装配置linu

在Spring Boot中实现HTTPS加密通信及常见问题排查

《在SpringBoot中实现HTTPS加密通信及常见问题排查》HTTPS是HTTP的安全版本,通过SSL/TLS协议为通讯提供加密、身份验证和数据完整性保护,下面通过本文给大家介绍在SpringB... 目录一、HTTPS核心原理1.加密流程概述2.加密技术组合二、证书体系详解1、证书类型对比2. 证书获

Druid连接池实现自定义数据库密码加解密功能

《Druid连接池实现自定义数据库密码加解密功能》在现代应用开发中,数据安全是至关重要的,本文将介绍如何在​​Druid​​连接池中实现自定义的数据库密码加解密功能,有需要的小伙伴可以参考一下... 目录1. 环境准备2. 密码加密算法的选择3. 自定义 ​​DruidDataSource​​ 的密码解密3

使用Python实现Windows系统垃圾清理

《使用Python实现Windows系统垃圾清理》Windows自带的磁盘清理工具功能有限,无法深度清理各类垃圾文件,所以本文为大家介绍了如何使用Python+PyQt5开发一个Windows系统垃圾... 目录一、开发背景与工具概述1.1 为什么需要专业清理工具1.2 工具设计理念二、工具核心功能解析2.

Java使用MethodHandle来替代反射,提高性能问题

《Java使用MethodHandle来替代反射,提高性能问题》:本文主要介绍Java使用MethodHandle来替代反射,提高性能问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录一、认识MethodHandle1、简介2、使用方式3、与反射的区别二、示例1、基本使用2、(重要)

Java实现本地缓存的常用方案介绍

《Java实现本地缓存的常用方案介绍》本地缓存的代表技术主要有HashMap,GuavaCache,Caffeine和Encahche,这篇文章主要来和大家聊聊java利用这些技术分别实现本地缓存的方... 目录本地缓存实现方式HashMapConcurrentHashMapGuava CacheCaffe

SpringBoot整合Sa-Token实现RBAC权限模型的过程解析

《SpringBoot整合Sa-Token实现RBAC权限模型的过程解析》:本文主要介绍SpringBoot整合Sa-Token实现RBAC权限模型的过程解析,本文给大家介绍的非常详细,对大家的学... 目录前言一、基础概念1.1 RBAC模型核心概念1.2 Sa-Token核心功能1.3 环境准备二、表结

Python实现一键PDF转Word(附完整代码及详细步骤)

《Python实现一键PDF转Word(附完整代码及详细步骤)》pdf2docx是一个基于Python的第三方库,专门用于将PDF文件转换为可编辑的Word文档,下面我们就来看看如何通过pdf2doc... 目录引言:为什么需要PDF转Word一、pdf2docx介绍1. pdf2docx 是什么2. by