24 December 2015

数据抓取本身流程很简单,但是当遇到网站的种类变多或者要采集的数据变多的时候,性能问题会称为数据抓取中要首要解决的问题。 这几天同事在测试采集数据的时候总是遇到反应很慢的情况。今晚趁着洗完澡脑子清醒,针对部分问题重构了下;做下记录。

这次遇到的问题主要是代理的问题,场景是这样的:

  • 我有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);       
      }   }
    
  • 另外我是在系统初始化的时候就将各个网站的采集类初始化好的,这样其实没有必要,完全可以在采集这个网站的时候再进行初始化即可。




Fork me on GitHub