更具体地说,同时在单个 WebDriver/WebElement 上执行多个操作是否安全?即像这样
WebDriver driver; //driver initialized somehow
final WebElement elem = driver.findElement(By.cssSelector("#elementID"));
//simplified for example, but in real code I'd be storing the results of these calls
new Thread() {
@Override
public void run() {
elem.isDisplayed();
}
}.run();
new Thread() {
@Override
public void run() {
elem.isEnabled();
}
}.run();
我自己尝试过在本地交互时没有出现任何问题,但在对远程硒网格执行相同的操作时遇到间歇性问题。
我不确定我遇到的问题是否来自 Selenium 本身,或者 Selenium 是否良好并且这是我正在使用的托管网格提供商的限制。 使用Python进行抓取时,selenium线程安全吗?提到selenium可能不是线程安全的,但我找不到任何确认。
这个问题有答案这里
“WebDriver 不是线程安全的。话虽如此,如果您可以序列化对底层驱动程序实例的访问,则可以在多个线程中共享引用。这是不可取的。另一方面,您/可以/实例化一个每个线程的 WebDriver 实例。”
您可以使用选项卡管理使其线程安全。每个线程都有自己的选项卡,当您使用完毕后,请关闭该选项卡。所有涉及导航的区域都应该同步。
/**
*
* @author michaelassraf
*/
@Component
public class ChromePDFExporter {
protected Logger logger = Utils.getInstance().getLogger(ChromePDFExporter.class);
@Autowired
RestfulClient restfulClient;
@Value(PropertiesFieldNames.ChromePDFExporterLocalPortNumber)
Integer chromePDFExporterLocalPortNumber;
@Value(PropertiesFieldNames.TempDir)
File tempDir;
private ChromeDriver chromeDriver;
String chromeDriverOriginalWindowHandle = null;
public synchronized ChromeDriver buildChromeClient() {
try {
logger.info("<< ChromePDFExporter is starting >>");
ChromeOptions options = getChromeOptions(chromePDFExporterLocalPortNumber);
ChromeDriverService service = new ChromeDriverService.Builder()
.withAppendLog(true)
.withVerbose(true)
.withLogLevel(ChromiumDriverLogLevel.ALL)
.withLogFile(
new File(tempDir.getAbsolutePath() + File.separator + ChromeDriver.class.getSimpleName() + "LogFile.log"))
.withLogOutput(new FileOutputStream(
new File(tempDir.getAbsolutePath() + File.separator + ChromeDriver.class.getSimpleName() + "LogOutput.log")))
.withAllowedListIps(Utils.getInstance().getMachinePrivateIP(restfulClient))
.usingPort(chromePDFExporterLocalPortNumber)
.build();
logger.info("<< ChromePDFExporter local ip is " + Utils.getInstance().getMachinePrivateIP(restfulClient) + " >>");
chromeDriver = new ChromeDriver(service, options);
chromeDriverOriginalWindowHandle = chromeDriver.getWindowHandle();
logger.info("<< ChromePDFExporter Launched " + chromeDriver.getSessionId() + " >>");
} catch (Throwable th) {
logger.error("ChromePDFExporter - Can't launch Chrome Driver.", th);
}
return chromeDriver;
}
private ChromeOptions getChromeOptions(Integer port) {
ChromeOptions options = new ChromeOptions();
options.addArguments("--no-sandbox"); // Bypass OS security model.
options.setExperimentalOption("useAutomationExtension", false);
options.addArguments("--headless");
options.addArguments("--disable-popup-blocking");
options.addArguments("--disable-setuid-sandbox");
options.addArguments("--enable-automation");
options.addArguments("--disable-browser-side-navigation");
options.addArguments("--disable-blink-features=AutomationControlled");
options.addArguments("--disable-web-security");
options.addArguments("--remote-allow-origins=*");
options.addArguments("--wait-for-all-loading");
options.addArguments("--remote-debugging-pipe");
options.addArguments("--incognito");
options.addArguments("--guest");
options.addArguments("--run-all-compositor-stages-before-draw");
options.addArguments("disable-infobars"); // disabling infobars
options.addArguments("--disable-extensions"); // disabling extensions
options.addArguments("--disable-gpu"); // applicable to windows os only
options.addArguments("--disable-dev-shm-usage"); // overcome limited resource problems
options.addArguments("--remote-debugging-port=" + port);
options.addArguments("--no-first-run");
options.addArguments("--no-default-browser-check");
options.addArguments("--disable-infobars");
options.addArguments("--user-data-dir=" + tempDir.getAbsolutePath() + File.separator + ChromeOptions.class.getSimpleName() + "Data");
options.addArguments("--profile-directory=" + tempDir.getAbsolutePath() + File.separator + ChromeOptions.class.getSimpleName() + "Profile");
Map<String, Object> prefs = new HashMap<String, Object>();
prefs.put("profile.default_content_settings.popups", false);
prefs.put("distribution.skip_first_run_ui", true);
prefs.put("signin.allowed_on_next_startup", false);
prefs.put("safebrowsing.enabled", false);
options.setExperimentalOption("prefs", prefs);
return options;
}
ChromeDriver getChromeDriver(String navigateTo) {
boolean shouldReconnect = chromeDriver == null;
checkIfAlive:
{
if (shouldReconnect) {
break checkIfAlive;
}
try {
chromeDriver.switchTo().window(navigateTo == null ? chromeDriverOriginalWindowHandle : navigateTo);
chromeDriver.getCurrentUrl();
} catch (Throwable th) {
shouldReconnect = true;
}
}
if (shouldReconnect) {
buildChromeClient();
}
return chromeDriver;
}
@Autowired
ReportComponentHTMLBlocks reportComponentHTMLBlocks;
@Autowired
Base64Encryption base64Encryption;
HashSet<String> processedComponents = new HashSet<>();
Object synchronizer = new Object();
boolean checkIfAtLeastOneComponentWasNotProcessed(HashSet<String> components) {
for (String component : components) {
if (!processedComponents.contains(component)) {
processedComponents.addAll(components);
return true;
}
}
return false;
}
public File exportFromPDFUsingChrome(File htmlFile,
String targeFile,
boolean addGenericFooterAndHeader,
String injectCustomFooter,
String organizationName,
String reportTemplateName,
HashSet<String> components) {
String url = "file://" + htmlFile.getAbsolutePath();
String tabName = null;
File pdfFile = new File(targeFile);
synchronized (synchronizer) {
((JavascriptExecutor) getChromeDriver(null)).executeScript("window.open()");
ArrayList<String> tabs = new ArrayList<>(getChromeDriver(null).getWindowHandles());
tabName = tabs.get(tabs.size() - 1);
getChromeDriver(tabName).get(url);
}
Map<String, Object> params = new HashMap();
params.put("headerLeft", 0);
params.put("headerRight", 0);
Double spaceForFooter = addGenericFooterAndHeader ? 1.0 : 0;
spaceForFooter = injectCustomFooter != null ? 5.0 : spaceForFooter;
Double spaceForHeader = addGenericFooterAndHeader ? 1.0 : 0;
params.put("headerBottom", spaceForFooter);
params.put("headerTop", spaceForHeader);
params.put("marginLeft", 0);
params.put("marginRight", 0);
params.put("marginBottom", spaceForFooter);
params.put("marginTop", spaceForHeader);
params.put("printBackground", true);
params.put("transferMode", "ReturnAsBase64");
params.put("displayHeaderFooter", addGenericFooterAndHeader || injectCustomFooter != null);
if (addGenericFooterAndHeader) {
params.put("headerTemplate", reportComponentHTMLBlocks
.getChromePDFExporterHeader(organizationName, reportTemplateName));
params.put("footerTemplate", reportComponentHTMLBlocks
.getChromePDFExporterFooter(20));
}
if (injectCustomFooter != null) {
params.put("footerTemplate", injectCustomFooter);
}
String command = "Page.printToPDF";
Map<String, Object> output = null;
boolean keepRunning = true;
while (keepRunning) {
workaroundForChromeExportBug:
{
synchronized (synchronizer) {
output = getChromeDriver(tabName).executeCdpCommand(command, params);
}
boolean containsOneUnprocessedCompenent = checkIfAtLeastOneComponentWasNotProcessed(components);
keepRunning = containsOneUnprocessedCompenent;
if (!containsOneUnprocessedCompenent) {
break workaroundForChromeExportBug;
}
logger.info("First run of Chrome, running export retry.");
synchronized (synchronizer) {
getChromeDriver(tabName).navigate().refresh();
}
try {
Thread.sleep(100l);
} catch (Throwable th) {
}
}
}
Utils.getInstance().deleteDirectory(pdfFile.getAbsolutePath());
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(pdfFile.getAbsolutePath());
} catch (Throwable th) {
logger.error("Can't create file output stream.", th);
}
byte[] byteArray = base64Encryption.decodeToByteArray((String) output.get("data"));
try {
fileOutputStream.write(byteArray);
} catch (Throwable th) {
logger.error("Can't write file input stream.", th);
}
try {
fileOutputStream.close();
} catch (Throwable th) {
logger.error("Can't close file outpute stream.", th);
}
synchronized (synchronizer) {
((JavascriptExecutor) getChromeDriver(tabName)).executeScript("window.close();");
}
return pdfFile;
}
@PreDestroy
public void destroyChromeDriverThreadLocal() {
if (chromeDriver == null) {
return;
}
chromeDriver.close();
chromeDriver.quit();
}
}