当前位置: 首页 > 知识库问答 >
问题:

用含硒的Chrome进行全页截图

帅雅逸
2023-03-14

我知道这在以前是不可能的,但现在有了以下更新:

现在有可能在Java中使用Selenium吗?

共有1个答案

乜坚成
2023-03-14

要在Java中使用Selenium Webdriver实现这一点需要做一些工作…正如Florent B所暗示的,我们需要改变缺省ChromeDriver使用的一些类来实现这一功能。首先,我们需要创建一个新的DriverCommandExecutor,它添加了新的Chrome命令:

import com.google.common.collect.ImmutableMap;
import org.openqa.selenium.remote.CommandInfo;
import org.openqa.selenium.remote.http.HttpMethod;
import org.openqa.selenium.remote.service.DriverCommandExecutor;
import org.openqa.selenium.remote.service.DriverService;

public class MyChromeDriverCommandExecutor extends DriverCommandExecutor {
    private static final ImmutableMap<String, CommandInfo> CHROME_COMMAND_NAME_TO_URL;

    public MyChromeDriverCommandExecutor(DriverService service) {
        super(service, CHROME_COMMAND_NAME_TO_URL);
    }

    static {
        CHROME_COMMAND_NAME_TO_URL = ImmutableMap.of("launchApp", new CommandInfo("/session/:sessionId/chromium/launch_app", HttpMethod.POST)
        , "sendCommandWithResult", new CommandInfo("/session/:sessionId/chromium/send_command_and_get_result", HttpMethod.POST)
        );
    }
}

之后,我们需要创建一个新的ChromeDriver类,然后它将使用这个东西。我们需要创建类,因为原始的类没有构造函数来替换命令执行器······因此新类变成:

import com.google.common.collect.ImmutableMap;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.html5.LocalStorage;
import org.openqa.selenium.html5.Location;
import org.openqa.selenium.html5.LocationContext;
import org.openqa.selenium.html5.SessionStorage;
import org.openqa.selenium.html5.WebStorage;
import org.openqa.selenium.interactions.HasTouchScreen;
import org.openqa.selenium.interactions.TouchScreen;
import org.openqa.selenium.mobile.NetworkConnection;
import org.openqa.selenium.remote.FileDetector;
import org.openqa.selenium.remote.RemoteTouchScreen;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.html5.RemoteLocationContext;
import org.openqa.selenium.remote.html5.RemoteWebStorage;
import org.openqa.selenium.remote.mobile.RemoteNetworkConnection;

public class MyChromeDriver  extends RemoteWebDriver implements LocationContext, WebStorage, HasTouchScreen, NetworkConnection {
    private RemoteLocationContext locationContext;
    private RemoteWebStorage webStorage;
    private TouchScreen touchScreen;
    private RemoteNetworkConnection networkConnection;

    //public MyChromeDriver() {
    //  this(ChromeDriverService.createDefaultService(), new ChromeOptions());
    //}
    //
    //public MyChromeDriver(ChromeDriverService service) {
    //  this(service, new ChromeOptions());
    //}

    public MyChromeDriver(Capabilities capabilities) {
        this(ChromeDriverService.createDefaultService(), capabilities);
    }

    //public MyChromeDriver(ChromeOptions options) {
    //  this(ChromeDriverService.createDefaultService(), options);
    //}

    public MyChromeDriver(ChromeDriverService service, Capabilities capabilities) {
        super(new MyChromeDriverCommandExecutor(service), capabilities);
        this.locationContext = new RemoteLocationContext(this.getExecuteMethod());
        this.webStorage = new RemoteWebStorage(this.getExecuteMethod());
        this.touchScreen = new RemoteTouchScreen(this.getExecuteMethod());
        this.networkConnection = new RemoteNetworkConnection(this.getExecuteMethod());
    }

    @Override
    public void setFileDetector(FileDetector detector) {
        throw new WebDriverException("Setting the file detector only works on remote webdriver instances obtained via RemoteWebDriver");
    }

    @Override
    public LocalStorage getLocalStorage() {
        return this.webStorage.getLocalStorage();
    }

    @Override
    public SessionStorage getSessionStorage() {
        return this.webStorage.getSessionStorage();
    }

    @Override
    public Location location() {
        return this.locationContext.location();
    }

    @Override
    public void setLocation(Location location) {
        this.locationContext.setLocation(location);
    }

    @Override
    public TouchScreen getTouch() {
        return this.touchScreen;
    }

    @Override
    public ConnectionType getNetworkConnection() {
        return this.networkConnection.getNetworkConnection();
    }

    @Override
    public ConnectionType setNetworkConnection(ConnectionType type) {
        return this.networkConnection.setNetworkConnection(type);
    }

    public void launchApp(String id) {
        this.execute("launchApp", ImmutableMap.of("id", id));
    }
}

这主要是原始类的副本,但是禁用了一些构造函数(因为所需的一些代码是包私有的)。如果需要这些构造函数,必须将类放在包org.openqa.selenium.chrome中。

import com.google.common.collect.ImmutableMap;
import org.openqa.selenium.remote.Command;
import org.openqa.selenium.remote.Response;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class ChromeExtender {
    @Nonnull
    private MyChromeDriver m_wd;

    public ChromeExtender(@Nonnull MyChromeDriver wd) {
        m_wd = wd;
    }

    public void takeScreenshot(@Nonnull File output) throws Exception {
        Object visibleSize = evaluate("({x:0,y:0,width:window.innerWidth,height:window.innerHeight})");
        Long visibleW = jsonValue(visibleSize, "result.value.width", Long.class);
        Long visibleH = jsonValue(visibleSize, "result.value.height", Long.class);

        Object contentSize = send("Page.getLayoutMetrics", new HashMap<>());
        Long cw = jsonValue(contentSize, "contentSize.width", Long.class);
        Long ch = jsonValue(contentSize, "contentSize.height", Long.class);

        /*
         * In chrome 61, delivered one day after I wrote this comment, the method forceViewport was removed.
         * I commented it out here with the if(false), and hopefully wrote a working alternative in the else 8-/
         */
        if(false) {
            send("Emulation.setVisibleSize", ImmutableMap.of("width", cw, "height", ch));
            send("Emulation.forceViewport", ImmutableMap.of("x", Long.valueOf(0), "y", Long.valueOf(0), "scale", Long.valueOf(1)));
        } else {
            send("Emulation.setDeviceMetricsOverride",
                ImmutableMap.of("width", cw, "height", ch, "deviceScaleFactor", Long.valueOf(1), "mobile", Boolean.FALSE, "fitWindow", Boolean.FALSE)
            );
            send("Emulation.setVisibleSize", ImmutableMap.of("width", cw, "height", ch));
        }

        Object value = send("Page.captureScreenshot", ImmutableMap.of("format", "png", "fromSurface", Boolean.TRUE));

        // Since chrome 61 this call has disappeared too; it does not seem to be necessary anymore with the new code.
        // send("Emulation.resetViewport", ImmutableMap.of()); 
        send("Emulation.setVisibleSize", ImmutableMap.of("x", Long.valueOf(0), "y", Long.valueOf(0), "width", visibleW, "height", visibleH));

        String image = jsonValue(value, "data", String.class);
        byte[] bytes = Base64.getDecoder().decode(image);

        try(FileOutputStream fos = new FileOutputStream(output)) {
            fos.write(bytes);
        }
    }

    @Nonnull
    private Object evaluate(@Nonnull String script) throws IOException {
        Map<String, Object> param = new HashMap<>();
        param.put("returnByValue", Boolean.TRUE);
        param.put("expression", script);

        return send("Runtime.evaluate", param);
    }

    @Nonnull
    private Object send(@Nonnull String cmd, @Nonnull Map<String, Object> params) throws IOException {
        Map<String, Object> exe = ImmutableMap.of("cmd", cmd, "params", params);
        Command xc = new Command(m_wd.getSessionId(), "sendCommandWithResult", exe);
        Response response = m_wd.getCommandExecutor().execute(xc);

        Object value = response.getValue();
        if(response.getStatus() == null || response.getStatus().intValue() != 0) {
            //System.out.println("resp: " + response);
            throw new MyChromeDriverException("Command '" + cmd + "' failed: " + value);
        }
        if(null == value)
            throw new MyChromeDriverException("Null response value to command '" + cmd + "'");
        //System.out.println("resp: " + value);
        return value;
    }

    @Nullable
    static private <T> T jsonValue(@Nonnull Object map, @Nonnull String path, @Nonnull Class<T> type) {
        String[] segs = path.split("\\.");
        Object current = map;
        for(String name: segs) {
            Map<String, Object> cm = (Map<String, Object>) current;
            Object o = cm.get(name);
            if(null == o)
                return null;
            current = o;
        }
        return (T) current;
    }
}

这使您可以使用指定的命令,并创建一个文件,其中包含png格式的图像。当然,您也可以通过对字节使用imageio.read()直接创建BufferedImage。

 类似资料:
  • 在尝试了各种方法之后...我偶然发现了这个页面,用chromedriver、selenium和Python进行了全页截图。 原始代码在这里。(我在下面的帖子中复制了代码) 它使用PIL,它的工作很好!然而,有一个问题...也就是说,它捕获固定的标题和整个页面的重复,并且在页面更改期间遗漏页面的某些部分。获取截图的示例url: Util.Py

  • 这篇帖子和这篇有关: Python selenium屏幕捕获未获取整个页面 但这似乎不是一个通用的解决方案,它要复杂得多。)

  • 是否可以在Python中使用chromedriver和selenium对元素进行截图。我知道chrome开发工具允许我们对特定元素进行截图。即使是JavaScript方法,我也可以使用命令获取数据URI。

  • 我正试图使用selenium和chromedriver进行全页截图,但我得到的是这一半截图。我尝试过其他方法,但到目前为止,只有这一个工作,它只需要半页截图。 任何人都可以用另一个技巧来修复这个问题,也可以附加输出结果。

  • 参考这个提问的方法,会报错跨域 https://segmentfault.com/q/1010000020779040

  • 我在一个基于Oracle Linux的Docker中用headless chrome运行了一个测试用例。 我请求的页面应该被重定向到一个带有username password字段的身份验证页面,但我在docker服务器()中得到的是空白页。这在我的MacBook上很好用。 这是当我尝试从docker内部直接使用chrome浏览器运行相同的输出 **[0108/134327.972921:错误:ce