说一下chrome和firefox的基础配置,首先
chromedriver和chrome的版本对应关系看 :https://www.cnblogs.com/reform999/p/10685946.html
firefox和geckodriver的对应关系: https://firefox-source-docs.mozilla.org/testing/geckodriver/Support.html
chromedriver的下载地址在: http://npm.taobao.org/mirrors/chromedriver/
geckodriver的下载地址是: https://github.com/mozilla/geckodriver/releases
chrome和火狐的下载地址就不说了,百度一下,官网那个就是。
下载对应版本的驱动和浏览器后:
java:
去 maven仓库 里找对应版本的依赖,记住千万别忘了 guava这个依赖。不然你可能会经历 找不到方法,找不到类,等等一系列各种各样奇怪的问题。
配置好selenium的依赖和驱动以及浏览器(浏览器的安装地址建议不要变动,让它自己安装就行了)后,设置系统变量,也就是把程序要的东西(驱动的地址),放到系统变量里,它会从系统变量里取。
//chrome:
System.setProperty("webdriver.chrome.driver", "chromedriver.exe的路径");
//firefox:
System.setProperty("webdriver.gecko.driver", "geckodriver.exe的路径");
//如果浏览器安装地址变了
//chrome:
System.setProperty("webdriver.chrome.bin","chrome安装地址,精确到chrome.exe");
//firefox:
System.setProperty("webdriver.firefox.bin","firefox安装地址,精确到firefox.exe")
这段代码在new WebDriver之前执行,现在可以试试:
public static void main(String[] args) {
System.setProperty("webdriver.chrome.driver", "chromedriver.exe的路径");
ChromeDriver driver = null;
try{
driver = new ChromeDriver();
driver.get("http://www.baidu.com");
((JavascriptExecutor) driver).executeScript("document.body.style.fontSize = '30px';document.body.innerHTML='hello world~
5秒后页面关闭 ';setInterval(()=>{let time=document.getElementById('close');time.innerText=(time.innerText-1)},1000);");
Thread.sleep(5000L);
}catch(Exception e){
e.printStackTrace();
}finally{
//养成良好的习惯,用完后quit掉。
if(null!=driver){
driver.quit();
}
}
}
python:
pip install selenium
然后就能用了(驱动和浏览器还是要下载的)。。
if __name__ == '__main__':
driver = None
try:
driver = webdriver.Chrome(executable_path='chromedriver.exe的路径')
driver.get("http://www.baidu.com")
driver.execute_script("document.body.style.fontSize = '30px';document.body.innerHTML='hello world~
5秒后页面关闭 ';setInterval(()=>{let time=document.getElementById('close');time.innerText=(time.innerText-1)},1000);")
time.sleep(5)
except Exception as e:
print(e)
finally:
# 养成好习惯,用完后quit掉
driver.quit()
该模式在chrome老版本可用,79+版本navigator.webdriver仍为true。(新版本消除navigator.webdriver方法参考目录上的2020.07.23(79+版本消除webdriver特征)
ChromeOptions options = new ChromeOptions();
options.setExperimentalOption("excludeSwitches", Collections.singletonList("enable-automation"));
WebDriver driver = new ChromeDriver(options);
目的是让chrome打开时加载浏览器插件,所以要先生成浏览器插件。
由于没有sock5代理,sock5的认证代理没法测试,这里用的是http代理,公司亲测此种方法可行,用了1年多。
private static ChromeOptions setProxyChrome(Proxy proxy) throws Exception{
ChromeOptions options = new ChromeOptions();
if(null==proxy||StringUtils.isEmpty(proxy.getIp())){
return options;
}
FileWriter fileWriter1 = null;
FileWriter fileWriter2 = null;
String driverPath = getDriverPath();
String jsonFilePath = driverPath + "manifest.json";
String jsFilePath = driverPath + "background.js";
String zipFilePath = driverPath + "proxy.zip";
File jsonFile = new File(jsonFilePath);
File zipFile = new File(zipFilePath);
File jsFile = new File(jsFilePath);
try {
String js = "var config={mode:\"fixed_servers\",rules:{singleProxy:{scheme:\"http\",host:\"" + proxy.getIp() + "\",port:" + proxy.getPort() + "},bypassList:[\"\"]}};chrome.proxy.settings.set({value:config,scope:\"regular\"},function(){});function callbackFn(details){return{authCredentials:{username:\"" + proxy.getProxyUser() + "\",password:\"" + proxy.getProxyPass() + "\"}};}chrome.webRequest.onAuthRequired.addListener(callbackFn,{urls:[\"\"]},['blocking']);" ;
String json = "{\"version\":\"1.0.0\",\"manifest_version\":2,\"name\":\"Chrome Proxy\",\"permissions\":[\"proxy\",\"tabs\",\"unlimitedStorage\",\"storage\",\"\",\"webRequest\",\"webRequestBlocking\"],\"background\":{\"scripts\":[\"background.js\"]},\"minimum_chrome_version\":\"22.0.0\"}" ;
if(!jsFile.exists()){
jsFile.createNewFile();
}
if(!jsonFile.exists()){
jsonFile.createNewFile();
}
fileWriter1 = new FileWriter(jsFilePath);
fileWriter2 = new FileWriter(jsonFilePath);
fileWriter1.write(js);
fileWriter2.write(json);
}catch (Exception e){
e.printStackTrace();
}finally {
try{
if(null!=fileWriter1){
fileWriter1.flush();
fileWriter1.close();
}
if(null!=fileWriter2){
fileWriter2.flush();
fileWriter2.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
try {
File[] files = new File[]{
jsFile, jsonFile};
zipFile(files, zipFile);
options.addExtensions(zipFile);
}catch (Exception e){
e.printStackTrace();
}
return options;
}
private static void zipFile(File[] srcFiles, File zipFile){
if (!zipFile.exists()) {
try {
zipFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FileOutputStream fileOutputStream = null;
ZipOutputStream zipOutputStream = null;
FileInputStream fileInputStream = null;
try {
fileOutputStream = new FileOutputStream(zipFile);
zipOutputStream = new ZipOutputStream(fileOutputStream);
ZipEntry zipEntry = null;
for (int i = 0; i < srcFiles.length; i++) {
fileInputStream = new FileInputStream(srcFiles[i]);
zipEntry = new ZipEntry(srcFiles[i].getName());
zipOutputStream.putNextEntry(zipEntry);
int len;
byte[] buffer = new byte[1024];
while ((len = fileInputStream.read(buffer)) > 0) {
zipOutputStream.write(buffer, 0, len);
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try{
if(null!=zipOutputStream){
zipOutputStream.close();
}
if(null!=fileOutputStream){
fileOutputStream.close();
}
if(null!=fileInputStream){
fileInputStream.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
这里参考了python控制已经打开chrome的做法,具体做法参考:
https://www.cnblogs.com/lovealways/p/9813059.html
java代码:
ChromeOptions options = new ChromeOptions();
options.setExperimentalOption("debuggerAddress","127.0.0.1:9222");
ChromeDriver driver = new ChromeDriver(options);
更新,开发者模式在79版本以前能用,详情点击这里,参考这个问答,79版本以后消除navigator.webdriver使用
ChromeOptions options = new ChromeOptions();
options.addArguments("--disable-blink-features=AutomationControlled");
WebDriver driver = new ChromeDriver(options);
这里再增加几个常用的参数 (chrome全部参数看另一篇文章, chromeOptions和chromePrefs的官方参数)
关于个人用户文件夹地址:
chrome:地址栏输入 chrome://version/ 个人资料路径 最后一个文件夹之前的就是。
firefox:地址栏输入 about:profiles 正在使用的那个就是 一般默认default-release
java:
//下载配置
HashMap<String, Object> chromePrefs = new HashMap<>();
chromePrefs.put("download.default_directory", "文件下载路径");
chromePrefs.put("profile.default_content_settings.popups", 0);//下载不弹框
chromePrefs.put("profile.default_content_setting_values.images", 2);//禁止显示图片
chromePrefs.put("browser.download.useDownloadDir", true);//使用用户下载文件夹
chromePrefs.put("profile.default_content_setting_values.automatic_downloads",1);//多文件下载总是允许
//消除特征配置
ChromeOptions options = new ChromeOptions();
options.addArguments("--disable-blink-features=AutomationControlled");
//使用平时打开的用户数据文件夹打开chrome(这样有些网站不用登录,有些网站会记录密码,而且配合消除特性的参数,大概率不会触发滑块)
options.addArguments("--user-data-dir=C:\\Users\\Administrator\\AppData\\Local\\Google\\Chrome\\User Data");
options.setExperimentalOption("prefs", chromePrefs);
python:
options = webdriver.ChromeOptions()
# 下载配置
prefs = {
"download.default_directory": "文件下载路径",
"profile.default_content_settings.popups": 0,
"profile.default_content_setting_values.images": 2,
"browser.download.useDownloadDir": true,
"profile.default_content_setting_values.automatic_downloads":1
}
options.add_experimental_option("prefs", prefs)
# 设置用户数据文件夹 chrome打开时加载的文件夹
options.add_argument("--user-data-dir=C:\\Users\\Administrator\\AppData\\Local\\Google\\Chrome\\User Data")
# 消除特征
options.add_argument("--disable-blink-features=AutomationControlled")
driver = webdriver.Chrome(chrome_options = options)
增加几个火狐的参数:
final FirefoxOptions fopts = new FirefoxOptions();
ProfilesIni allProfiles = new ProfilesIni();
/**
* 获取默认文件夹的方法:
* 打开常用的火狐,地址栏输入about:profiles ,回车 ,找到正在使用的配置文件 后面的default,default-release就是用户配置文件夹
*/
FirefoxProfile firefoxProfile = allProfiles.getProfile("用户文件夹");//默认default
firefoxProfile.setPreference("dom.webdriver.enabled", false);//消除navigator.webdriver
firefoxProfile.setPreference("browser.download.folderList", 2);//下载到用户配置文件夹
firefoxProfile.setPreference("browser.download.manager.showWhenStarting",false);//下载是否弹框
firefoxProfile.setPreference("browser.download.dir", "下载文件夹路径");
firefoxProfile.setPreference("permissions.default.image", 2);//禁用图片
fopts.setProfile(firefoxProfile);
WebDriver driver = new FirefoxDriver(fopts);
以下内容来自 https://blog.csdn.net/qq_37931379/article/details/104595450 (仅供参考)
FirefoxOptions options = new FirefoxOptions();
//声明一个profile对象
FirefoxProfile profile = new FirefoxProfile();
//设置Firefox的“broswer.download.folderList”属性为2
/**
* 如果没有进行设定,则使用默认值 1,表示下载文件保存在“下载”文件夹中
* 设定为0,则下载文件会被保存在用户的桌面上
* 设定为2,则下载的文件会被保存的用户指定的文件夹中
*/
profile.setPreference("browser.download.folderList", 2);
//browser.download.manager.showWhenStarting的属性默认值为true
//设定为 true , 则在用户启动下载时显示Firefox浏览器的文件下载窗口
//设定为false,则在用户启动下载时不显示Firefox浏览器的文件下载窗口
profile.setPreference("browser.download.manager.showWhenStarting", false);
//设定文件保存的目录
profile.setPreference("browser.download.dir", downloadFilePath);
//browser.helperApps.neverAsk.openFile 表示直接打开下载文件,不显示确认框
//默认值.exe类型的文件,"application/excel"表示Excel类型的文件
// application/x-msdownload
profile.setPreference("browser.helperApps.neverAsk.openFile", "application/x-msdownload");
//browser.helperApps.never.saveToDisk 设置是否直接保存 下载文件到磁盘中默认值为空字符串,厦航代码行设定了多种温江的MIME类型
profile.setPreference("browser.helperApps.neverAsk.saveToDisk", "application/x-msdownload");
//browser.helperApps.alwaysAsk.force 针对位置的MIME类型文件会弹出窗口让用户处理,默认值为true ,设定为false 表示不会记录打开未知MIME类型文件
profile.setPreference("browser.helperApps.alwaysAsk.force", true);
//下载.exe文件弹出窗口警告,默认值是true ,设定为false 则不会弹出警告框
profile.setPreference("browser.download.manager.alertOnEXEOpen", false);
//browser.download.manager.focusWhenStarting设定下载框在下载时会获取焦点
profile.setPreference("browser.download.manager.focusWhenStarting", true);
//browser.download.manager.useWindow 设定下载是否现在下载框,默认值为true,设定为false 会把下载框隐藏
profile.setPreference("browser.download.manager.useWindow", false);
//browser.download.manager.showAlertOnComplete 设定下载文件结束后是否显示下载完成的提示框,默认值为true,
//设定为false表示下载完成后,现在下载完成提示框
profile.setPreference("browser.download.manager.showAlertOnComplete", false);
//browser.download.manager.closeWhenDone 设定下载结束后是否自动关闭下载框,默认值为true 设定为false 表示不关闭下载管理器
profile.setPreference("browser.download.manager.closeWhenDone", false);
增加参考文章:https://www.cnblogs.com/wpjamer/articles/3873769.html
下面记录几个java关于selenium的异常:
/**未配置系统变量,即chromedriver.exe的位置
* 配置方法如下:(最上面有详细的说明)
* chrome:
* System.setProperty("webdriver.chrome.driver", "chromedriver.exe的位置");
* firefox:
* System.setProperty("webdriver.gecko.driver", "geckodriver.exe的位置");
* phantomjs:
* System.setProperty("phantomjs.binary.path", "phantomjs.exe的位置");
*/
java.lang.IllegalStateException: The path to the driver executable must be set by the webdriver.chrome.driver system property;
/**
* 这个出现的情况有很多,举个例子,如果你使用driver.get("www.baidu.com");就会出现这个报错,因为这个地址不对,要使用https://www.baidu.com这个完整路径。
*/
org.openqa.selenium.InvalidArgumentException: invalid argument
/**
* 这个是比较常见的错误,比如打开百度首页后,直接
* driver.findElements(By.id("testest"));
* 就会报这个错误,因为页面上没有这个元素。
* 首先反思一下选择器有没有写错,浏览器console先试试能不能拿到对应的元素,浏览器能拿到,但是selenium拿不到的话,
* 看一下是不是元素在iframe内,在iframe的情况,selenium是要切换句柄的。
* 然后看一下是不是页面或者元素需要某个操作(比如点击)才会出现,或者你刚点击,元素还没加载出来就开始获取了。
* 还有 不要直接获取这个元素。
* 正确的做法是:先判断有没有这个元素,如果没有,执行没有的逻辑(等待,刷新,或者抛出异常等等)
* 一般来说,在获取这个元素之前,都会先用
* new WebDriverWait(driver, 5).until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(cssSelector)));
* 这个方法的意思是5秒内元素不出来就不往下走,5秒后还没出来就抛出异常
*/
org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {
"method":"css selector","selector":"xxx"}
/**
* 要点击或者操作的元素已经不在当前页面的环境内了,例如打开一个网站后,获取了一个select下的option列表
* 循环点击这个option列表,但是每点击一次,页面就会跳转到新的页面,那么在第二次循环的时候就会报这个异常,也就是说,你要操作的元素是你上一个页面的(或者已经消失的)元素,你在这个页面想点击一个不存在,但是你恰好还有WebElement实例的元素,就会报这个错。
*/
org.openqa.selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document
/**
* 当前元素不可交互,比如说,点击的元素不在页面可见范围内,如果是点击操作,可能点击上方还有别的元素挡住
* 了,也可能是当前元素不可点击。或者给一个disabled属性的input进行sendKeys操作。
* 一般来说,点击都是通过执行js来实现的,但是执行js不一定能真正点击(具体原因还不清楚),sendKeys有时候会比执行js来的好用。
*/
org.openqa.selenium.ElementNotInteractableException: element not interactable
/**
* 类似这种报错,有几种情况,用户文件夹被占用,chrome版本和chromedriver版本对不上
*/
org.openqa.selenium.InvalidArgumentException: invalid argument: user data directory is already in use, please specify a unique value for --user-data-dir argument, or don't use --user-data-dir
remote stacktrace: Backtrace:
Ordinal0 [0x007FC013+3194899]
Ordinal0 [0x006E6021+2056225]
Ordinal0 [0x0057F608+587272]
Ordinal0 [0x004FF28A+62090]
Ordinal0 [0x004FC64A+50762]
Ordinal0 [0x00521EE9+204521]
Ordinal0 [0x00521D0D+204045]
Ordinal0 [0x0051FC1B+195611]
Ordinal0 [0x00503B7F+80767]
Ordinal0 [0x00504B4E+84814]
Ordinal0 [0x00504AD9+84697]
Ordinal0 [0x006FCE64+2149988]
GetHandleVerifier [0x0096BE95+1400773]
GetHandleVerifier [0x0096BB61+1399953]
GetHandleVerifier [0x009731FA+1430314]
GetHandleVerifier [0x0096C69F+1402831]
Ordinal0 [0x006F3D61+2112865]
Ordinal0 [0x006FE5CB+2155979]
Ordinal0 [0x006FE6F5+2156277]
Ordinal0 [0x0070F26E+2224750]
BaseThreadInitThunk [0x76A56359+25]
RtlGetAppContainerNamedObjectPath [0x77127C24+228]
RtlGetAppContainerNamedObjectPath [0x77127BF4+180]
划到页面底部:
System.setProperty("webdriver.chrome.driver", "E://drivers//chromedriver.exe");
WebDriver driver = null;
try {
driver = new ChromeDriver();
driver.get("https://blog.csdn.net/weixin_42525191/article/details/105216417");
Actions actions = new Actions(driver);
Thread.sleep(2000);
//看效果记得打断点。
actions.sendKeys(Keys.END).perform(); //方法一
Thread.sleep(2000);
((JavascriptExecutor) driver).executeScript("window.scrollTo(0,document.body.scrollHeight)");//方法二
}catch (Exception e){
e.printStackTrace();
}finally {
if(null!=driver){
driver.quit();
}
}
工具方法(java):
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.*;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.remote.Augmenter;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
/**
* selenium 工具方法
*/
public class WebDriverUtils {
/**
* 等待元素出现时间
*/
private static final long WAIT_TIME_SECONDS = 5;
/**
* 阿里代码手册,不允许任何魔法值直接出现在方法体中
* 获取input值的value属性
*/
private static final String VALUE_ATTR = "value";
/**
* 延迟按键的基本睡眠时间
*/
private static final Long SLEEP_TIME = 100L;
/**
* 延迟按键的随机睡眠时间
*/
private static final Long SLEEP_RANDOM_TIME = 100L;
/**
* 用css选择option
* @param driver 驱动
* @param cssSelector css选择器
* @param value 值
* @return 返回执行结果
*/
public static Object setValueByCssSelector(WebDriver driver, String cssSelector, String value) {
String varName = "current_ele_" + System.currentTimeMillis();
String script = "var " + varName + " = document.querySelector('" + cssSelector + "');" + varName + ".value='"
+ value + "';" + varName + ".dispatchEvent(new UIEvent('change'));" + varName
+ ".dispatchEvent(new InputEvent('input'));";
return ((JavascriptExecutor) driver).executeScript(script);
}
/**
* 获取单个元素
* @param driver 元素
* @param cssSelector css选择器
* @return 返回元素
*/
public static WebElement getEle(WebDriver driver, String cssSelector){
if(null==driver || StringUtils.isEmpty(cssSelector)){
throw new RuntimeException("驱动或者css选择为空");
}
try{
new WebDriverWait(driver, WAIT_TIME_SECONDS).until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(cssSelector)));
return driver.findElement(By.cssSelector(cssSelector));
}catch (Exception e){
throw new RuntimeException(e);
}
}
/**
* 延迟按键
* @param driver 驱动
* @param cssSelector css选择器
* @param keys 待输入文本
*/
public static void sendKeys(WebDriver driver, String cssSelector, String keys){
if(null == driver || StringUtils.isEmpty(cssSelector) || StringUtils.isEmpty(keys)){
throw new RuntimeException("驱动或者css选择器,或者待输入值为空");
}
if(!judgeEle(driver, cssSelector)){
throw new RuntimeException("元素不存在");
}
WebElement ele = getEle(driver, cssSelector);
sendKeys(ele, keys);
}
/**
* 延迟按键
* @param ele 待输入元素
* @param keys 待输入文本
*/
public static void sendKeys(WebElement ele, String keys){
if(null == ele || StringUtils.isEmpty(keys)){
throw new RuntimeException("待输入元素为空,或者待输入值为空");
}
ele.clear();
int length = keys.length();
for (int i = 0; i < length; i++) {
char tempChar = keys.charAt(i);
ele.sendKeys(String.valueOf(tempChar));
sleep((int)(Math.floor(Math.random()*SLEEP_RANDOM_TIME)+SLEEP_TIME));
}
}
/**
* 获取元素内部的元素
* @param ele 元素
* @param cssSelector css选择器
* @return 返回元素
*/
public static WebElement getEle(WebElement ele, String cssSelector){
if(null==ele || StringUtils.isEmpty(cssSelector)){
throw new RuntimeException("驱动或者css选择为空");
}
try{
return ele.findElement(By.cssSelector(cssSelector));
}catch (Exception e){
throw new RuntimeException(e);
}
}
/**
* 获取元素列表
* @param driver 驱动
* @param cssSelector css选择器
* @return 返回元素列表
*/
public static List<WebElement> getElements(WebDriver driver, String cssSelector){
if(null==driver || StringUtils.isEmpty(cssSelector)){
throw new RuntimeException("驱动或者css选择为空");
}
try{
new WebDriverWait(driver, WAIT_TIME_SECONDS).until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(cssSelector)));
return driver.findElements(By.cssSelector(cssSelector));
}catch (Exception e){
throw new RuntimeException(e);
}
}
/**
* 获取元素内部的元素列表
* @param element 元素
* @param cssSelector css选择器
* @return 返回元素列表
*/
public static List<WebElement> getElements(WebElement element, String cssSelector){
if(null==element || StringUtils.isEmpty(cssSelector)){
throw new RuntimeException("元素或者css选择为空");
}
try{
return element.findElements(By.cssSelector(cssSelector));
}catch (Exception e){
throw new RuntimeException(e);
}
}
/**
* 根据css选择器单击元素
* @param driver 驱动
* @param cssSelector css选择器
* @return 返回是否点击成功
*/
public static boolean clickEle(WebDriver driver, String cssSelector){
if(null==driver || StringUtils.isEmpty(cssSelector)){
return false;
}
try{
new WebDriverWait(driver, WAIT_TIME_SECONDS).until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(cssSelector)));
((JavascriptExecutor)driver).executeScript("arguments[0].click()", driver.findElement(By.cssSelector(cssSelector)));
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据元素单击元素
* @param driver 驱动
* @param ele 元素
* @return 返回是否点击成功
*/
public static boolean clickEle(WebDriver driver, WebElement ele){
if(null == driver || null==ele){
return false;
}
try{
((JavascriptExecutor)driver).executeScript("arguments[0].click()", ele);
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据css选择器双击元素
* @param driver 驱动
* @param cssSelector css选择器
* @return 返回是否点击成功
*/
public static boolean doubleClickEle(WebDriver driver, String cssSelector){
if(null==driver || StringUtils.isEmpty(cssSelector)){
return false;
}
try{
new WebDriverWait(driver, WAIT_TIME_SECONDS).until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(cssSelector)));
Actions actions = new Actions(driver);
actions.doubleClick(driver.findElement(By.cssSelector(cssSelector))).perform();
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据元素双击元素
* @param driver 驱动
* @param element 元素
* @return 返回是否点击成功
*/
public static boolean doubleClickEle(WebDriver driver, WebElement element){
if(null==driver || null == element){
return false;
}
try{
Actions actions = new Actions(driver);
actions.doubleClick(element).perform();
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}
/**
* 判断元素是否存在,传驱动
* @param driver 驱动
* @param cssSelector css选择器
* @return 返回是否存在
*/
public static boolean judgeEle(WebDriver driver, String cssSelector){
if(null==driver || StringUtils.isEmpty(cssSelector)){
return false;
}
try{
new WebDriverWait(driver, 2*WAIT_TIME_SECONDS).until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(cssSelector)));
}catch (Exception e){
return false;
}
return true;
}
/**
* 判断元素内部是否存在某元素
* @param ele 元素
* @param cssSelector css选择器
* @return 返回是否存在
*/
public static boolean judgeEle(WebElement ele, String cssSelector){
if(null==ele || StringUtils.isEmpty(cssSelector)){
return false;
}
try{
ele.findElement(By.cssSelector(cssSelector));
return true;
}catch (Exception e){
return false;
}
}
/**
* 根据document.readyState 判断页面是否加载完成
*
* @param driver 驱动
*/
public static boolean judgePageIsComplete(WebDriver driver) {
if (null == driver) {
throw new RuntimeException("驱动为空");
}
while (true) {
try {
String isComplete = ((JavascriptExecutor) driver).executeScript("return document.readyState").toString();
if ("complete".equalsIgnoreCase(isComplete)) {
return true;
}
sleep(500L);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
/**
* 获取input的value
* @param ele input
* @return input的value属性
*/
public static String getInputValue(WebElement ele){
if(null==ele){
return null;
}
return ele.getAttribute(VALUE_ATTR);
}
/**
* 网页元素截图
* @param driver 驱动
* @param element 元素
* @return 返回图片base64编码
*/
public static String shotWebElement(WebDriver driver, WebElement element){
if(null==driver||null==element){
return null;
}
Point location = element.getLocation();
Dimension size = element.getSize();
ByteArrayOutputStream bos = null;
String result = "";
try {
WebDriver augmentedDriver = new Augmenter().augment(driver);
byte[] screenshotAs = ((TakesScreenshot) augmentedDriver).getScreenshotAs(OutputType.BYTES);
BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(screenshotAs));
BufferedImage croppedImage = originalImage.getSubimage(location.getX(), location.getY(), size.getWidth(), size.getHeight());
bos = new ByteArrayOutputStream();
ImageIO.write(croppedImage, "PNG", bos);
result = Base64.encodeBase64String(bos.toByteArray()).replaceAll("\n", "");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (null != bos) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
/**
* 睡眠
* @param time 睡眠时间/ms
*/
private static void sleep(long time){
try {
Thread.sleep(time);
}catch (Exception e){
e.printStackTrace();
}
}
}
qq群:330405140(selenium学习之路),非教育,非推销,单纯的测试技术群(主要是selenium)。
群里有各种 小姐姐 解答问题,当然 小姐姐没看到问题就没办法了 -_-
今天碰到一个很恶心的绑定主页,记录一下,火狐浏览器一直打开都很正常,今天打开突然指向了
http://upan.000g.com (会重定向到360主页,别在浏览器打开)
尝试了网上的一些办法,甚至重装了好几次火狐浏览器,结果再打开还是这个主页地址。这里说一下我的解决方法,由于我是安装在虚拟机上的firefox,虚拟机上安装有360,打开360,点击功能大全,找到主页防护,点一下,解锁所有的浏览器主页绑定,然后把浏览器主页设置为你想要的页面即可。
手动打开火狐 about:preferences 进入隐私与安全,找到 登录信息与密码 除了使用主密码以外全勾上以后,关闭火狐,再打开查看配置,勾被取消了。
如此一来,就算配置用户文件夹 user-data-dir,也没自动填写密码。
我的做法是,打开表单页面,判断是否自动填写,若没有,则进入 about:preferences 页面,点击相应的按钮。java代码如下(参考上面的工具方法):
WebElement email = getEle(driver, "#ap_email");
if(StringUtils.isEmpty(email.getAttribute("value"))){
//进入火狐密码设置页面
driver.get("about:preferences#privacy");
//点击 向您询问是否保存网站的登录名和密码(R)
if(judgeEle(driver, "#savePasswords")){
clickEle(driver, "#savePasswords");
sleep(1000L);
}
//点击 自动填写登录名和密码(I)
if(judgeEle(driver, "#passwordAutofillCheckbox")){
clickEle(driver, "#passwordAutofillCheckbox");
}
//返回表单填写页面
driver.get(loginUrl);
//判断页面是否加载完
if (!judgePageIsComplete(driver)) {
throw new RuntimeException("页面加载异常");
}
}
注意,勾选之后返回之前的页面,页面是有刷新的,如果要开新窗口设置相关参数,返回原窗口的时候要刷新。
在看下面的方法之前,首先思考一下你是否需要这么做
这里先问几个问题。
第一,拦截selenium请求的原因是否是因为页面上的元素不是固定,不能直接通过元素选择器获取,需要解析请求的响应体来获取数据?
第二,是否是因为需要新增请求头或者修改没有加密或很好解决的加密的请求参数?
第三,是否是因为解析页面太慢,需要直接获取请求来达到快速获取数据的目的?
以上只是部分原因,如果是因为这样,考虑用别的方式解决问题。例如:自己构造请求,发送数据返回给程序。提取页面源码,使用beautifulsoup4,jsoup来解析元素。
给出使用selenium发送请求的demo:
function getData() {
let x = '';
$.ajax({
url: "https://blog.csdn.net//phoenix/web/v1/article/like",
type: "post",
async: false,
data: "articleId=105216417",
success: function(result) {
x = result;
}
})
return x;
}
//将以上函数代码修改后压缩,给selenium执行js,js末尾加上return getData();
//如果页面没有jquery
function getData() {
let url = "https://blog.csdn.net//phoenix/web/v1/article/like";
let xhr = new XMLHttpRequest();
//get
xhr.open("get", url, false);
xhr.send(url);
//post
xhr.open('post',url,false);
// xhr.send('要发送的数据,例如 e=2&b=1 没有就是空字符串')
xhr.send("articleId=105216417");
let res = '';
if (xhr.readyState == 4 && xhr.status == 200) {
res = xhr.responseText
}
return res
}
//将以上函数代码修改后压缩,给selenium执行js,js末尾加上return getData();
以下是拦截请求的部分代码,亲自试验过,似乎没法拿到响应体,网上有人通过其他方式拿到响应体,但是不完美,这里就不拿响应体了。想拿也不是没办法,拿到请求头,请求url,请求体,自己发送就好了。
先给出浏览器插件的webRequest的部分方法:
//这个方法能获取请求体
chrome.webRequest.onBeforeRequest.addListener(function (details) {
console.info(details);
/**
* 通过请求体details拿到的requestBody有两种格式,formData的就不说了
* 这里说一下ArrayBuffer的转化方法:
* decodeURIComponent(escape(String.fromCodePoint.apply(null, new Uint8Array(d.requestBody.raw[0].bytes))))
*/
},{
urls: ["" ]}, ["blocking",'requestBody'])
//这个方法能获取请求头
chrome.webRequest.onResponseStarted.addListener(function(details){
console.info(details);
//给每个请求添加 test:test 请求头
details.requestHeaders.push({
"name":"test","value":"test"})
//如果要修改某个请求头,判断details.url,再循环改details.requestHeaders的属性就好了。
return {
requestHeaders: details.requestHeaders };;
},{
urls:["" ]},["responseHeaders"])
//这个是google官方插件User-Agent Switcher for Chrome的改变user agent的函数 replaceHeader
// Given a UserAgent object, will replace the "User-Agent" header in the
// map provided as requestHeaders.
function replaceHeader(user_agent, requestHeaders) {
if (!user_agent || !user_agent.ua_string)
return requestHeaders;
var newHeaders = [];
for (var i = 0; i < requestHeaders.length; i++) {
if (requestHeaders[i].name != "User-Agent") {
newHeaders.push(requestHeaders[i]);
} else {
var new_value = requestHeaders[i].value;
if (user_agent.ua_string != "")
new_value = (user_agent.append_to_default_ua ? new_value + " " + user_agent.ua_string : user_agent.ua_string);
newHeaders.push({
"name" : "User-Agent",
"value" : new_value});
}
}
return newHeaders;
}
以下是 java demo:
/**
* 根据demo自行修改
* @return
* @throws Exception
*/
private static ChromeOptions setChromeIntercept() throws Exception{
ChromeOptions options = new ChromeOptions();
FileWriter fileWriter1 = null;
FileWriter fileWriter2 = null;
String driverPath = "C:/";//浏览器插件存放文件目录
String jsonFilePath = driverPath + "manifest.json";
String jsFilePath = driverPath + "background.js";
String zipFilePath = driverPath + "intercept.zip";
File jsonFile = new File(jsonFilePath);
File zipFile = new File(zipFilePath);
File jsFile = new File(jsFilePath);
try {
String js = "chrome.webRequest.onBeforeSendHeaders.addListener(function(details){details.requestHeaders.push({\"name\":\"test\",\"value\":\"test\"});return{requestHeaders:details.requestHeaders}},{urls:[\"\"]},[\"blocking\",\"requestHeaders\"]);" ;
String json = "{\"version\":\"1.0.0\",\"manifest_version\":2,\"name\":\"Chrome Intercept\",\"permissions\":[\"tabs\",\"unlimitedStorage\",\"storage\",\"\",\"webRequest\",\"webRequestBlocking\"],\"background\":{\"scripts\":[\"js/background.js\"]},\"minimum_chrome_version\":\"22.0.0\"}" ;
if(!jsFile.exists()){
jsFile.createNewFile();
}
if(!jsonFile.exists()){
jsonFile.createNewFile();
}
fileWriter1 = new FileWriter(jsFilePath);
fileWriter2 = new FileWriter(jsonFilePath);
fileWriter1.write(js);
fileWriter2.write(json);
}catch (Exception e){
e.printStackTrace();
}finally {
try{
if(null!=fileWriter1){
fileWriter1.flush();
fileWriter1.close();
}
if(null!=fileWriter2){
fileWriter2.flush();
fileWriter2.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
try {
File[] files = new File[]{
jsFile, jsonFile};
//zipFile方法参考上面的 设置有认证的代理。
zipFile(files, zipFile);
options.addExtensions(zipFile);
}catch (Exception e){
e.printStackTrace();
}
return options;
}
群里有人说还有通过代理的方式拦截请求,具体去问群里的其他人吧。
tips:远程连接火狐的时候,用户文件夹似乎不能设置。尝试了几种办法,最后发现他是把文件夹内容发送出去了,远程服务器接收到以后也没有打开火狐。似乎是有点问题。chrome配置用户文件夹是传地址,所以chrome可以远程配置用户文件夹。
下面是步骤:
首先下载jar包:https://www.selenium.dev/downloads/
下载最新稳定版本(Latest stable version)。
远程服务器上安装java环境,并配置环境变量,将jar包放入远程服务器上,将chromedriver的目录写入环境变量。
在jar包所在的文件夹打开命令行,输入
java -jar xxxxxx.jar -port 8888
xxx.jar 是你下载的jar包 , 8888 是你指定打开程序的端口,可以自己设置,如果看到打印
Selenium Server is up and running on port 8888
如上图所示,则启动成功。
远程服务器jar包启动成功后,编写代码连接远程服务器,为远程打开selenium添加参数:
@Test
public void RemoteTest() throws Exception{
DesiredCapabilities chrome = DesiredCapabilities.chrome();
ChromeOptions options = new ChromeOptions();
options.addArguments("xxx参数");
chrome.merge(options);
chrome.setCapability("key","value");
WebDriver driver = null;
try{
driver = new RemoteWebDriver(new URL("http://远程ip:8888/wd/hub/"),chrome);
driver.get("https://www.baidu.com/s?wd=selenium")
Thread.sleep(2000L);
}catch(Exception e){
e.printStackTrace();
}finally{
if(null!=driver){
driver.quit();
}
}
}