基于selenium与firefox的爬虫实现方案

Posted 黑六

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于selenium与firefox的爬虫实现方案相关的知识,希望对你有一定的参考价值。

百度百科对selenium的定义: Selenium是一个用于Web应用程序测试的工具。

所以爬虫在将selenium应用到生产环境的时候会遇到一些问题,以下是我在使用的时候遇到的问题以及解决方案,针对这些问题我也开源了一个selenium的使用工具,该工具是基于selenium-java的封装, 地址:https://gitee.com/wangyelou/S...

一、 浏览器频繁启动关闭性能消耗大

解决方案: 创建浏览器对象池管理浏览器,用完浏览器后不立即销毁,而是放入池中供下次调用,为了防止浏览器运行时间长而导致的一些意想不到的情况,可以每隔一段时间对浏览器进行重启操作。

二、 selenium没有提供在线自动切换代理的方法

selenium代理设置是在浏览器创建的时候配置的,对于正在运行的浏览器selnenium并没有切换代理的方法

解决方案: firefox支持在about:config页面使用js切换代理, selenium可以跳转到该页面然后执行相关js代码,从而实现在线切换代理

// js在 about:config 页面设置firefox代理
var prefs = Components.classes[\'@mozilla.org/preferences-service;1\'].getService(Components.interfaces.nsIPrefBranch);
prefs.setIntPref(\'network.proxy.type\', 1);
prefs.setCharPref(\'network.proxy.http\', \'127.0.0.1\');
prefs.setIntPref(\'network.proxy.http_port\', 1234);
prefs.setCharPref(\'network.proxy.ssl\', \'127.0.0.1\');
prefs.setIntPref(\'network.proxy.ssl_port\', 1234);

三、代理认证问题

  • 方案1: 基于firefox 60 以下版本, selenium可以获取弹窗对象,输入用户名和密码,再点击确认按钮
  • 方案2:利用browsermob-proxy做代理转发,在browsermob-proxy层做代理认证

    BrowserMobProxyServer bmpServer = new BrowserMobProxyServer();
    
    bmpServer.setChainedProxyManager(new ChainedProxyManager() {
        public void lookupChainedProxies(HttpRequest httpRequest, Queue<ChainedProxy> chainedProxies) {
            chainedProxies.add(new ChainedProxyAdapter() {
                @Override
                public InetSocketAddress getChainedProxyAddress() {
                    return new InetSocketAddress("127.0.0.1", 7000);
                }
    
                @Override
                public void filterRequest(HttpObject httpObject) {
                    if (httpObject instanceof HttpRequest) {
                        HttpHeaders.addHeader((HttpRequest)httpObject, HttpHeaders.Names.PROXY_AUTHORIZATION, "Basic " + BrowserMobHttpUtil.base64EncodeBasicCredentials("username", "password"));
                    }
                }
            });
        }
    });
    
    bmpServer.setTrustAllServers(true);
    bmpServer.start(888);
  • 方案3:基于firefox api制作插件, 在浏览器层面做代理认证
    https://developer.mozilla.org...

    function callbackFn1(details) {
        console.log(username);
        return {
            authCredentials: {
                username: "username",
                password: "password"
            }
        };
    }
     
    chrome.webRequest.onAuthRequired.addListener(
            callbackFn1,
            {urls: ["<all_urls>"]},
            [\'blocking\']
    );
  • 方案3中firefox 加载插件问题:

    • 加载的插件必须经过官方的签名,否则只能使用插件调试功能
    • selenium 以临时插件的方式加载的(重写firefoxwebdriver 方法)

          public String installExtension(Path path) {
              return (String) execute(INSTALL_EXTENSION,
                      ImmutableMap.of("path", path.toAbsolutePath().toString(),
                              "temporary", true)).getValue();
          }

四、 兼容网页和文件下载

文件下载会直接下载到本地目录,这时通过getPageSource()无法获取相关内容

解决方案:每个浏览器设置自己唯一的下载目录,当访问完网页后,检测下载目录是否有文件,有则代表是下载文件

selenium设置文件自动下载

surrogate.addPreference("browser.download.folderList", 2);
surrogate.addPreference("browser.download.dir", setupConfig.getDownDir(uid));
surrogate.addPreference("browser.download.useDownloadDir", true);
surrogate.addPreference("browser.download.manager.showWhenStarting", false);
surrogate.addPreference("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream, application/x-001...");

五、 窗口错误

当当前窗口关闭时,selenium无法感知到, 这时如果对页面进行操作会抛出NoSuchWindow异常

解决方案:重写execute方法

while (true) {
    try {
        return super.execute(driverCommand, parameters);
    } catch (NoSuchWindowException e) {
        String s = getWindowHandles().toArray()[0].toString();
        switchTo().window(s);
        System.out.println(s);
        parameters = ImmutableMap.of("handle", s);
    }
}

六、保留标签页可能会导致cpu长时间占用

在访问完网页之后,如果不关闭对应的标签页,那么浏览器可能会保持较高的cpu消耗

解决方案:访问完网页之后,关闭标签页,只保留一个浏览器空白标签页

七、firefox隐藏 window.navigator.webdriver

  1. firefox 88以下版本,firefox提供配置参数

    profile.addPreference("dom.webdriver.enabled", false)
  2. firefox 88以上版本

    可以利用以下js实现屏蔽
    Object.defineProperties(navigator, {webdriver:{get:()=>undefined}});

但是如果在页面加载完再利用selenium执行js,是起不到效果的,因为网站加载完就会执行相关js,获取到window.navigator.webdriver的值

所以需要拦截相关js请求,在每个js返回内容前加上上面js,达到效果。具体可以利用browsermob-proxy实现。

以上是关于基于selenium与firefox的爬虫实现方案的主要内容,如果未能解决你的问题,请参考以下文章

python爬虫使用Selenium的准备工作之二:

[Python爬虫] Selenium实现自动登录163邮箱和Locating Elements介绍

爬虫入门Selenium用法简介

Python爬虫之selenium的使用

如何用python模拟点击onclick

python爬虫之Selenium