深入浅出爬虫(Java福利版)
程序员文章站
2022-05-04 15:52:58
...
福利在手,说走就走;
关键技术点
- 生产消费者模式的应用;
- 线程池的应用;
- 网页解析技术(Jsoup)的应用;
- Selenium的应用;
- 乐观锁的简单实现;
- 单例模式的应用;
- 防反爬技术的应用;
- 自定义应用池的实现;
- Java语言实现,Maven编译;
关键代码说明
生产消费者模式的实现;
// 生产消费者模式
BlockingQueue<LMIndex> queue = new ArrayBlockingQueue<LMIndex>(100);
// 线程池管理任务;
ExecutorService es = Executors.newFixedThreadPool(3);
es.submit(new Producer(queue));
es.submit(new Producer(queue));
es.submit(new Consumer(queue));
es.submit(new Consumer(queue));
es.shutdown();
以上代码中,是应用的入口main方法的内容,在内容中使用队列来作为生产消费者的通道,使用线程池来管理任务线程,
在这里,为了避免拖累目标服务器,尽量少用那么多线程来操作。
消费者的重试机制
public void run() {
AtomicInteger retry = new AtomicInteger(RETRY_COUNT);
while (true) {
LMIndex lmi = null;
try {
lmi = queue.poll(2, TimeUnit.SECONDS);
} catch (InterruptedException ex) {
ex.printStackTrace();
if (retry.decrementAndGet() == 0) {
break;
}
}
System.out.println("消费:" + lmi);
int retryCount = retry.decrementAndGet();
if (lmi == null && retryCount == 0) {
break;
}
if (lmi == null && retryCount > 0) {
continue;
}
// 恢复初始状态
retry.set(RETRY_COUNT);
consume(lmi);
//
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
if (retry.decrementAndGet() == 0) {
break;
}
}
}
}
为了避免出现生产慢,消费快的情况,这里使用了重试机制;这里有个关键的地方,重试获取任务完成之后记得要恢复重试机制的初始状态,要不然会出现意外情况哦;
乐观锁的简单实现
public static String getSource(String url) {
WebDriver driver;
while (true) {
driver = DriverHolder.getDriver();
if (driver != null) {
break;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
driver.get(url);
String src = driver.getPageSource();
DriverHolder.completeTask(driver);
return src;
}
从上面的代码中可以很明显的看到,当获取driver失败的时候,在进行不断的重试,直到成功为止。
除了重试机制,在后面的操作中,当操作完成的时候,需要重置driver的状态。
至于为什么要重置driver的状态,可以看后面的说明。
Driver工作类
/**
*
*/
package mk.lesmao.driver;
import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.service.DriverService;
import mk.lesmao.SeleniumUtil;
/**
* @author MichaelKoo
*
*
* 驱动工具类
*/
public class DriverHolder {
static ConcurrentHashMap<Integer, DriverEntity> driverPool = new ConcurrentHashMap<Integer, DriverEntity>(5);
static final int POOL_SIZE = 10;
/**
* 1、如果池为空,则添加;
*
* 2、如果有空闲的则使用;
*
* 3、如果池未满,则添加;
*
* @return
*/
public static WebDriver getDriver() {
System.out.println("driver driverPool size=" + driverPool.size());
//
if (driverPool.size() == 0) {
DriverEntity de = DriverEntity.create();
if (de != null) {
driverPool.put(de.driver.hashCode(), de);
return de.driver;
}
}
//
Iterator<Integer> iter = driverPool.keySet().iterator();
while (iter.hasNext()) {
DriverEntity de = driverPool.get(iter.next());
if (!de.hasTask) {
de.hasTask = true;
driverPool.put(de.driver.hashCode(), de);
return de.driver;
}
}
//
if (driverPool.size() < POOL_SIZE) {
DriverEntity de = DriverEntity.create();
driverPool.put(de.driver.hashCode(), de);
return de.driver;
}
return null;
}
/**
* 完成操作进行状态恢复
*
* @param driver
*/
public static void completeTask(WebDriver driver) {
DriverEntity de = driverPool.get(driver.hashCode());
de.hasTask = false;
driverPool.put(driver.hashCode(), de);
}
/**
* 驱动实体
*
* @author MichaelKoo
*
*
*/
static class DriverEntity {
WebDriver driver;
DriverService service;
boolean hasTask = true;
private void close() {
if (driver != null) {
driver.close();
}
if (service != null && service.isRunning()) {
service.stop();
service = null;
}
}
static DriverEntity create() {
DriverEntity entity = new DriverEntity();
entity.service = new ChromeDriverService.Builder().usingAnyFreePort().build();
try {
entity.service.start();
} catch (IOException e) {
e.printStackTrace();
return null;
}
Capabilities options = SeleniumUtil.getCapabilities(SeleniumUtil.lesmaoHeader());
entity.driver = new RemoteWebDriver(entity.service.getUrl(), options);
return entity;
}
}
/**
* 释放驱动池
*/
public static void release() {
Iterator<Integer> iter = driverPool.keySet().iterator();
while (iter.hasNext()) {
DriverEntity de = driverPool.get(iter.next());
de.close();
}
driverPool.clear();
}
}
关键词有,池化思想,状态恢复,对象重复利用;
从completeTask方法可以看到恢复状态的操作,这里做个说明,如果不恢复driver状态,那么前面的getSource就一直会失败;
一直在重试。
声明
本文只做学习之用,请勿用于其他用途,谢谢。