中国天气网城市代码爬取

来源:互联网 发布:淘宝昵称在哪里显示 编辑:程序博客网 时间:2024/06/10 04:48

  想试着收集一些气象数据,记得之前气象数据共享平台可以下,结果发现需要实名认证还要提交证件照之类的,想了想,脸黑,估计认证不过,还是算了。于是又网上找了找,发现在美国noaa网站可以下载,包含中国大概700多个站点的数据,时间从50年代一直到最近,但是细看了几个站点的数据后,发现有些站点数据只到2000年初或者更早。继续搜索后想到可以用爬虫,从天气网等网站爬取一些数据,考虑到权威性,选择了中国天气网。

目标网页

  目标网页主要是各个城市的天气页面,主要是当前天气,以及过去24小时天气,每个城市的网址为:
www.weather.com.cn/weather1d/+{城市代码}.shtml
  因此,首先需要找到各个城市的代码,从网上搜了一些后,发现这些可能时间较早,与现在网站上数据不能一一对应,所以考虑从网页中重新爬取到城市代码,顺便可以练习一下相关爬虫技术。
  网上搜到的爬虫案例大多基于python编写,但是个人最近刚看到一个用java爬取京东商品信息及图片的案例,因此想先用java试着爬取,个人感觉用python会显得更简洁,python代码可以很快的看到核心代码,而java,尤其是比较成熟的java代码,即使核心代码很短,却经常被包含在各种异常处理,各种线程,各种配置文件,各种分门别类里。
  话不多说,说城市代码的获取,在研究每个城市天气页面的源码时,发现在一个select的div下,有对应城市的各个区县的代码,比如在上海闵行的页面里,可以看到上海市的各个区县(下图)的城市代码及名称。
这里写图片描述
  再说城市代码的组成规则,前三位101代表中国,接下来的六位依次是省(自治区、直辖市、特区),地市以及区县,分别各占两位,直辖市区县最后两位都是0,真不明白为什么不直接用行政代码。

具体方法

  用到的技术主要是发送请求的httpClient以及解析网页的Jsoup。

        <dependency>            <groupId>org.apache.httpcomponents</groupId>            <artifactId>httpclient</artifactId>            <version>${version}</version>        </dependency>        <dependency>            <groupId>org.jsoup</groupId>            <artifactId>jsoup</artifactId>            <version>${version}</version>        </dependency>

  因为不清楚具体有多少个地级市,最先想到的是循环101010100-101350000,遍历所有页面,但这样循环次数过多,因此加了一层循环,内层循环连续出现无效页面次数大于100时,跳出内层循环,外层循环加10000,也就是调到下一个省。需要注意的是,该网站无效页面返回并不是404,而是空页面,因此可以通过判断html页面字符长度来确认无效页面。

for (long j = 101010100; j< 101360000; j+=10000) {            //标记无效页面            int countInvalid = 0;            for(long i = j;i<101350000;i++) {                String url = "http://www.weather.com.cn/weather1d/" + i + ".shtml";                String html = getHtml(url);                StringBuilder sb = new StringBuilder();                if (html!=null && html.length()>20) {                    //计数归零                    countInvalid=0;                    File file = new File("D:\\temp\\" + i +".txt");                    //解析Html                    Document document = Jsoup.parse(html);                    Elements lis = document.select("#select ul").select("li");                    //System.out.println(lis.size());                    for (Element li : lis) {                        String cityCode = li.attr("tip");                        String cityName = li.text();                                        sb.append(cityCode + "\t" + cityName + "\r\n");                         }                    FileUtils.writeStringToFile(file, sb.toString() , "UTF-8");                }else {                    //连续100次出现无效页面,退出内循环                    countInvalid++;                    if(countInvalid > 100) {                        break;                    }                }                           }        }

  用httpClient发送页面请求方法如下,用的是最基础的方法:

    public static String getHtml(String url) throws Exception {        // 创建Httpclient对象        CloseableHttpClient httpclient = HttpClients.createDefault();        // 创建http GET请求        HttpGet httpGet = new HttpGet(url);        CloseableHttpResponse response = null;        try {            // 执行请求            response = httpclient.execute(httpGet);            // 判断返回状态是否为200            if (response.getStatusLine().getStatusCode() == 200) {                String content = EntityUtils.toString(response.getEntity(), "UTF-8");                return content;            }        } finally {            if (response != null) {                response.close();            }            httpclient.close();        }        return null;    }

  这样就得到每个区县级别对应的txt文件,接下来就是处理重复的txt文件,原本的想法是,每个地市级别(前7位代码)仅保留一个,最后将txt文件合并成一个。在实际操作中发现,遍历每个文件的每一行数据,删除掉重复项,写入一个列表里,最后将列表输出到一个txt文件,这样会快很多。最终得到3231条记录。

File[] files = new  File("D:\\temp\\weatherCitycode").listFiles();        File outFile = new File("D:\\temp\\weatherCitycode.txt");        List<String> cityList = new ArrayList<String>();        for(int idx = 0; idx<files.length;idx++) {            File curFile = files[idx];             List<String> lines = FileUtils.readLines(curFile, "UTF-8");            for(String line:lines) {                if (!cityList.contains(line)){                    cityList.add(line);                }            }        }        FileUtils.writeLines(outFile, cityList);        System.out.println(cityList.size());