Java爬虫——Selenium Java详解及案例
一些基础的知识可以通过以下链接先了解:
https://www.jianshu.com/p/20526e2ac3b1
import com.bbt.companyreg.skuhelper.service.HttpProxyIPPoolService;
import com.bbt.companyreg.skuhelper.service.impl.HttpProxyIPPoolServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
/**
* Created by wuming on 2020/6/10 11:03
*/
@Slf4j
public class WebDriverUtil {
private static ChromeDriver webDriver;
public static String ip;
public static ChromeDriver getWebDriver() {
String driver = null;
if(isOSWindow()){
driver = "D:\\chromedriver\\chromedriver.exe";
}else{
driver = "/usr/bin/driver/chromedriver";
}
System.setProperty("webdriver.chrome.driver", driver);
ChromeOptions options = new ChromeOptions();
options.addArguments("--start-maximized"); // 启动时自动最大化窗口
options.addArguments("--disable-popup-blocking"); // 禁用阻止弹出窗口
options.addArguments("no-sandbox"); // 启动无沙盒模式运行
options.addArguments("disable-extensions"); // 禁用扩展
options.addArguments("no-default-browser-check"); // 默认浏览器检查
options.setHeadless(Boolean.TRUE);//设置chrome 无头模式
options.addArguments("--headless");//不用打开图形界面。
//这里设置访问的ip
options.addArguments("--proxy-server=http://" + ip);
webDriver = new ChromeDriver(options);
// webDriver.manage().window().maximize();
// 与浏览器同步非常重要,必须等待浏览器加载完毕,隐式等待30s
webDriver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
//设置页面超时时间,有时候浏览器会处于假死状态,设置这个解决。
webDriver.manage().timeouts().pageLoadTimeout(20, TimeUnit.SECONDS);
return webDriver;
}
public static void closeDriver() {
webDriver.close();
webDriver = null;
}
//判断当前是否linux系统
public static boolean isOSWindow() {
Properties prop = System.getProperties();
String os = prop.getProperty("os.name");
if (os != null && os.toLowerCase().indexOf("windows") > -1) {
return true;
} else {
return false;
}
}
public static String getIp(){
String ip = null;
Integer port = null;
for(int i=0;i<6;i++){
HttpProxyIPPoolService httpProxyIPPoolService = new HttpProxyIPPoolServiceImpl();
List<HttpProxyIPPoolServiceImpl.IPData> ips = httpProxyIPPoolService.getNextIP();
HttpProxyIPPoolServiceImpl.IPData ipData = ips.get(0);
port = Integer.valueOf(ipData.getPort());
ip = ipData.getIp();
if(test(ip, port)){
return ip+":"+port;
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return ip+":"+port;
}
//可以通过这种方式筛选出速度比较快的ip
private static boolean test(String ip, Integer port) {
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("www.baidu.com");
HttpHost proxy = new HttpHost(ip, port);
RequestConfig requestConfig = RequestConfig.custom()
.setProxy(proxy)
.setConnectTimeout(200)
.setSocketTimeout(200)
.setConnectionRequestTimeout(200)
.build();
httpGet.setConfig(requestConfig);
// //设置请求头信息
// httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like " +
// "Gecko) Chrome/62.0.3202.94 Safari/537.36");
CloseableHttpResponse response = httpClient.execute(httpGet);
if (response == null) {
log.warn("当前ip不可用:重新获取---"+ip);
return false;
} else {
HttpEntity entity = response.getEntity();
if (entity != null) {
return true;
}
}
} catch (Exception e) {
log.info(e.getMessage());
}
log.warn("当前ip不可用:"+ip);
return false;
}
//显示等待的方式让这两个按钮加载完毕
private void loadElement(){
(new WebDriverWait(webDriver,10))
.until(ExpectedConditions.elementToBeClickable(By.className(skuBtn)));
(new WebDriverWait(webDriver,10))
.until(ExpectedConditions.elementToBeClickable(By.className(skuActionBtn)));
}
显示等待,隐式等待和强制等待的区别:
在实际使用selenium时,等待下个等待定位的元素出现,特别是web端加载的过程,都需要用到等待,而等待方式的设置是保证脚本稳定有效运行的一个非常重要的手段,在selenium中(appium通用)常用的等待分为显示等待WebDriverWait()、隐式等待implicitly_wait()、强制等待sleep()三种,下面我们就分别介绍一下这三种等待的区别
sleep(): 强制等待,设置固定休眠时间。后脚本的执行过程中执行 sleep()后线程休眠,而另外两种线程不休眠。
implicitly_wait():隐式等待,是设置的全局等待。设置等待时间,是对页面中的所有元素设置加载时间,如果超出了设置时间的则抛出异常。隐式等待可以理解成在规定的时间范围内,浏览器在不停的刷新页面,直到找到相关元素或者时间结束。
WebDriverWait():显示等待,是针对于某个特定的元素设置的等待时间,在设置时间内,默认每隔一段时间检测一次当前页面某个元素是否存在,如果在规定的时间内找到了元素,则直接执行,即找到元素就执行相关操作,如果超过设置时间检测不到则抛出异常。默认检测频率为0.5s,默认抛出异常为:NoSuchElementException。
注意:
1.Java使用chromedriver的时候,同一个时间内只能够打开一个虚拟浏览器,不能使用多线程的方式同时打开多个chrome。
2.访问的频率也不能太快,要适当使用sleep降低访问频率,否则会被网站监测出来,网址很快被封。
3.当我们在一段时间内多次去爬取同一个网站的数据的时候,我们的ip可能会被ban,此时我们需要用到代理ip,使用一些免费的代理服务器的话,网络可能不稳定。此时可以去网上找些付费的,例如我之前使用过的小象代理。每个月也就100多。
public class HttpProxyIPPoolServiceImpl implements HttpProxyIPPoolService {
private static String IP_URL = "https://api.xiaoxiangdaili.com/ip/get";
private static String appKey="";
private static String appSecret="";
private static String cnt = "5";
private static String wt = "json";
@Override
public List<IPData> getNextIP() {
String responseJson = null;
try {
responseJson = Jsoup.connect(IP_URL).method(Connection.Method.GET)
.header("Content-Type", "application/json;charset=UTF-8").data("appKey", appKey, "appSecret", appSecret, "cnt", cnt, "wt", wt)
.ignoreHttpErrors(true).ignoreContentType(true).timeout(2000).get().text();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
log.info("responseJson:" + responseJson);
XiaoXiangResultDto result = JSONObject.parseObject(responseJson, XiaoXiangResultDto.class);
log.info("result IP:" + result.getData().get(0).getIp() + ", port:" + result.getData().get(0).getPort() + ", during:" + result.getData().get(0).getDuring());
return result.getData();
}
public static class IPData {
private String ip;
private String port;
private String realIp;
private int during;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
public String getRealIp() {
return realIp;
}
public void setRealIp(String realIp) {
this.realIp = realIp;
}
public int getDuring() {
return during;
}
public void setDuring(int during) {
this.during = during;
}
}
@Data
private static class XiaoXiangResultDto {
private int code;
private boolean success;
private String msg;
private List<IPData> data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public List<IPData> getData() {
return data;
}
public void setData(List<IPData> data) {
this.data = data;
}
}
上一篇: JSPatch 实现原理详解(二)
下一篇: 指针基础||一篇文章带你看懂指针