由于工作需要,开始做起了网络爬虫
以爬取小红书为例

创建chromeDriver

 // 创建浏览器public void createDriver() {// 开启浏览器后需要访问的地址String XIAOHOGNSHU_URL = "https://www.xiaohongshu.com/explore";// 这里我是使用了ip代理模式,因为同一个ip地址频繁请求一个网页会出发反爬虫机制HttpProxy httpProxy= new HttpProxy();try {//获取最优代理iphttpProxy=httpProxyService.getBestProxyIp();Assert.isNotNull(httpProxy, BaseCodeUtil.OBJECT_NOT_EXIST);}catch (Exception e) {// 生成5个代理iphttpProxyService.createProxy(5);httpProxy=httpProxyService.getBestProxyIp();Assert.isNotNull(httpProxy, BaseCodeUtil.OBJECT_NOT_EXIST);}// 过期时间String expireTimeStr = httpProxy.getExpire_time();SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Long expireTime = 0L;try {// 格式化,计算ip到期时间expireTime =simpleDateFormat.parse(expireTimeStr).getTime() - System.currentTimeMillis();}catch (Exception e) {logger.error(e.getMessage(),e);}String ip = httpProxy.getIp();String ipAndPort = httpProxy.getIp() + ":" + httpProxy.getPort();WebDriver driver=null;// 获取当前服务器系统String osName=System.getProperty("os.name");if(osName.contains("Windows")) {String webDirverHome = "E:\\code\\chromedriver.exe";System.setProperty("webdriver.chrome.driver", webDirverHome);ChromeOptions chromeOptions = new ChromeOptions();// 启用所有类型的日志并收集所有日志LoggingPreferences logPrefs = new LoggingPreferences();logPrefs.enable(LogType.PERFORMANCE, Level.ALL);logPrefs.enable(LogType.BROWSER, Level.ALL);chromeOptions.setCapability(CapabilityType.LOGGING_PREFS, logPrefs);// 不加载图片chromeOptions.addArguments("blink-settings=imagesEnabled=false");// 代理ipchromeOptions.addArguments("--proxy-server=http://" + ipAndPort);// 生成浏览器driver = new ChromeDriver(chromeOptions);}else {/* linux */ChromeOptions options = new ChromeOptions();// 禁用沙盒options.addArguments("no-sandbox");// 禁止加载图片options.addArguments("blink-settings=imagesEnabled=false");options.addArguments("--whitelisted-ips=\"\"");// 代理ipoptions.addArguments("--proxy-server=http://" + ipAndPort);// 无界面模式 在Linux中一定是不能唤起浏览器的(很重要)options.setHeadless(Boolean.TRUE);driver = new ChromeDriver(options);}// 发起请求driver.get(XIAOHOGNSHU_URL);// 在页面加载时,寻找 note-wrapper 元素,若在10秒钟未找到,抛出异常new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".note-wrapper")));
//      driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);// 获取当前浏览器的信息logger.info("Title:" + driver.getTitle());logger.info("currentUrl:" + driver.getCurrentUrl());// 这里我是把生成的浏览器放在一个BlockingQueue中进行管理logger.info(">>>>>>>>>>queue:"+queue.size()+">>>>>>>>");// 这里是把生成的浏览器放在DelayQueue中进行定时删除ip到期的浏览器(ip到期后不可用,浏览器也会失效)logger.info(">>>>>>>>>>delayQueue:"+delayQueue.size()+">>>>>>>>");HashMap<String, Object> driverMap = new HashMap<String,Object>();String name = UUID.randomUUID().toString();driverMap.put("name", name);driverMap.put("driver", driver);driverMap.put("ip", ip);driverMap.put("failNum", 0);logger.info("################浏览器信息#####################");logger.info("name:" + name + ";ip:" + ip + ";failNum:" + 0);queue.offer(driverMap);WebDriverTask webDriverTask = new WebDriverTask(name,expireTime);delayQueue.offer(webDriverTask);logger.info("<<<<<<<<<<queue:"+queue.size()+"<<<<<<<<");logger.info("<<<<<<<<<<delayQueue:"+delayQueue.size()+"<<<<<<<<");}

创建浏览器是在项目初始化的时候进行的,我这里是直接创建了5个浏览器放在容器中,并定时的去更新浏览器

 private int webDriverNum = 5;public void initDriver() {// 循环创建浏览器for(int i=0;i<webDriverNum;i++) {System.out.println("========initDriver 第" + (i + 1) + "个===================");try {createDriver();}catch (Exception e) {logger.error(e.getMessage(),e);logger.error("第" + (i + 1) + "个浏览器启动失败");}}// 再启动一个线程进行轮询工作new Thread(new Runnable() {@Overridepublic void run() {while(delayQueue.size() > 0) {try {// 当delayQueue中的浏览器时间到期后会取出来,否则将阻塞在这里WebDriverTask webDriverTask = delayQueue.take();String name = webDriverTask.getName();// 然后用轮询的方式一个一个进行名字比较,true 则销毁并创建新的浏览器,false 则重新塞回队列中for(int i = 0; i < queue.size(); i++) {Map<String, Object> driverMap = queue.take();String driverName = (String) driverMap.get("name");if(StringUtils.equals(driverName, name)) {//销毁WebDriver driver = (WebDriver)driverMap.get("driver");driver.quit();//新建浏览器并塞入队列createDriver();}else {//塞回队列queue.put(driverMap);}}} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}

我之前用的是 @PostConstruct 进行初始化的,后来可能是生命周期初始化顺序有点问题,后来改成在 Listerner中使用 SpringContextHolder.getBean(ChromeDriverService.class).initDriver(); 的方法在程序启动的时候开启5个浏览器

 // 项目关闭的时候,记得结束掉开启的浏览器,释放资源@PreDestroypublic void destoryDriver() {logger.info("=======销毁前,关闭webDriver======");while(queue.size()>0) {logger.info(">>>>>>>>>>销毁前,webDriver_count:"+queue.size()+">>>>>>>>");Map<String, Object> driverMap = queue.poll();WebDriver driver = (WebDriver)driverMap.get("driver");driver.quit();delayQueue.clear();logger.info("<<<<<<<<<<销毁后,webDriver_count:"+queue.size()+"<<<<<<<<");}}

请求小红书

 public UserKocAuth xiaohongshuAuthentication(String homePageUrl) {String chromeDriver =null;Map<String, Object> driverMap = null;UserKocAuth userKocAuth = new UserKocAuth();try {long before = System.currentTimeMillis();System.out.println("无头浏览器开始,before:" + before);// 获取浏览器driverMap = chromeDriverService.getDriver();// 发起请求chromeDriver = doChromeDriver(homePageUrl,driverMap);long after = System.currentTimeMillis();System.out.println("无头浏览器结束,after:" + after);System.out.println("无头浏览器花费时间,all:" + (after - before));// 用来解析浏览器返回的HTML页面(放在之后一篇再讲Jsoup)Document doc = Jsoup.parse(chromeDriver);logger.info(doc.text());Elements cardElement=doc.getElementsByClass("card-info").get(0).getElementsByClass("info-number");logger.info("关注:"+cardElement.get(0).text());logger.info("粉丝:"+cardElement.get(1).text());logger.info("获赞与收藏:"+cardElement.get(2).text());String attentionStr = cardElement.get(0).text();String fansStr = cardElement.get(1).text();String likeNumStr = cardElement.get(2).text();Integer attention = numberEndWithCN(attentionStr);Integer fansNum = numberEndWithCN(fansStr);Integer likeNum = numberEndWithCN(likeNumStr);Element element = doc.getElementsByClass("author-container").first().getElementsByClass("left").first();String face = element.select("img").first().attr("src");logger.info("头像:" + face);String name = doc.getElementsByClass("user-name").get(0).getElementsByClass("name-detail").text();logger.info("名字:" + name);// 这里我是写了一个方法,切割url链接,获取url中的idMap<String, String> paramsMap = GetParamsMapFromUrlUtil.urlSplit(homePageUrl);String platformId = paramsMap.get("appuid");userKocAuth.setPlatformId(platformId);userKocAuth.setFansNum(fansNum);userKocAuth.setAttention(attention);userKocAuth.setLikeNum(likeNum);userKocAuth.setPlatformFace(face);userKocAuth.setPlatformName(name);}catch (Exception e) {logger.error(e.getMessage(),e);int failNum = (Integer)driverMap.get("failNum");driverMap.put("failNum", ++failNum);throw new IllegalParameterException(ErrorCodeUtil.AUTHENTICATION_EXCEPTION);}finally {// 归还浏览器chromeDriverService.returnDriver(driverMap);}return userKocAuth;}

具体的chromeDriver请求方法

 public String doChromeDriver(String url, Map<String,Object> driverMap) {String pageSource = null;Long chromeDriver = System.currentTimeMillis();WebDriver driver = (WebDriver)driverMap.get("driver");Long chromeDriverOver = System.currentTimeMillis();logger.info("结束唤起浏览器,开始渲染页面,chromeDriverOver:" + chromeDriverOver);logger.info("唤起页面一共花费,all:" + (chromeDriverOver - chromeDriver));// 发起请求driver.get(url);long urlOver = System.currentTimeMillis();logger.info("获取url,urlOver:" + urlOver);logger.info("获取url共花费,all:" + (urlOver - chromeDriverOver));// 页面加载直到页面获取到 author-container 元素,10秒后若没找到该元素,则抛出异常new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".author-container")));pageSource = driver.getPageSource();logger.info("获取结果pageSource:" + pageSource);return pageSource;}

小结

1、每次创建浏览器的时候我都会去Redis中取一个ip,如果没有ip,我会创建5个ip放在Redis中,问题就在于:如果有一个ip过期了,程序中就会从剩下的四个ip中去取一个,这样一来,创建的5个浏览器中一定会有两个浏览器共用同一个ip,循序下去5个浏览器就会使用容一个ip,这样一来就违背了我们的初衷,并且当这个ip过期之后,5个浏览器就同时崩溃,且再次创建浏览器的时候,只会创建一个浏览器。所以需要定时去刷新ip的数量和浏览器的数量
2、在项目启动时我是另外开了一个线程去做一个死循环的轮询,定时查看过期ip,当有ip过期,则关闭该浏览器并重新启动一个新ip的浏览器
3、结束项目的时候一定要关闭浏览器释放资源,不然会循序下去会占用CPU大量的资源

无头浏览器返回的HTML页面,可以用Jsoup进行解析,详情可以看Jsoup用法

JAVA网络爬虫(无头浏览器ChromeDriver)相关推荐

  1. Java网络爬虫实操(5)

    上一篇:Java网络爬虫实操(4) 大家好,前几篇文章介绍的URL都是返回HTML内容的,然后再从HTML字符串里解析出我们想要的数据. 但是,随着前端编程技术的发展,至少十多年前开始ajax.jso ...

  2. java 网络爬虫 正则表达式_【干货】Java网络爬虫基础知识

    原标题:[干货]Java网络爬虫基础知识 引言 Java 网络爬虫具有很好的扩展性可伸缩性,其是目前搜索引擎开发的重要组成部分.例如,著名的网络爬虫工具 Nutch 便是采用 Java 开发,该工具以 ...

  3. 第三十六期:学 Java 网络爬虫,需要哪些基础知识?

    说起网络爬虫,大家想起的估计都是 Python ,诚然爬虫已经是 Python 的代名词之一,相比 Java 来说就要逊色不少.有不少人都不知道 Java 可以做网络爬虫,其实 Java 也能做网络爬 ...

  4. Java网络爬虫该如何学习

    文章目录 引言 怎么入门网络爬虫 课程特色 学完本课程能收获什么 引言 互联网以及移动技术的飞速发展,使得全球数据量呈现前所未有的爆炸式增长态势.例如,用户在互联网上的搜索数据.交易数据.评论数据.社 ...

  5. Java网络爬虫学习记录(请求基础篇)

    目录 个人实验遇见错误集: 一.javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX ...

  6. python网络爬虫、Java 网络爬虫,哪个更好?

    说起网络爬虫,大家想起的估计都是 Python ,诚然爬虫已经是 Python 的代名词之一,相比 Java 来说就要逊色不少.有不少人都不知道 Java 可以做网络爬虫,其实 Java 也能做网络爬 ...

  7. 学 Java 网络爬虫,需要哪些基础知识?

    说起网络爬虫,大家想起的估计都是 Python ,诚然爬虫已经是 Python 的代名词之一,相比 Java 来说就要逊色不少.有不少人都不知道 Java 可以做网络爬虫,其实 Java 也能做网络爬 ...

  8. Java网络爬虫小案例(详细版)

    有bug:修改了<scope>test</scope>后,在控制台还是不能显示日志信息,没找到解决办法 配置了log4j.properties,控制台没有显示日志信息_连胜是我 ...

  9. Java网络爬虫实战案例一

    紧接我们上次的问题,如何获取服务器发送的资源,保存到本地?上一篇文章见java网络爬虫核心原理. 一.Java IO流三分游(input,output)   我们知道计算机是用来处理数据的.所有的程序 ...

  10. Java网络爬虫实操(3)

    上一篇:Java网络爬虫实操(2) 本篇文章主要介绍NetDiscovery框架中pipeline模式的一些实际使用方法. 1) 什么是pipeline pipeline是一种常见的算法模式,针对不断 ...

最新文章

  1. 微服务为什么选Spring Cloud
  2. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之设备树模型
  3. 【Clickhouse】ClickHouse REST API(HTTP接口)及Engine引擎的使用
  4. [DBNETLIB][ConnectionOpen(connect()).]SQL Server 不存在或拒绝访问解决方
  5. 苹果开发者账号分类详解
  6. 西门子S7200PLC连接CHNet-S7200PD实现以太网通信配置方法
  7. 图书馆借书系统c语言,急求程序!!!简单图书馆借/还书管理子系统
  8. 软件工程4-软件概要设计
  9. Directory Opus一款功能强大的资源管理器
  10. 3D游戏角色建模设计这样做,模型满分!
  11. Verilog 仿真事件中的延时分析
  12. .ps格式的文件怎么打开?方法:通过GSview打开或将.ps转换成.pdf
  13. 01背包问题深度理解
  14. 如何拍摄一部优秀的广告片——表现手法,特殊创意
  15. 安装activemq或者tomcat等启动时报错
  16. 静觅小白爬虫及进阶系列学习笔记
  17. Deep Snake for Real-Time Instance Segmentation:基于Deep Snake的实例实时分割
  18. MySQL · 捉虫动态 · UK 包含 NULL 值备库延迟分析
  19. 内网渗透笔记——注册表自启动与msi提权
  20. python收集参数_python收集参数

热门文章

  1. 【云贝学院】腾讯云TDSQL-数据库字符集
  2. golang的开源游戏服务器框架
  3. ubuntu设置开机启动程序
  4. 优化理论20---插值法: Hermite插值法、龙格现象、分段插值、样条插值
  5. 转帖:BTree,B-Tree,B+Tree,B*Tree都是什么
  6. 在linux下安装chrome 浏览器
  7. python定位二维码_python实现二维码、条形码识别
  8. L1-020. 帅到没朋友(2016)
  9. Oracle计算偏差率的方法
  10. 光学分频器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告