首页 > SSH框架效率问题

SSH框架效率问题

背景交代:

现在在做一个毕设,目的从各个招聘网站上(比如智联招聘)爬取职位信息,并存到自己的数据库中。然后进行分析,去重。最后能利用这些数据给用户推荐职位,并绘制一些诸如职位在城市的分布,薪资情况等等的排名的图表出来。

使用的框架:

现在使用到的技术有 SpringMVC + Spring + Hibernate 框架。
分布式采用了比较简单的Hessian。
HTML爬取和分析使用的Jsoup。
数据库MYSql。
前端暂时打算使用Bootstrap 但是还没定是不是自己做,有可能采用修改免费模板的方式。

问题:

这是利用以上搭建的环境爬取2940个职位信息的耗时情况。
惨不忍睹。
想问一下在这种架构的情况下如何能提高程序的效率?

以下是爬虫部分的代码:

@Component
public class Spider_ZhiLian implements Runnable{
    
    private WorkDao workDao;
    
    private static Logger logger = Logger.getLogger(Spider_ZhiLian.class);
    
    public Spider_ZhiLian(WorkDao workDao) 
    {  
        this.workDao = workDao;
    }
    public Spider_ZhiLian() 
    {  
    }
    public void run() {
        // TODO Auto-generated method stub
        getJobs();
    }
    
    public void getJobs()
    {
        int total = 0;
        
        Calendar a=Calendar.getInstance();
        final String YEAR = a.get(Calendar.YEAR)+"-";
        
        List<Work> list = new ArrayList<Work>();
        
        Element errorElements  = null;
        
        long startTime = System.currentTimeMillis();    //获取开始时间
        long downTime = 0;//下载时间
        long thinkTime = 0;//解析时间
        long saveTime = 0;//存入数据库时间
        for(int t = 0;t<50;t++)
        {
            list.clear();
            try {
                long point1start = System.currentTimeMillis();    //下载一个页面的时间
                Document document = Jsoup.connect("http://sou.zhaopin.com/jobs/searchresult.ashx?jl=%E9%80%89%E6%8B%A9%E5%9C%B0%E5%8C%BA&isadv=0&sg=91bdb06d4b2c4871a1000b75fe7002b7&p="+t+1).userAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31").post();
                //System.out.println(document);
                long point1end = System.currentTimeMillis();    //下载一个页面的时间
                downTime += point1end-point1start;
                
                long point2start = System.currentTimeMillis();    //解析一个页面的时间
                Elements companyName = document.select("td.gsmc");//公司
                Elements salary = document.select("td.zwyx");//月薪
                Elements place = document.select("td.gzdd");//工作地点
                Elements name = document.select("td.zwmc");//职位
                Elements date = document.select("td.gxsj");//更新时间
                Elements infomations = document.select("li.newlist_deatil_two");
                
                
                //地点:重庆公司性质:股份制企业公司规模:1000-9999人学历:本科
                //地点:重庆公司性质:民营公司规模:100-499人学历:不限职位月薪:15001-20000元/月
                
                for(int i = 0;i< companyName.size() ;i++)
                {
                    Work work = new Work();
                    
                    errorElements = infomations.get(i);
                    
                    String[] infos = infoSpliter(infomations.get(i).text());
                    
                    work.setCompanyName(companyName.get(i).text());
                    work.setName(name.get(i).text());
                    work.setSalary(salary.get(i).text());
                    work.setPlace(place.get(i).text());
                    work.setCompanySize(infos[0]);
                    work.setDate(Date.valueOf(YEAR+date.get(i).text()));
                    work.setEducation(infos[1]);
                    work.setType(2);
                    work.setUrl(name.get(i).getElementsByTag("a").attr("href"));

                    total++;
                    list.add(work);
                }
                long point2end = System.currentTimeMillis();    //解析一个页面的时间
                thinkTime += point2end-point2start;
                
                long point3start = System.currentTimeMillis();    //存入页面的时间
                workDao.saveWorkList(list);
                long point3end = System.currentTimeMillis();    //存入页面的时间
                saveTime += point3end-point3start;
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println(errorElements.text());
            }
            
        }
        long endTime = System.currentTimeMillis();    //获取开始时间
        logger.info("共有   " +  total + "个职位,耗时:  "+(endTime-startTime)/1000.0 + " 秒。  ");
        logger.info("获取HTML耗时   "+downTime/1000.0 + " 秒。  耗时比重:  "+ downTime*1.00/(endTime-startTime) *100 + "%");
        logger.info("解析HTML耗时   "+thinkTime/1000.0 + " 秒。耗时比重:  "+ thinkTime*1.00/(endTime-startTime) *100 + "%");
        logger.info("存入数据库耗时   "+saveTime/1000.0 + " 秒。耗时比重:  "+ saveTime*1.00/(endTime-startTime) *100 + "%");
        logger.info("处理异常耗时   "+(endTime-startTime-saveTime-downTime-thinkTime)/1000.0 + " 秒。耗时比重:  "+ (endTime-startTime-saveTime-downTime-thinkTime)*1.00/(endTime-startTime) *100 + "%");
    }
    
    //将网站中一串的信息处理成String数组的形式
    private String[] infoSpliter(String info)
    {
        String[] result = new String[2];
        
        String[] temp1 = info.split("公司规模:");
        String[] temp2 = null;
        if(temp1.length < 2)
        {
            result[0] = "未知";
        }
        else
        {
            temp2 = temp1[1].split("人");
            result[0] = temp2[0];//人数(规模)
        }
            
        String[] temp3 = info.split("学历:");
        String[] temp4 = temp3[1].split("职位月薪:");
        result[1] = temp4[0];
        return result;
    }
}

大家无视我弱智的时间统计方式吧。。。AOP还不太会用。。只能先这么统计一下了。

自己的想法:

最左面的电脑(也就是主机)来控制中间三台电脑的爬虫。
中间三台电脑和主机在同一局域网内,三台主机连接互联网爬虫的数据再通过内网传送到主机的数据库上。
我觉得这样的方式应该能提高不少速度。

还有一种想法是在各个爬虫节点上配置数据库,爬虫之后直接存在自己的数据库中,然后主机访问三个爬虫节点的数据库。但是有个问题是数据的重复问题。

最后:

很感谢您能看到这里。
大概就是以上的情况,希望有大神能提出宝贵的建议让我的程序提高些效率。不然这样上百万的数据真的会跑到崩溃。
或者不是效率问题,其他方面有好的建议也欢迎提出。
再次感谢!


你的耗时分析里已经指出了问题,“获取HTML耗时”占的比重是90%,再看你的代码,业务逻辑是

public void getJobs(){
    ...
    for(int i=0;i<50;i++){
        //生成url
        //爬取页面
        //解析内容
        ...
    }
    ...
}

并不清楚SSH框架中如何处理这个'''getJobs()'''这个函数,但看起来是单线程的,耗时这么久也就不足为奇了。

所以,应该把下载页面和处理内容两部分的工作分开来。这就是一个生产者-消费者问题了啊:页面下载的功能是生产者,网络IO明显比处理页慢,可以多开几个线程来进行下载页面工作,放入队列里;内容的分析处理部分是消费者,从队列里拿出页面解析就好,看情况要不要多线程。

【热门文章】
【热门文章】