JAVA爬取城市天气

307 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

前言

现在的主流爬虫一般是使用python的scrapy来爬取数据,我主流技术栈是JAVA,py虐懂皮毛,上网搜索了下java的爬虫,发现有个国产的爬虫webMagic,网上使用率比较高。API简单易懂,易于使用,只是好久没更新维护,简易的爬虫都是可以满足的。

框架

webMagic有自己的一套爬虫流程,我们只需要按照流程创建对应的类即可

image.png

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

抓取前准备

我们的目标网页是www.weather.com.cn/weather/101…, 中间的数字是对应的每个城市的城市id,我们可以预先在数据库中配置我们想要抓取的城市id,城市名称,方便后续批量获取。我选取的是我所在的城市【武汉】。

页面分析

image.png

中间这一块区域是我们要获取的数据源,我们需要对数据进行提取分析。F12查看后得知

image.png 7日天气就在这个li元素数组中,我们只需要提取就行了

processor

前提

抓取的时候建议使用xpath路径,选择器推荐用id或者class这种明确的选取规则,保证唯一行动前提下,不要使用网页上自带的xpath选取器。

我们的目标是获取到执行的页面,抓取我们想要的元素,从id=7d入手,向下寻找

List<Air7Day> air7DayList = new ArrayList<>();

    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 = NumberUtil.parseInt(page.getHtml().xpath("//div[@id='7d']/ul/li["+ i +"]/p[2]/span/text()").toString());
        int lowTemperature = NumberUtil.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(cityId);
        air7Day.setWDate(LocalDate.now().plusDays(i - 1));

        air7DayList.add(air7Day);

    }

    page.putField("air7DayList", air7DayList);
}
  • 执行了7天的循环,获取最近7天的天气
  • 存入一个list中,传到到下一个流程pipeline中去处理

pipeline

image.png

逻辑比较简单,进行解析列表,数据入库即可。

启动

image.png

通过spider直接进行create处理,传入processor,pipeline进行处理。可以制定定时任务,针对不同的任务采取不同的策略。

问题

抓取空气质量指数,会遇到直接查询不到数据的情况,这个是因为aqi页面地址,采用ajax动态写入的数据,所以我们需要引入chrome驱动进行加载,不是简单的抓取网页,需要渲染页面后再抓取数据。

image.png

同步需要设置一下无头模式,即可抓取到aqi数据。

image.png

拓展

同样可以通过爬虫获取到bing首页的图片,地址设置为 cn.bing.com/。 通过分析网页内容得知背景图片是在下面内容中

image.png 可以通过class定位进行选择 String imageUrl = page.getHtml().xpath("//div[@class='img_cont']").regex("background-image: url\\((.*)\\)").toString();

问题

之前都是可以获取到背景图片,最近不行,估计是做了防爬虫处理。可以打开无头模式,让浏览器弹出来即可查询到背景图片地址。