Java 实现通过网址URL截取整个网页的长图并保存(遇到的各种坑)

1.寻找实现的技术

通过查询资料发现可以用 Selenium + PhantomJS 进行实现(通过浏览器模拟打开页面,并截图),通过查找资料实现了功能,发现通过PhantomJS截出来的图片变形严重。

通过Selenium 的官网 发现Selenium 是支持所有主流的浏览器驱动进行模拟浏览器,且官方不推荐PhantomJS。

Selenium是一个用于Web应用程序测试的工具 

PhantomJS是一个基于webkit的JavaScript API

支持的浏览器如下:

不建议使用说明:

所以后面打算用Chome驱动结合去使用

2.  编码实现

        2.1环境准备

               项目中导入对应的jar

                

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.141.59</version>
</dependency>
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>27.0-jre</version>
</dependency>

                准备chome驱动

https://registry.npmmirror.com/binary.html?path=chromedriver/&spm=a2c6h.24755359.0.0.6d444dccLAiv2m 通过这个地址去下载对应版本的驱动 window 下载Windows的  Linux下载对应Linux的  我这里用的是 90.0.4430.24 这个版本

        2.1开始编码

    public static void guge(String url){
        //这里设置下载的驱动路径,Windows对应chromedriver.exe Linux对应chromedriver,具体路径看你把驱动放在哪
        System.setProperty("webdriver.chrome.driver", "C:\\Users\\86186\\Desktop\\phantomjs-2.1.1-windows\\bin\\chromedriver.exe");
        ChromeOptions options = new ChromeOptions();
        //ssl证书支持
        options.setCapability("acceptSslCerts", true);
        //截屏支持
        options.setCapability("takesScreenshot", true);
        //css搜索支持
        options.setCapability("cssSelectorsEnabled", true);
        options.setHeadless(true);
        ChromeDriver driver = new ChromeDriver(options);
        try {
            //设置需要访问的地址
            driver.get(url);
            //获取高度和宽度一定要在设置URL之后,不然会导致获取不到页面真实的宽高;
            Long width = (Long)driver.executeScript("return document.documentElement.scrollWidth");
            Long height =(Long) driver.executeScript("return document.documentElement.scrollHeight");
            System.out.println("高度:"+height);
            //设置窗口宽高,设置后才能截全
            driver.manage().window().setSize(new Dimension(width.intValue(), height.intValue()));
            //设置截图文件保存的路径
            String screenshotPath = "C:\\Users\\86186\\Desktop\\phantomjs-2.1.1-windows\\bin\\img\\imgGG.png";
            File srcFile = driver.getScreenshotAs(OutputType.FILE);
            FileUtils.copyFile(srcFile, new File(screenshotPath));
        }catch (Exception e){
            throw new RuntimeException("截图失败",e);
        }finally {
            driver.quit();
        }
    }

                     第一个坑就是宽高必须要在设置路径之后,上面已经说明;

                     坑2:有些页面加载比较慢,它是默认20S没加载完成就不会加载了导致有些页面有大的图片截图下来显示一个加载中。。。

    //解决如下: 设置超时时间
    public static void guge(String url){
        //这里设置下载的驱动路径,Windows对应chromedriver.exe Linux对应chromedriver,具体路径看你把驱动放在哪
        System.setProperty("webdriver.chrome.driver", "C:\\Users\\86186\\Desktop\\phantomjs-2.1.1-windows\\bin\\chromedriver.exe");
        ChromeOptions options = new ChromeOptions();
        //ssl证书支持
        options.setCapability("acceptSslCerts", true);
        //截屏支持
        options.setCapability("takesScreenshot", true);
        //css搜索支持
        options.setCapability("cssSelectorsEnabled", true);
        options.setHeadless(true);
        ChromeDriver driver = new ChromeDriver(options);
        //设置超时,避免有些内容加载过慢导致截不到图
        driver.manage().timeouts().pageLoadTimeout(1, TimeUnit.MINUTES);
        driver.manage().timeouts().implicitlyWait(1, TimeUnit.MINUTES);
        driver.manage().timeouts().setScriptTimeout(1, TimeUnit.MINUTES);
        try {
            //设置需要访问的地址
            driver.get(url);
            //获取高度和宽度一定要在设置URL之后,不然会导致获取不到页面真实的宽高;
            Long width = (Long)driver.executeScript("return document.documentElement.scrollWidth");
            Long height =(Long) driver.executeScript("return document.documentElement.scrollHeight");
            System.out.println("高度:"+height);
            //设置窗口宽高,设置后才能截全
            driver.manage().window().setSize(new Dimension(width.intValue(), height.intValue()));
            //设置截图文件保存的路径
            String screenshotPath = "C:\\Users\\86186\\Desktop\\phantomjs-2.1.1-windows\\bin\\img\\imgGG.png";
            File srcFile = driver.getScreenshotAs(OutputType.FILE);
            FileUtils.copyFile(srcFile, new File(screenshotPath));
        }catch (Exception e){
            throw new RuntimeException("截图失败",e);
        }finally {
            driver.quit();
        }
    }

                      坑3:有些页面是懒加载的模式,如果滚动条不滚动到对应的位置,是不会去加载那部分类容,导致懒加载部分截图空白。

    //解决如下: 模拟浏览器滚动滚动条 解决懒加载问题
    public static void guge(String url){
        //这里设置下载的驱动路径,Windows对应chromedriver.exe Linux对应chromedriver,具体路径看你把驱动放在哪
        System.setProperty("webdriver.chrome.driver", "C:\\Users\\86186\\Desktop\\phantomjs-2.1.1-windows\\bin\\chromedriver.exe");
        ChromeOptions options = new ChromeOptions();
        //ssl证书支持
        options.setCapability("acceptSslCerts", true);
        //截屏支持
        options.setCapability("takesScreenshot", true);
        //css搜索支持
        options.setCapability("cssSelectorsEnabled", true);
        options.setHeadless(true);
        ChromeDriver driver = new ChromeDriver(options);
        //设置超时,避免有些内容加载过慢导致截不到图
        driver.manage().timeouts().pageLoadTimeout(1, TimeUnit.MINUTES);
        driver.manage().timeouts().implicitlyWait(1, TimeUnit.MINUTES);
        driver.manage().timeouts().setScriptTimeout(1, TimeUnit.MINUTES);
        try {
            //设置需要访问的地址
            driver.get(url);
            //获取高度和宽度一定要在设置URL之后,不然会导致获取不到页面真实的宽高;
            Long width = (Long)driver.executeScript("return document.documentElement.scrollWidth");
            Long height =(Long) driver.executeScript("return document.documentElement.scrollHeight");
            System.out.println("高度:"+height);
            //这里需要模拟滑动,有些是滑动的时候才加在的
            long temp_height = 0;
            while (true) {
                //每次滚动500个像素,因为懒加载所以每次等待2S 具体时间可以根据具体业务场景去设置
                Thread.sleep(2000);
                driver.executeScript("window.scrollBy(0,500)");
                temp_height += 500;
                if(temp_height>=height){
                    break;
                }
            }
            //设置窗口宽高,设置后才能截全
            driver.manage().window().setSize(new Dimension(width.intValue(), height.intValue()));
            //设置截图文件保存的路径
            String screenshotPath = "C:\\Users\\86186\\Desktop\\phantomjs-2.1.1-windows\\bin\\img\\imgGG.png";
            File srcFile = driver.getScreenshotAs(OutputType.FILE);
            FileUtils.copyFile(srcFile, new File(screenshotPath));
        }catch (Exception e){
            throw new RuntimeException("截图失败",e);
        }finally {
            driver.quit();
        }
    }

                       通过以上操作,终于可以在本地(Windows)运行并且生成完美截图,开始部署在到服务器上

                       准备Linux环境安装Chrome浏览器以及依赖


//chrome程序
yum install https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
 
//chrome依赖库 
yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 -y

                              安装完成之后运行各种坑又来了~~~~~~~~~~~~~~~ 

                      坑4:在Linux服务器上报错 错误如下:

java.lang.IllegalStateException: The driver is not executable: /home/soft/chrome/chromedriver
    at com.google.common.base.Preconditions.checkState(Preconditions.java:588)
    at org.openqa.selenium.remote.service.DriverService.checkExecutable(DriverService.java:150)
    at org.openqa.selenium.remote.service.DriverService.findExecutable(DriverService.java:141)
    at org.openqa.selenium.chrome.ChromeDriverService.access$000(ChromeDriverService.java:35)
    at org.openqa.selenium.chrome.ChromeDriverService$Builder.findDefaultExecutable(ChromeDriverService.java:159)
    at org.openqa.selenium.remote.service.DriverService$Builder.build(DriverService.java:355)
    at org.openqa.selenium.chrome.ChromeDriverService.createDefaultService(ChromeDriverService.java:94)
    at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:157)
    at com.bmsoft.evidence.service.impl.ArticleServiceImpl.getFilePathByUrl(ArticleServiceImpl.java:734)
    at com.bmsoft.evidence.service.impl.ArticleServiceImpl.screenshot(ArticleServiceImpl.java:287)
    at com.bmsoft.evidence.service.impl.ArticleServiceImpl$FastClassBySpringCGLIB$dafff6e9.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:752)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:295)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
    at com.bmsoft.evidence.service.impl.ArticleServiceImpl$EnhancerBySpringCGLIB$52855f88.screenshot(<generated>)
    at com.bmsoft.evidence.service.impl.AsyncServiceImpl.saveWebSiteDetail(AsyncServiceImpl.java:139)
    at com.bmsoft.evidence.service.impl.AsyncServiceImpl.saveWebSite(AsyncServiceImpl.java:124)
    at com.bmsoft.evidence.service.impl.AsyncServiceImpl$FastClassBySpringCGLIB$d0ae7c23.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:752)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

                       驱动执行无权限,通过赋予驱动权限来处理

//进入驱动所在文件夹后执行以下命令:

chmod a+x chromedriver 

                       坑4:有权限之后又出现新的错误,错误如下:

org.openqa.selenium.WebDriverException: unknown error: Chrome failed to start: exited abnormally.
  (unknown error: DevToolsActivePort file doesn't exist)
  (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)
Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:17:03'
System info: host: 'zf-test', ip: '127.0.0.1', os.name: 'Linux', os.arch: 'amd64', os.version: '3.10.0-514.26.2.el7.x86_64', java.version: '1.8.0_261'
Driver info: driver.version: ChromeDriver
remote stacktrace: #0 0x7fd05e812e89 <unknown>

    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.openqa.selenium.remote.W3CHandshakeResponse.lambda$errorHandler$0(W3CHandshakeResponse.java:62)
    at org.openqa.selenium.remote.HandshakeResponse.lambda$getResponseFunction$0(HandshakeResponse.java:30)
    at org.openqa.selenium.remote.ProtocolHandshake.lambda$createSession$0(ProtocolHandshake.java:126)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Spliterators$ArraySpliterator.tryAdvance(Spliterators.java:958)
    at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
    at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:499)

                        通过查找发现是一些浏览器的参数设置不对导致,设置浏览器参数

    //解决如下: 模拟浏览器滚动滚动条 解决懒加载问题
    public static void guge(String url){
        //这里设置下载的驱动路径,Windows对应chromedriver.exe Linux对应chromedriver,具体路径看你把驱动放在哪
        System.setProperty("webdriver.chrome.driver", "C:\\Users\\86186\\Desktop\\phantomjs-2.1.1-windows\\bin\\chromedriver.exe");
        ChromeOptions options = new ChromeOptions();
        //ssl证书支持
        options.setCapability("acceptSslCerts", true);
        //截屏支持
        options.setCapability("takesScreenshot", true);
        //css搜索支持
        options.setCapability("cssSelectorsEnabled", true);
        //设置浏览器参数
        options.addArguments("--headless");
        options.addArguments("--no-sandbox");
        options.addArguments("--disable-gpu");
        options.addArguments("--disable-dev-shm-usage");
        options.setHeadless(true);
        ChromeDriver driver = new ChromeDriver(options);
        //设置超时,避免有些内容加载过慢导致截不到图
        driver.manage().timeouts().pageLoadTimeout(1, TimeUnit.MINUTES);
        driver.manage().timeouts().implicitlyWait(1, TimeUnit.MINUTES);
        driver.manage().timeouts().setScriptTimeout(1, TimeUnit.MINUTES);
        try {
            //设置需要访问的地址
            driver.get(url);
            //获取高度和宽度一定要在设置URL之后,不然会导致获取不到页面真实的宽高;
            Long width = (Long)driver.executeScript("return document.documentElement.scrollWidth");
            Long height =(Long) driver.executeScript("return document.documentElement.scrollHeight");
            System.out.println("高度:"+height);
            //这里需要模拟滑动,有些是滑动的时候才加在的
            long temp_height = 0;
            while (true) {
                //每次滚动500个像素,因为懒加载所以每次等待2S 具体时间可以根据具体业务场景去设置
                Thread.sleep(2000);
                driver.executeScript("window.scrollBy(0,500)");
                temp_height += 500;
                if(temp_height>=height){
                    break;
                }
            }
            //设置窗口宽高,设置后才能截全
            driver.manage().window().setSize(new Dimension(width.intValue(), height.intValue()));
            //设置截图文件保存的路径
            String screenshotPath = "C:\\Users\\86186\\Desktop\\phantomjs-2.1.1-windows\\bin\\img\\imgGG.png";
            File srcFile = driver.getScreenshotAs(OutputType.FILE);
            FileUtils.copyFile(srcFile, new File(screenshotPath));
        }catch (Exception e){
            throw new RuntimeException("截图失败",e);
        }finally {
            driver.quit();
        }
    }

                              处理完以上问题 终于在服务器上不报错了,且生成了图片文件,正在高兴的时候,打开图片发现上面的文字全是方框 o(╥﹏╥)o

                        坑5:图片中文变成方框:

 通过查询资料发现是因为系统没有中文字体导致的,然后开始安装系统中文字体

yum install ipa-gothic-fonts xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-utils xorg-x11-fonts-cyrillic xorg-x11-fonts-Type1 xorg-x11-fonts-misc -y

安装上面字体之后 发现还是有些字变方框,这时候我就把Windows上的一些中文字体移到Linux系统中去了,window字体位置自行百度

#1.在/usr/share/fonts目录下创建 chinese目录进行存放中文字体

mkdir -p /usr/share/fonts/chinese/

#2.为刚加入的字体设置缓存使之有效

fc-cache -fv      

#3.查看系统中的字体,是否已包含songti                     

fc-list  

完成以上设置,再执行截图,完美截出想要的图片 终于算搞定了~~~~~~~~~ O(∩_∩)O哈哈~

  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值