前言:最近折腾了一个QQ机器人,突然有个灵感。当用户发送一个网页链接时,我想使用Java对网页进行截图,然后将截图文件发回到QQ上,感觉这个功能很酷炫,于是昨天(2020-10-29)研究了一晚上,下面对截图功能这个部分进行总结。
一. 概述
首先我在网上查询了相关资料(并不多),最常见的两种方案:
使用 Java 自带的 Robot
类,对电脑屏幕进行截图,不建议使用。
使用 Selenium
工具,对游览器进行截图。
由于最后想要运行在树莓派上(ARM 32位),遇到了不少问题。
受限于树莓派zero的性能,生成一张截图可能要30s左右。
二. 准备与安装
方案一:使用Java自带Robot类
只需要 JDK1.6
及以上版本即可。
方案二:使用Selenium
第一步:
安装 chrome
游览器:
1 2 3 # Linux 上建议使用chromium sudo apt install chromium-browser # Win和Mac可直接在官网下载
在 chrome
游览器中,或者命令行执行 chromium-browser --version
查看版本号
并在下面的链接中下载相应版本号的 chromedriver
,解压后即可使用。
chromedriver
下载地址:http://npm.taobao.org/mirrors/chromedriver/
注意:如果你使用的是32位的Linux(例如树莓派)或者没有找到相应的版本号,建议使用命令去安装:
1 2 3 4 # 安装chromedriver sudo apt-get install chromium-chromedriver # 查看chromedriver的安装位置 dpkg -L chromium-chromedriver
第二步:
添加 commons-io
与 selenium
依赖,以 Maven 为例。
1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > org.seleniumhq.selenium</groupId > <artifactId > selenium-java</artifactId > <version > 3.141.59</version > </dependency > <dependency > <groupId > commons-io</groupId > <artifactId > commons-io</artifactId > <version > 2.8.0</version > </dependency >
三. 代码实现
方案一:使用Java自带Robot类
1 2 3 4 5 6 7 8 9 10 11 12 13 Desktop.getDesktop().browse(new URL(url).toURI()); Robot robot = new Robot(); Dimension d = new Dimension(Toolkit.getDefaultToolkit().getScreenSize()); int width = (int ) d.getWidth();int height = (int ) d.getHeight();robot.keyRelease(KeyEvent.VK_F11); Image image = robot.createScreenCapture(new Rectangle(0 , 0 , width, height)); BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = bi.createGraphics(); g.drawImage(image, 0 , 0 , width, height, null ); ImageIO.write(bi, "jpg" , new File("/Users/ming/Desktop/temp/" + System.currentTimeMillis()+".jpg" ));
方案二:使用Selenium
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public void screenshot (String url) throws IOException { System.setProperty("webdriver.chrome.driver" , "/usr/bin/chromedriver" ); ChromeOptions options = new ChromeOptions(); options.addArguments("--headless" ); options.addArguments("--disable-infobars" ); options.addArguments("--disable-extensions" ); options.addArguments("--disable-gpu" ); options.addArguments("--no-sandbox" ); options.addArguments("--disable-dev-shm-usage" ); options.addArguments("--hide-scrollbars" ); options.addArguments("--window-size=375,190" ); WebDriver driver = new ChromeDriver(options); driver.get(url); File srcFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE); FileUtils.copyFile(srcFile, new File("/home/project/screenshot.png" )); driver.quit(); }
注意:如果在树莓派上运行,要按照上面的参数去设置,否则会报一些奇奇怪怪的错误。
如果想对页面的 特定区域 进行截图呢?请往下继续看。
四. Selenium中花里胡哨的操作
元素定位
首先,Selenium提供定位元素的方法:driver.findElement()
方式
示例
根据Id查找
driver.findElement(By.id (“id”))
根据name查找
driver.findElement(By.name (“name”))
根据链接的全部文本查找
driver.findElement(By.linkText(“linkText”))
根据链接的部分文本查找
driver.findElement(By.partialLinkText(“partialLinkText”))
根据ClassName查找
driver.findElement(By.className(“className”))
根据tagName查找
driver.findElement(By.tagName(“tagName”))
根据xpath查找
driver.findElement(By.xpath(“xpath”))
表单输入与提交
除此之外,还可以模拟在游览器中输入表单并提交
1 2 3 driver.findElement(By.name("firstname" )).sendKeys("Lei" ); driver.findElement(By.name("lastname" )).sendKeys("Hou" ); driver.findElement(By.id("submit" )).click();
截图指定区域
下面直接上代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public File elementSnapshot (WebElement element) throws Exception { File screen = ((TakesScreenshot) this .driver).getScreenshotAs(OutputType.FILE); BufferedImage image = ImageIO.read(screen); int width = element.getSize().getWidth(); int height = element.getSize().getHeight(); Rectangle rect = new Rectangle(width, height); Point p = element.getLocation(); int w = rect.width; int h = rect.height; int x = p.getX(); int y = p.getY(); int w_driver = 1920 ; int h_driver = 1080 ; System.out.println("width:" + w); System.out.println("height:" + h); System.out.println("x:" + x); System.out.println("y:" + y); System.out.println("y+height:" + (y + h)); System.out.println("x+width:" + (x + w)); try { if (y + h > h_driver) { h = h - (y + h - h_driver); System.out.println("修改后的height:" + h); System.out.println("修改后的y+height:" + (y + h)); } if (x + w > w_driver) { w = x - (x + w - w_driver); System.out.println("修改后的width:" + w); System.out.println("修改后的x+width:" + (x + w)); } BufferedImage img = image.getSubimage(x, y, w, h); ImageIO.write(img, "png" , screen); System.out.println("Screenshot By element success" ); } catch (IOException e) { e.printStackTrace(); } return screen; }
那么传进去的参数: WebElement
怎么获取呢?
1 2 3 4 driver.get(url); WebElement element = driver.findElement(By.className("pyqBox" ));
上述代码是通过元素定位的方法来截图,非常方便。