地球人都知道,若是使用selenIUm时要修改user-agent可以在启动浏览器时添加设置项,如chromeOptions.addArguments("user-agent=xxx");。然则若何在每次请求的时刻动态更改user-agent呢?

经由我的不懈努力,终于在网上找到一个相关的信息使用python3和selenium4修改chrome的user-agent。
这内里提到了使用driver.execute_cdp_cmd来切换,这让我了解了一下cdp下令。简朴的来说,cdp下令时chrome支持的一种基于websocket的协议,通过这个协议可以与浏览器内核通讯。平时使用的F12浏览器开发工具就是基于cdp的。cpd下令可以实现的功效许多,可以参考Chrome DevTools Protocol。再这内里我找到了一个Network.setUserAgentOverride下令可以修改请求user-agent。
下令的参数如下:

然则,在我的项目中现在使用的selenium-java的版本是3.141.59,这个版本还没用提供对于cdp下令的支持,前面信息中提到了是在selenium4中使用使用的cdp下令。于是我又去maven堆栈搜索有没有selenium4的jar包可以用。

这内里已经有5个alpha测试的版本了,虽然还不是稳固版本,然则为了实现新功效先试一试,在maven中添加依赖:

<!-- https://MVnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependeNCy>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>4.0.0-alpha-5</version>
</dependency>

然后实验挪用ChromeDriverexecuteCdPCoMMAnd方式

可以看到,第一个参数是commandName,对于修改user-agent的需求,这个地方应该填写Network.setUserAgentOverride,后面是参数的键值对,现在只需要填写userAgent就可以了,其他都是不需要的可选参数。

一样平常情况下,问题到这里就解决了,然则在我的项目中却没有这么简朴。由于种种缘故原由,我的项目中使用的是Selenium-Server来提供浏览器环境的,也就是说,建立的都是RemoteWebDriver,虽然ChromeDriver是继续自RemoteWebDriver的,然则cdp下令是chrome浏览器独占的, 因此RemoteWebDriver也就没有提供相关的支持了。那么若何在确定RemoteWebDriver挪用chrome浏览器的情况下提供cpd下令的支持呢?为了实现这个功效照样费了一些时间,因此在这里把历程记录下来。
首先看一下ChromeWebDriver是若何实现cdp下令的:

pUBLic Map<String, Object> executeCdpCommand(String commandName, Map<String, Object> parameters) {
    Objects.requireNonNull(commandName, "Command name must be set.");
    Objects.requireNonNull(parameters, "Parameters for command must be set.");
    Map<String, Object> toReturn = (Map)this.getExecuteMethod().execute("executeCdpCommand", ImmutableMap.of("cmd", commandName, "params", parameters));
    return ImmutableMap.copyOf(toReturn);
}

在Selenium4中,ChromeDriver继续自ChroMiumDriver,二者其实是一模一样的。ChromiumDriver提供了cdp下令的支持,行使executeMethod运行下令executeCdpCommand,将要运行的详细下令和参数一并传入。于是我又最先找这个ExecuteMethod是什么器械,发现ChromiumWebDriver并没有对这个参数举行任何设置,因此应该是在ChromiumDriver继续的RemoteWebDriver来设置的。果真,在RemoteWebDriver中有this.executeMethod = new RemoteExecuteMethod(this);,在ChromiumWebDriver中获取到的一定也是这个工具。那么很容易想到,继续一个RemoteWebDriver并编写一个方式挪用这个executeMethod不就行了吗?

public class CdpRemoteWebDriver extends RemoteWebDriver {

    public CdpRemoteWebDriver(URL remoteAddress, Capabilities capabilities) {
        super(remoteAddress, capabilities);
    }

    public Map<String, Object> executeCdpCommand(String commandName, Map<String, Object> parameters) {
        Objects.requireNonNull(commandName, "Command name must be set.");
        Objects.requireNonNull(parameters, "Parameters for command must be set.");
        Map<String, Object> toReturn = (Map)this.getExecuteMethod().execute("executeCdpCommand", ImmutableMap.of("cmd", commandName, "params", parameters));
        return ImmutableMap.copyOf(toReturn);
    }
    
}

然后再建立CdpRemoteWebDriver实例,在接见网页之前设置user-agent

Map uaMap = new HashMap(){{
    put("userAgent", "customUserAgent");
}};
((CdpRemoteWebDriver) driver).executeCdpCommand("Network.setUserAgentOverride",
        uaMap
        );
driver.get(url);

运行试一下!

org.openqa.selenium.UnsupporteDCommandException: executeCdpCommand
Build info: version: '4.0.0-alpha-5', revision: 'b3a0d621cc'
System info: host: 'DESKTOP-BM176Q1', ip: '192.168.137.1', os.name: 'Windows 10', os.arch: 'amd64', os.version: '10.0', java.version: '1.8.0_161'
Driver info: driver.version: CdpRemoteWebDriver
	at org.openqa.selenium.remote.codec.AbstractHttpCommandCodec.encode(AbstractHttpCommandCodec.java:246)
	at org.openqa.selenium.remote.codec.AbstractHttpCommandCodec.encode(AbstractHttpCommandCodec.java:129)
	at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:155)
	at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:582)
	at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:639)
	at org.openqa.selenium.remote.RemoteExecuteMethod.execute(RemoteExecuteMethod.java:36)
	at com.zju.edu.eagle.accessibilitycheck.a11ycheck.executor.impl.CdpRemoteWebDriver.executeCdpCommand(CdpRemoteWebDriver.java:24)

效果不行,说executeCdpCommand是不支持的下令,为什么一样的executeMethod效果不一样呢?定位到错误的地方看一下

public HttpRequest encode(Command command) {
    String name = (String)this.aliases.getOrDefault(command.getName(), command.getName());
    AbstractHttpCommandCodec.CommandSpec spec = (AbstractHttpCommandCodec.CommandSpec)this.nameToSpec.get(name);
    if (spec == null) {
        throw new UnsupportedCommandException(command.getName());
    }
    ...
}

运行下令时,先从(AbstractHttpCommandCodec.CommandSpec)this.nameToSpec中获取下令的相关信息了,而要运行的executeCdpCommand没有事先界说,以是就出现异常了。

public AbstractHttpCommandCodec() {
    this.defineCommand("status", get("/status"));
    this.defineCommand("getAllSessions", get("/sessions"));
    this.defineCommand("Newsession", post("/session"));
    this.defineCommand("getCapabilities", get("/session/:sessionId"));
    ...
}

这些下令是在AbstractHttpCommandCodec中界说的,而executeCdpCommand不在其中。这说明虽然ChromeDriver和RemoteWebDriver有相同的executeMethod,但后续挪用照样涉及到了差别的类,于是我又转头查看ChromeDriver中的代码,发现有这样一个组织函数

public ChromeDriver(ChromeDriverService service, Capabilities capabilities) {
    super(new ChromiumDriverCommandExecutor(service), capabilities, "goog:chromeOptions");
}

这内里建立了一个ChromiumDriverCommandExecutor,再点进来看一下

static {
    CHROME_COMMAND_NAME_TO_URL.put("launchApp", new CommandInfo("/session/:sessionId/chromium/launch_app", HttpMethod.POST));
    CHROME_COMMAND_NAME_TO_URL.put("getNetworkConditions", new CommandInfo("/session/:sessionId/chromium/network_conditions", HttpMethod.GET));
    CHROME_COMMAND_NAME_TO_URL.put("setNetworkConditions", new CommandInfo("/session/:sessionId/chromium/network_conditions", HttpMethod.POST));
    CHROME_COMMAND_NAME_TO_URL.put("deleteNetworkConditions", new CommandInfo("/session/:sessionId/chromium/network_conditions", HttpMethod.DELETE));
    CHROME_COMMAND_NAME_TO_URL.put("executeCdpCommand", new CommandInfo("/session/:sessionId/goog/cdp/execute", HttpMethod.POST));
    CHROME_COMMAND_NAME_TO_URL.put("getCastSinks", new CommandInfo("/session/:sessionId/goog/cast/get_sinks", HttpMethod.GET));
    CHROME_COMMAND_NAME_TO_URL.put("selectCastSink", new CommandInfo("/session/:sessionId/goog/cast/set_sink_to_use", HttpMethod.POST));
    CHROME_COMMAND_NAME_TO_URL.put("startCastTabMirroring", new CommandInfo("/session/:sessionId/goog/cast/start_tab_mirroring", HttpMethod.POST));
    CHROME_COMMAND_NAME_TO_URL.put("getCastIssueMessage", new CommandInfo("/session/:sessionId/goog/cast/get_issue_message", HttpMethod.GET));
    CHROME_COMMAND_NAME_TO_URL.put("stopCasting", new CommandInfo("/session/:sessionId/goog/cast/stop_casting", HttpMethod.POST));
    CHROME_COMMAND_NAME_TO_URL.put("setPermission", new CommandInfo("/session/:sessionId/permissions", HttpMethod.POST));
}

可以看到这内里也界说了一些下令,executeCdpCommand也在其中。而RemoteWebDriver没有这个下令的信息,自然也就无法执行了。经由进一步查看源码,我发现ChromiumDriverCommandExecutorHttpCommandExecutor的子类,HttpCommandExecutor是RemoteWebDriver中真正的下令执行者。

ChromeWebDriver能够提供自界说的CommandExecutor来增添分外下令,自然我们自己继续的类也可以。在HttpCommandExecutor中有这样一个组织函数HttpCommandExecutor(Map<String, CommandInfo> additionalCommands, URL addressOfRemoteServer),只要把添加的下令的键值对传入,就可以支持分外的下令了。

最终版本的代码如下

public class CdpRemoteWebDriver extends RemoteWebDriver {

    private static final HashMap<String, CommandInfo> CHROME_COMMAND_NAME_TO_URL = new HashMap();

    public CdpRemoteWebDriver(URL remoteAddress, Capabilities capabilities) {
        super((CommandExecutor)(new HttpCommandExecutor(ImmutableMap.copyOf(CHROME_COMMAND_NAME_TO_URL), remoteAddress)), capabilities);
    }

    public Map<String, Object> executeCdpCommand(String commandName, Map<String, Object> parameters) {
        Objects.requireNonNull(commandName, "Command name must be set.");
        Objects.requireNonNull(parameters, "Parameters for command must be set.");
        Map<String, Object> toReturn = (Map)this.getExecuteMethod().execute("executeCdpCommand", ImmutableMap.of("cmd", commandName, "params", parameters));
        return ImmutableMap.copyOf(toReturn);
    }

    static {
        CHROME_COMMAND_NAME_TO_URL.put("launchApp", new CommandInfo("/session/:sessionId/chromium/launch_app", HttpMethod.POST));
        CHROME_COMMAND_NAME_TO_URL.put("getNetworkConditions", new CommandInfo("/session/:sessionId/chromium/network_conditions", HttpMethod.GET));
        CHROME_COMMAND_NAME_TO_URL.put("setNetworkConditions", new CommandInfo("/session/:sessionId/chromium/network_conditions", HttpMethod.POST));
        CHROME_COMMAND_NAME_TO_URL.put("deleteNetworkConditions", new CommandInfo("/session/:sessionId/chromium/network_conditions", HttpMethod.DELETE));
        CHROME_COMMAND_NAME_TO_URL.put("executeCdpCommand", new CommandInfo("/session/:sessionId/goog/cdp/execute", HttpMethod.POST));
        CHROME_COMMAND_NAME_TO_URL.put("getCastSinks", new CommandInfo("/session/:sessionId/goog/cast/get_sinks", HttpMethod.GET));
        CHROME_COMMAND_NAME_TO_URL.put("selectCastSink", new CommandInfo("/session/:sessionId/goog/cast/set_sink_to_use", HttpMethod.POST));
        CHROME_COMMAND_NAME_TO_URL.put("startCastTabMirroring", new CommandInfo("/session/:sessionId/goog/cast/start_tab_mirroring", HttpMethod.POST));
        CHROME_COMMAND_NAME_TO_URL.put("getCastIssueMessage", new CommandInfo("/session/:sessionId/goog/cast/get_issue_message", HttpMethod.GET));
        CHROME_COMMAND_NAME_TO_URL.put("stopCasting", new CommandInfo("/session/:sessionId/goog/cast/stop_casting", HttpMethod.POST));
        CHROME_COMMAND_NAME_TO_URL.put("setPermission", new CommandInfo("/session/:sessionId/permissions", HttpMethod.POST));
    }
    
}

再测试一下效果

可以看到user-agent已经被乐成替换了。

总结一下解决问题的流程

  1. 继续RemoteWebDriver类
  2. 参考ChromeDriver实现executeCdpCommand方式
  3. 参考ChromeDriver建立自界说的commandExecutor增添下令

事实上,由于cdp下令是chrome浏览器提供的支持,与selenium无关,在selenium4中只是内置了这个下令的参数和地址,挪用的原理与原来支持的方式是一样的。在自己实现的CdpRemoteWebDriver中已经自己添加了参数,并不需要将依赖升级到4.0.0就可以挪用cdp下令了。

,

Allbet<

www.xaks68887722.com欢迎进入欧博开户平台(Allbet Gaming),欧博开户平台开放欧博(Allbet)开户、欧博(Allbet)代理开户、欧博(Allbet)电脑客户端、欧博(Allbet)APP下载等业务

|
发布评论

分享到:

聊城在线:回收站清空后的文件怎么恢复
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。