Linux环境下Selenium webdriver.Remote出现SSL错误,macOS正常求助
问题背景
我最近在爬取一个第三方网站,一开始用Beautiful Soup搞不定对方复杂的认证系统,转用了Selenium。脚本基于Selenium 3.12.0驱动和selenium-server-standalone-3.12.0.jar,Linux和macOS机器都运行JDK 1.8.0_172。
结果遇到个头疼的问题:
- 把Selenium服务器部署在macOS上时,不管在Linux还是本地跑脚本都能正常工作;
- 但把服务器移到Linux上后,不管在哪跑脚本,都会卡在加载jQuery的步骤,抛出SSL握手错误:
18:28:02.453 ERROR [DefaultJavaScriptErrorListener.loadScriptError] - Error loading JavaScript from [https://ww6.fltplan.com/js/jquery-1.12.3.min.js]. javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1002) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
目标网站代码本身比较老旧,可能有不合理的实现,而且这套配置几周前在Linux上还好好的,突然就失效了,不得不重新调试。
我的连接代码如下:
capabilities = { 'browserName': 'htmlunit', 'version': 'firefox', 'platform': 'ANY', 'javascriptEnabled': True, 'acceptInsecureCerts': True, } driver = webdriver.Remote( # "http://192.168.1.46:4444/wd/hub", webdriver.DesiredCapabilities.HTMLUNITWITHJS # "http://localhost:4444/wd/hub", webdriver.DesiredCapabilities.HTMLUNITWITHJS "http://localhost:4444/wd/hub", capabilities ) driver.get("https://www.fltplan.com/")
注释里的第一个地址是macOS上的服务器,能正常运行;后面两个是Linux上的配置,都失败了,其中第二个几周前还能正常使用。
排查思路与解决方法
1. 检查Linux服务器的SSL证书信任情况
HTMLUnitDriver依赖JVM的SSL信任库,Linux上的JDK可能没把目标网站的证书加入信任列表,或者证书链出了问题。我之前遇到过类似的情况,你可以试试:
- 先导出目标网站的SSL证书,然后导入到Linux服务器的JDK信任库(路径一般是
$JAVA_HOME/jre/lib/security/cacerts) - 用keytool命令导入,默认密码是
changeit:keytool -import -alias fltplan -keystore $JAVA_HOME/jre/lib/security/cacerts -file /path/to/your/downloaded/cert.crt
2. 给HTMLUnitDriver添加更明确的SSL配置
虽然你已经设置了acceptInsecureCerts,但HTMLUnit有时候需要更明确的SSL参数。可以试试修改capabilities,指定TLS版本并强制忽略SSL错误:
capabilities = { 'browserName': 'htmlunit', 'version': 'firefox', 'platform': 'ANY', 'javascriptEnabled': True, 'acceptInsecureCerts': True, 'sslProtocol': 'TLSv1.2', # 目标网站可能不支持旧协议,指定TLS1.2试试 'ignoreSslErrors': True }
3. 排查Linux服务器的网络问题
先确认Linux服务器能不能正常访问那个jQuery文件,用curl测试最直接:
curl -v https://ww6.fltplan.com/js/jquery-1.12.3.min.js
如果curl也报SSL错误,那就是服务器网络或者系统SSL配置的问题,和Selenium无关。还要检查是不是防火墙、代理拦截了443端口的出站请求,或者服务器的SSL套件配置有问题。
4. 升级Selenium相关依赖
Selenium 3.12.0搭配的HTMLUnit版本可能比较旧,对新的SSL规则支持不好。可以试试升级到Selenium 3的最后稳定版3.141.59,这个版本的兼容性要好很多,说不定能解决这个问题。
5. 换用真实浏览器的无头模式
HTMLUnit毕竟是模拟浏览器,对JS和SSL的支持不如真实浏览器。如果上面的方法都不行,可以试试在Linux服务器上部署ChromeDriver或者GeckoDriver,用headless模式跑:
from selenium.webdriver.chrome.options import Options chrome_options = Options() chrome_options.add_argument("--headless") chrome_options.add_argument("--disable-gpu") chrome_options.add_argument("--no-sandbox") # Linux环境下需要加这个参数 capabilities = chrome_options.to_capabilities() driver = webdriver.Remote("http://localhost:4444/wd/hub", capabilities) driver.get("https://www.fltplan.com/")
我自己爬复杂网站的时候,这种方式比HTMLUnit靠谱多了,很少遇到奇怪的JS或SSL问题。
内容的提问来源于stack exchange,提问作者Paul Tomblin

