您的位置:首页 > 编程语言

中国天气网城市代码爬取

2017-10-01 01:32 309 查看
  想试着收集一些气象数据,记得之前气象数据共享平台可以下,结果发现需要实名认证还要提交证件照之类的,想了想,脸黑,估计认证不过,还是算了。于是又网上找了找,发现在美国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());
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: