webMagic 爬虫使用

现在的主流爬虫一般是使用python的scrapy来爬取数据,我主流技术栈是JAVA,py虐懂皮毛,上网搜索了下java的爬虫,发现有个国产的爬虫webMagic,网上使用率比较高。API简单易懂

查看官方文档,webMagic官网 不知道为什么 我电脑打不开,找到里面的docs,有提供简单的示例和各种的常见操作处理,一般作者的博客都是oschina上,可以去上面查看爬虫相关的博客

我这边先准备爬一下天气网中国天气官网的数据练手,webMagic封装了几个常用的组件
组件图

组件 用途
DownLoader 网页的处理:普通静态网页,selenium JS渲染抓取
PageProcesser 整个HTML会载入这个processer,可以通过xpath,正则,JS抓取
Scheduler 多个请求网页计划的容器,可以使用优先规则,先进先出,都实现了重复请求的排除策略,防止重复抓取
Pipeline 处理从processer过来的数据,可以自行处理保存到数据库或者其他地方
页面内容获取

抓取的网页属于动态网页,需要使用载体来渲染页面,一开始使用的是phantomjs,爬取的时候 总是会出现一些莫名的错误,后来我改用了chromeDriver的headless模式来抓取。config.ini文件的配置如下:

What WebDriver to use for the tests
driver=chrome

config.ini的读取设置

ClassPathResource classPathResource = new ClassPathResource("/config.ini");
String path = classPathResource.getURL().getPath();

System.setProperty("webdriver.chrome.driver", "E:\\workdocument\\ww\\chromedriver.exe");
System.setProperty("selenuim_config", path);

上去配置了驱动路径和config文件的路径,作者上面的有个key命名写错了,应该是selenium。在代码中会读取config.ini中的属性来采用哪种驱动类型来渲染页面

processer的处理

分析网页我采用的是xpath的表达式,感觉写复杂的网页内容定向的时候也比较简单,还支持一些高级的表达式功能。参考网址
xpath 先爬取了常用的7天的天气数据

for (int i = 1; i <= 7; i++) {
            // 1 day
            // 白天气象文字
            Air7Day air7Day = new Air7Day();
            String dayWord = page.getHtml().xpath("//div[@id='7d']/ul/li["+ i +"]/p[1]/text()").toString();
            String daytimeCode = page.getHtml().xpath("//div[@id='7d']/ul/li["+ i +"]/big[1]/@class").toString();
            String dayNightCode = page.getHtml().xpath("//div[@id='7d']/ul/li["+ i +"]/big[1]/@class").toString();

            int highTemperature = Integer.parseInt(page.getHtml().xpath("//div[@id='7d']/ul/li["+ i +"]/p[2]/span/text()").toString());
            int lowTemperature = Integer.parseInt(page.getHtml().xpath("//div[@id='7d']/ul/li["+ i +"]/p[2]/i/text()").toString().replace("℃", ""));

            String windDirection = page.getHtml().xpath("//div[@id='7d']/ul/li["+ i +"]/p[3]/em/span[1]/@title").toString();
            String windScale = page.getHtml().xpath("//div[@id='7d']/ul/li["+ i +"]/p[3]/i/text()").toString();

            if (dayWord.contains("转")) {
                String[] dayWordArray = dayWord.split("转");
                String daytime = dayWordArray[0];
                String dayNight = dayWordArray[1];

                air7Day.setTextForDay(daytime);
                air7Day.setTextForNight(dayNight);
            } else {
                air7Day.setTextForDay(dayWord);
                air7Day.setTextForNight(dayWord);
            }

            air7Day.setCodeForDay(daytimeCode.substring(daytimeCode.length() - 2, daytimeCode.length()));
            air7Day.setCodeForNight(dayNightCode.substring(dayNightCode.length() - 2, dayNightCode.length()));

            air7Day.setHighTemperature(highTemperature);
            air7Day.setLowTemperature(lowTemperature);

            air7Day.setWindDirection(windDirection);
            air7Day.setWindScale(windScale);

            air7Day.setCityId(101020100);
            air7Day.setWDate(DateUtil.shiftDate(i - 1));

            air7DayList.add(air7Day);

        }
pipeline处理

这边暂时就比较简单,直接存入了数据库,存入之前去了一下重复数据

if (!resultItems.isSkip()) {
            List<Air7Day> air7DayList = resultItems.get("air7DayList");
            air7DayList.forEach(air7Day -> {
                // 判断是否存在
                int c = (int) sqlSessionHandler.selectOne("airDailyMapper.air:daily:exists", air7Day);
                if (c > 0) {
                    // 存在更新
                    sqlSessionHandler.insert("airDailyMapper.air:daily:update", air7Day);
                } else {
                    // 不存在则插入
                    sqlSessionHandler.insert("airDailyMapper.air:daily:insert", air7Day);
                }
            });
        }

skip来定义这个resultItems是否跳过,可以通过这个判断来决定是否做后续的处理.

过程中遇到的问题
  1. webMagic-core包中的log4j的冲突,解决方式是排除

    compile('us.codecraft:webmagic-core:0.7.3'){
         exclude group:'org.slf4j',module:'slf4j-log4j12'
     }
    compile('us.codecraft:webmagic-selenium:0.7.3'){
         exclude group:'org.slf4j',module:'slf4j-log4j12'
         exclude group:'com.github.detro',module:'phantomjsdriver'
     }
    
  2. 如果使用的是phantomjs的话,关联jar包请使用
    compile(‘us.codecraft:webmagic-core:0.7.3’)
    compile(‘com.github.detro:ghostdriver:2.1.0’)

  3. chrome无头模式时,暂时没有找到设置chromeOption的地方
    直接在重写了一遍downloader,处理了一下无头模式,防止chrome弹出的问题

暂时还没用到太多复杂的爬虫操作,整体上都是可以很轻松的解决,满足正常的爬虫需求,不得不赞一下webMagic,文档也写的很细心,一个优秀的爬虫框架。