数据抓取本身流程很简单,但是当遇到网站的种类变多或者要采集的数据变多的时候,性能问题会称为数据抓取中要首要解决的问题。
这几天同事在测试采集数据的时候总是遇到反应很慢的情况。今晚趁着洗完澡脑子清醒,针对部分问题重构了下;做下记录。
这次遇到的问题主要是代理的问题,场景是这样的:
- 我有100个代理,
- 系统初始化的时候,我把这100个代理中放到一个队列(ArrayBlockingQueue)中。
- 然后平均每个采集类(一个采集类对应一个网站或者一类网站)分配10个代理。
- 问题来了,当我网站超过10个到11个的时候,这时候启动系统,第十一个采集类会无法从代理中获取代理。于是第十一个类一直在登真队列中哪个好心人把代理资源放回来。可惜它永远登不到那天。
问题的原因其实是我多虑了,因为代理资源本身是可以多个采集类共用的。而我起初的考虑是为了让代理资源能够均衡使用,不至于某个代理频繁的被使用,而某些代理永远没有用到(浪费了)。
我的解决方法是这样的:
- 把代理资源放在一个List中,而不是队列中。
- 每个采集类获取代理的时候从list中顺序获取,从第1个代理获取,到100个以后,再重头开始继续依次获取。
- 由于List中无法自己标记当前获取到第几个了,且在多线程环境下,所以使用了AtomicInteger来做计数器。
- 参考代码如下:
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProxieQueue {
private static Logger logger = LoggerFactory.getLogger(ProxieQueue.class);
private static List<Proxie> proxieList = com.google.common.collect.Lists.newArrayList();
private static AtomicInteger listCounter =new AtomicInteger();
private ProxieQueue() {
}
static {
try {
InputStream is =Configurations.class.getClassLoader().getResourceAsStream("proxies.txt");
List<String> lineList = IOUtils.readLines(is); ;
for (String line : lineList) {
String[] strArray = line.split(":");
Proxie proxie = new Proxie();
proxie.setUrl(strArray[0]);
proxie.setUser(strArray[1]);
proxie.setPass(strArray[2]);
proxie.setPort(Integer.valueOf(strArray[3]));
proxieList.add(p);
}
} catch (Exception e) {
logger.error("读取代理配置失败", e);
}
}
public static Proxie get() {
int index = listCounter.getAndIncrement();
if(index == proxieList.size()){
//获取到结尾后,从头开始继续获取
index= listCounter.getAndSet(0);
}
return proxieList.get(index);
}
}
- 另外我是在系统初始化的时候就将各个网站的采集类初始化好的,这样其实没有必要,完全可以在采集这个网站的时候再进行初始化即可。
欢迎去我的个人站点查看文章
或者,欢迎关注俺的微信订阅号,每天一篇小笔记,每天提高一点点:
公众号:enilu123