java selenium div内嵌滚动条 网页长截图发邮件

java selenium 网页内嵌滚动条截图发邮件


由于公司要求做一个接口,请求这个接口进行网页截图并发送邮件的功能,本来前期是用python写好了,but似乎不太符合要求,那么就用java来重新写一遍,虽然我是java菜鸡,但这并不影响 我修修补补,ctrl c 和ctrl v嘛。

主要问题

此需求很简单,主要就是对网页的截图。 如果网页是自带了 浏览器的滚动条,那么 selenium 一条命令就可以解决整个网页的截图了,然鹅我们这网页是个坑,他的滚动条都是放在div这种元素中内嵌了, 因此需要对 div元素计算高度, 进行滚动截图, 然后将滚动出来的截图拼接到一起。在这里插入图片描述
如图,这玩意分为了好几部分
左侧功能栏(截图时忽略)
顶部功能栏(不可能每滚动一次,就把顶部也截图进去,所以我选择 先截图顶部,然后截图 网页滚动条主体内容)
网页主体滚动条(当然啦, 这个滚动条属于那个非常惹人厌恶的div元素)
在这里插入图片描述
可以很清楚的明白,就是这玩意。 因此我们获取浏览器或者窗口高度是没有效果的, 必须先要获取这个div 滚动条的高度, just 像这样:

document.getElementsByClassName('scroll-canvas scroll-canvas--dashboard')[0].scrollHeight

下面展开说

    private void getGrafana(String username, String password, String url,
                            String mailUser, String mailPassword, String[] mailReceiver, String mailServer) {
        WebDriver driver=null;
        // 定义图片名称
        Date d = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String  file_name = "sendmail_" + sdf.format(d) + ".png";
        try {
        	//各位如果自己写, 此处直接调用chrome driver就行。我这里是调用了封装的方法
            driver = WebDriverUtil.getH5Driver();
            driver.get(url);
            driver.manage().timeouts().implicitlyWait(20,TimeUnit.SECONDS);
            System.out.println("打开网址后等待加载");
            // 用户名
            By loc  = By.xpath("//input[@id='username']");
            WebElement ele1 = WebDriverUtil.find_element(driver, loc, 1,10);
            ele1.click();
            ele1.clear();
            ele1.sendKeys(username);
            //密码
            loc = By.xpath("//input[@id='password']");
            WebElement ele2 = WebDriverUtil.find_element(driver, loc, 1, 10);
            ele2.click();
            ele2.clear();
            ele2.sendKeys(password);
            //点击登录
            loc = By.xpath("//input[@name='submit']");
            WebElement ele3 = WebDriverUtil.find_element(driver, loc, 1, 10);
            ele3.click();
            Thread.sleep(8000);
            if(driver.getTitle() != null && driver.getTitle().length() >0){
                System.out.println("打开了:" +driver.getTitle());
            }else{
                System.out.println("没有打开页面,请检查!");
            }
            try {
                long scroll_width = (long) ((JavascriptExecutor)driver).executeScript("return document.body.parentNode.scrollWidth;");
                long scroll_height = (long) ((JavascriptExecutor)driver).executeScript("return document.body.offsetHeight;");
                //System.out.println(scroll_width + scroll_height);
                driver.manage().window().setSize(new Dimension((int)scroll_width, (int) scroll_height));
                String js_scroll = "document.getElementsByClassName('scroll-canvas scroll-canvas--dashboard')[0].scrollTop =";
                int content_height = (int)(long) ((JavascriptExecutor)driver).executeScript("return document.getElementsByClassName('scroll-canvas scroll-canvas--dashboard')[0].scrollHeight");
                int title_height = (int)(long) ((JavascriptExecutor)driver).executeScript("return document.getElementsByClassName('navbar')[0].offsetHeight");
                System.out.println("content height is: " + content_height + ", title height is: " + title_height);
                WebElement scroll_content = driver.findElement(By.xpath("//div[@class='scroll-canvas scroll-canvas--dashboard']"));
                WebElement scroll_top = driver.findElement(By.xpath("//div[@class='navbar']"));
                Thread.sleep(2000);
                boolean flag = GrafanaSendScreenShot.fullpage_screenshot(driver, file_name, title_height,  content_height, js_scroll, scroll_top, scroll_content);
            }catch (Exception e){
                e.printStackTrace();
                System.out.println("计算浏览器高度宽度或者截图失败,请检查");
                System.out.println(e.getMessage());
                Reporter.log(e.getMessage());
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("加载网页出现问题,请检查");
            System.out.println(e.getMessage());
            Reporter.log(e.getMessage());
        } finally {
            if(driver != null) {
                driver.close();
                driver.quit();
            }
        }
        File file = new File("");
        String baseDir = file.getAbsolutePath();
        String file_path = baseDir+"/screen/";
        File filetmps = new File(file_path+file_name);
        boolean flag = filetmps.exists();
        if (flag) {
            System.out.println("使用截图文件: "+ filetmps.getName());
            try{
                System.out.println("开始发送邮件...");
                GrafanaSendScreenShot.send_email(filetmps, mailUser, mailPassword, mailReceiver, mailServer);
                System.out.println("邮件发送成功...");
            }catch (Exception e){
                e.printStackTrace();
                System.out.println(e.getMessage());
                Reporter.log(e.getMessage());
            }
        }else{
            System.out.println("截图不存在,请检查或者重试!");
        }

    }

这里要注意下:
方式1:

JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
jsExecutor.executeScript("js代码");

方式2:

(JavascriptExecutor)driver).executeScript("js代码");

在这里插入图片描述
我作为一个菜鸡,我真看不出来这有啥不同。采用方式1时, 我返回的一直就是null, 简直null到我怀疑人生。方式2是可行的。不知道 大佬们是个啥情况,如果了解麻烦告知我下。

下面是 截图主要代码:

public class GrafanaSendScreenShot {

    public static boolean fullpage_screenshot(WebDriver driver, String file_pic, int title_height,
                                              int content_height, String js_scroll, WebElement scroll_top, WebElement scroll_content) throws InterruptedException, IOException {
        System.out.println("Starting chrome full page screenshot workaround ...");
        // 获取浏览器滚动条高度,宽度等信息
        if (content_height == 0 ){
            content_height = (int)(long) ((JavascriptExecutor)driver).executeScript("return document.body.parentNode.scrollHeight");
        }
        int total_width = (int)(long) ((JavascriptExecutor)driver).executeScript("return document.body.offsetWidth");
        int total_height = content_height + title_height;
        int viewport_width =(int)(long) ((JavascriptExecutor)driver).executeScript("return document.body.clientWidth");
        int viewport_height = (int)(long) ((JavascriptExecutor)driver).executeScript("return window.innerHeight");
        System.out.println("Total: ("+total_width+ ","+total_height+")" + " Viewport: ("+viewport_width+","+viewport_height+")");
        List<List<Integer>> rectangles = new ArrayList<>();
        int i = 0;
        int count = 0;
        int top_height;
        int top_width;
        while(i<total_height){
            int ii = 0;
            if(count == 0){
                top_height = i + title_height;
            }else{
                top_height = i + viewport_height;
            }
            if(top_height > total_height){
                top_height = total_height;
            }
            while (ii < total_width){
                top_width = ii + viewport_width;
                if (top_width > total_width){
                    top_width = total_width;
                }
                System.out.println("Appending rectangle: "+ii+"  ,"+ i+"  ,"+ top_width+"  ,"+ top_height);
                List<Integer> tmp_data = new ArrayList<>();
                tmp_data.add(ii);
                tmp_data.add(i);
                tmp_data.add(top_width);
                tmp_data.add(top_height);
                rectangles.add(tmp_data);
                ii = ii + viewport_width;
            }
            if(count == 0){
                i = i + title_height;
            }else{
                i = i + viewport_height;
            }
            count = count +1;
        }
        //搞一张全新的图片撒~
        BufferedImage stitched_image = null;
        stitched_image = new BufferedImage(total_width, total_height, BufferedImage.TYPE_INT_RGB);
        List<Integer> previous = new ArrayList<>();
        int part = 0;
        int [] offset ;
        File file = new File("");
        //使用相对路径,将中间过程的截图文件和 最终的文件都放在screen目录下
        String baseDir = file.getAbsolutePath();
        String file_path = baseDir+"/screen/";
        System.out.println("pic tmp file path: " +file_path);
        //截图截完一次, 翻滚一次
        for(List<Integer> rectangle:rectangles){
            if( previous.size()!=0 ){
                ((JavascriptExecutor)driver).executeScript(js_scroll+"("+ rectangle.get(0) +","+ rectangle.get(1) +" )");
                System.out.println("Scrolled To" +"("+ rectangle.get(0) +","+ rectangle.get(1) +" )");
                Thread.sleep(1000);
            }
            String file_name = "part_"+part+".png";
            System.out.println("Capturing..."+file_name);
            File filetmps = new File(file_path+file_name);
            //如果part==0,截图顶部功能栏,这里其实 都是对 scroll这个元素进行截图
            if(part == 0){
                //指定了OutputType.FILE做为参数传递给getScreenshotAs()方法,其含义是将截取的屏幕以文件形式返回。
                File srcFile =  scroll_top.getScreenshotAs(OutputType.FILE);
                //利用FileUtils工具类的copyFile()方法保存getScreenshotAs()返回的文件对象。
                FileUtils.copyFile(srcFile,filetmps);
                String jq1 = "$('.navbar').remove()";
                String jq2 = "$('.scroll-canvas--dashboard').css('height','100%')";
                ((JavascriptExecutor)driver).executeScript(jq1);
                System.out.println("取消顶部栏显示");
                ((JavascriptExecutor)driver).executeScript(jq2);
                System.out.println("取消底部高度栏显示");
                Thread.sleep(1000);
            }else{
                File srcFile = scroll_content.getScreenshotAs(OutputType.FILE);
                FileUtils.copyFile(srcFile,filetmps);
                Thread.sleep(1000);
            }
            Image screenshot = ImageIO.read(new File(file_path+file_name));
            if (rectangle.get(1) + viewport_height > total_height) {
                offset = new int[]{rectangle.get(0), (total_height - viewport_height)};
            }else{
                offset = new int[]{rectangle.get(0), rectangle.get(1)};
            }
            System.out.println("Adding to stitched image with offset " +offset[0]+","+offset[1]);
            // 将图片贴到 原始图上
            Graphics g =stitched_image.getGraphics();
            g.drawImage(screenshot,offset[0],offset[1],null);
            g.dispose();
            boolean flag = filetmps.exists();
            if (flag) {
                 System.out.println("tmp图片存在,删除");
                filetmps.delete();
            }else{
                System.out.println("tmp图片不存在");
            }
            part = part + 1;
            previous = rectangle;
        }
        // 最终生成的图片
        ImageIO.write(stitched_image,"png", new File(file_path +file_pic));
        System.out.println("Finishing chrome full page screenshot workaround...");
        return true;
    }
	
	//这个是发送邮件啦,简单
    public static void send_email(File file_name, String mailUser, String mailPassword, String[] mailReceiver, String mailServer)
            throws UnsupportedEncodingException, MessagingException {
        Date d = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        Properties properties = new Properties();
        //1.创建邮件对象
        properties.put("mail.smtp.host", mailServer);// 指定SMTP服务器
        properties.put("mail.smtp.auth", "true"); // 指定是否需要SMTP验证
        Session session = Session.getInstance(properties);
        MimeMessage message =new MimeMessage(session);
        //2.设置发件人
        // InternetAddress 的三个参数分别为: 邮箱, 显示的昵称(只用于显示, 没有特别的要求), 昵称的字符集编码
        message.setFrom(new InternetAddress(mailUser,mailUser,"UTF-8"));
        //3.设置多个收件人
        //To收件人   CC 抄送  BCC密送
        InternetAddress[] sendTo = new InternetAddress[mailReceiver.length];
        for (int i=0; i<mailReceiver.length; i++){
            System.out.println("发送到:"+ mailReceiver[i]);
            sendTo[i] = new InternetAddress(mailReceiver[i]);
        }
        message.setRecipients(MimeMessage.RecipientType.TO, sendTo);
        //收件人单人方式
        //message.setRecipient(MimeMessage.RecipientType.TO,new InternetAddress(mailReceiver,"xin","UTF-8"));
        // message.addRecipient(MimeMessage.RecipientType.CC,new InternetAddress("409****1@qq.com","xinyuezi","UTF-8"));
        //4.设置主题
        message.setSubject("截图" ,"UTF-8");
        //message.setContent("Test Content:这是一封测试邮件...","text/html;charset=UTF-8");

        //一个Multipart对象包含一个或多个bodypart对象,组成邮件正文
        MimeMultipart multipart = new MimeMultipart();
        //读取本地图片,将图片数据添加到"节点"
        MimeBodyPart image = new MimeBodyPart();
        DataHandler dataHandler1 = new DataHandler(new FileDataSource(file_name));
        image.setDataHandler(dataHandler1);
        image.setContentID("image_screen");
        //创建文本节点
        MimeBodyPart text = new MimeBodyPart();
        text.setContent("<font color=black>截图时间: "+sdf.format(d)+"<br></font><img src='cid:image_screen'/>","text/html;charset=UTF-8");

        // 附件, 暂时用不到
        //创建附件节点  读取本地文件,并读取附件名称
//        MimeBodyPart file1 = new MimeBodyPart();
//        DataHandler dataHandler2 = new DataHandler(new FileDataSource("C:\\Users\\Public\\Pictures\\Sample Pictures\\growing.png"));
//        file1.setDataHandler(dataHandler2);
//        file1.setFileName(MimeUtility.encodeText(dataHandler2.getName()));
//
//        MimeBodyPart file2 = new MimeBodyPart();
//        DataHandler dataHandler3 = new DataHandler(new FileDataSource("E:\\Tang\\topics.xlsx"));
//        file2.setDataHandler(dataHandler3);
//        file2.setFileName(MimeUtility.encodeText(dataHandler3.getName()));

        //将文本和图片添加到multipart
        multipart.addBodyPart(text);
        multipart.addBodyPart(image);
        multipart.setSubType("mixed");//混合关系
        message.setContent(multipart);
        message.saveChanges();
        Transport transport = session.getTransport("smtp");
        transport.connect(mailUser, mailPassword);
        transport.sendMessage(message,message.getAllRecipients());
        transport.close();
    }

}

##引用
1: https://www.cnblogs.com/MasterMonkInTemple/p/9970512.html
2: https://blog.csdn.net/xinyuezitang/article/details/85768462
3: https://www.cnblogs.com/chbyiming-bky/articles/8940105.html
4: https://www.iteye.com/blog/lijingshou-2019323

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值