现在的主流爬虫一般是使用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是否跳过,可以通过这个判断来决定是否做后续的处理.
过程中遇到的问题
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' }
如果使用的是phantomjs的话,关联jar包请使用
compile(‘us.codecraft:webmagic-core:0.7.3’)
compile(‘com.github.detro:ghostdriver:2.1.0’)chrome无头模式时,暂时没有找到设置chromeOption的地方
直接在重写了一遍downloader,处理了一下无头模式,防止chrome弹出的问题
暂时还没用到太多复杂的爬虫操作,整体上都是可以很轻松的解决,满足正常的爬虫需求,不得不赞一下webMagic,文档也写的很细心,一个优秀的爬虫框架。