Java爬取中国天气网实况天气数据_Honyelchak的博客-程序员宅基地

技术标签: 天气  爬虫  Java  API  

因实验室需求,需要找一个实况天气API。

百度云、阿里云、腾讯云上边我都去找了,很多平台要么没有,要么要收费(免费的可调用次数太少了)。而我在高德开放平台上找到了一个,但是不符合要求,被老师pass掉了。

百度搜一下,基本上都是用Python自动化测试Selenium写的,那也太没意思了吧。找不到,那我只好自己写一个爬虫去爬取了。

分析

如果想在中国天气网上爬取实况天气还是很简单的,但是由于思路一直受限制,所以道路很曲折。

在这里插入图片描述

原来是考虑着爬取这个图,每天爬一次就够了。

我费劲脑汁,我还想用x、y的比值来计算出每个点所在的位置然后得出每个点对应的数据。但是经过几次测试之后,误差较大,所以这个方案就放弃了。

然后考虑追溯每个点数据的来源,但是这个网站的js文件用了混淆加密,变量全是a、b、c、dxxxx看不出来意思的英文字母。后来又想想。网页上的数据无非来自两个地方:

  • 静态的(在源代码中可以直接找到的)
  • 异步请求(在开发者工具栏中network中可以查找)
  • 其他的都是玄学

那么根据这个网站,不是静态的。但是network中有那么多的请求,不好找?。

我们就在网站源代码中找js文件,看看哪个js文件中有请求。看到一个请求像是在请求数据(一般数据都放在一个服务器上),所以我们根据该请求的Domain,缩小network中的我们寻找的部分。

img

在Domain是d1.weather.com.cn中进行寻找,果然数据基本上都出于这个Domain。

1546572380110

成功了一半,但是请求链接后面加的那一串数字是什么?

http://d1.weather.com.cn/sk_2d/101180101.html?_=1546572266854

越看越像时间戳,然后再eclipse中测试一下,果然是时间戳。

那么问题就简单了。直接写代码就行了。

总结

遇到爬取任务时,要先分析:

找数据
  • 看数据是否就在网站源代码中
  • 看数据是否是异步加载的
    • 若network中请求不多,直接遍历查看请求即可。
    • 反之,通过请求链接寻找Domain,然后缩小范围寻找请求。
  • 如果上两个都不容易,那就在开发者工具中Break on subtree modifications调试js文件
    • 如果js文件使用了混淆加密,你不想掉头发的话,就直接找异步请求吧。
检查数据

要知道很多网站都有反爬虫机制,也就是说 你获取的数据中可能被"投了毒"。好好检查检查数据是否正确。

写代码爬取数据

要注意以下几部分

  • 请求头伪装的像浏览器一点
    • 让爬虫随机获取user-agent
    • 设置referer(这个还是比较有用的)
  • 请求之间有一些延迟最好
  • 如果需要的话,就上下边的终极武器。
    • 构造cookies
    • 构造IP

Java代码

我用了quartz定时任务框架来实现定时爬取。

public void execute(JobExecutionContext arg0) {
    

		String[] city = new String[124];
		// 读取城市ID
		int i = 0;
		String str = "";
		try {
    
			URL resource = this.getClass().getResource("/cityId.txt");
			String path = resource.getPath();// 获取文件的绝对路径
			BufferedReader br = new BufferedReader(new InputStreamReader(
					new FileInputStream(path)));
			while ((str = br.readLine()) != null) {
    
				city[i] = str;
				i++;
			}
		} catch (IOException e) {
    
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		RequestConfig defaultRequestConfig = RequestConfig.custom()
				.setSocketTimeout(60000).setConnectTimeout(60000)
				.setConnectionRequestTimeout(60000)
				.setStaleConnectionCheckEnabled(true).build();
		// 创建httpClient对象
		CloseableHttpClient h = HttpClients.custom()
				.setDefaultRequestConfig(defaultRequestConfig).build();

		// 创建并设置URI
		URIBuilder uri = null;
		// 创建Get请求
		HttpGet hg = null;
		String url = "";
		// 创建响应对象
		CloseableHttpResponse response = null;
		InputStream inputstream = null;
		for (int j = 0; j < city.length; j++) {
    

			try {
    
				url = "http://d1.weather.com.cn/sk_2d/" + city[j] + ".html?_="
						+ System.currentTimeMillis();
				uri = new URIBuilder(url);
				hg = new HttpGet(uri.build());
			} catch (Exception e2) {
    
				e2.printStackTrace();
			}
			// 设置请求头
			hg.setHeader("Accept",
					"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
			hg.setHeader("Accept-Encoding", "gzip, deflate");
			hg.setHeader("Accept-Language",
					"zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
			hg.setHeader("Cache-Control", "no-cache");
			hg.setHeader("Connection", "keep-alive");
			hg.setHeader("Host", "d1.weather.com.cn");
			hg.setHeader("Upgrade-Insecure-Requests", "1");
			hg.setHeader("Cookie",
					"f_city=%E9%83%91%E5%B7%9E%7C101180101%7C; Hm_"
							+ "lvt_080dabacb001ad3dc8b9b9049b36d"
							+ "43b=1546482322; Hm_lpvt_080dabacb001a");
			hg.setHeader(
					"User-Agent",
					"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36");
			hg.setHeader("Referer",
					"http://www.weather.com.cn/weather1dn/101180101.shtml");
			// 发送请求
			HttpEntity entity = null;
			String line = "";
			String Sline = "";

			try {
    
				response = h.execute(hg);
				// 获取请求结果
				entity = response.getEntity();
				inputstream = entity.getContent();
				BufferedReader bufferedreader = new BufferedReader(
						new InputStreamReader(inputstream, "UTF-8"));
				while ((line = bufferedreader.readLine()) != null) {
    

					Sline += line + "\n";
				}

				Sline = Sline.substring(Sline.indexOf('{'));

				JSONObject jsonObject = JSONObject.fromObject(Sline);
				show(jsonObject);

			} catch (ClientProtocolException e1) {
    
				// TODO Auto-generated catch block
				System.out.println("请求超时等问题");
				e1.printStackTrace();
			} catch (IOException e1) {
    
				// TODO Auto-generated catch block
				System.out.println("I/O问题");
				e1.printStackTrace();
			} finally {
    
				try {
    
					inputstream.close();
					response.close();
				} catch (IOException e) {
    
					// TODO Auto-generated catch block
					System.out.println("I/O、response流关闭");
					e.printStackTrace();
				}
			}
		}

	}

	/**
	 * 样例对照表
	 * 
	 * var dataSK = { "nameen":"zhengzhou", "cityname":"郑州", "city":"101180101",
	 * "temp":"1", 摄氏度 "tempf":"33", 华氏度 "WD":"西南风", 风向 "wde":"SW", 风向英文
	 * "WS":"1级", 风力等级 "wse":"<12km/h", 风速 "SD":"52%", 湿度 "time":"11:25",
	 * "weather":"晴", "weathere":"Sunny", "weathercode":"d00", "qy":"1019", 气压
	 * "njd":"4.94km", 能见度 "sd":"52%", 湿度 "rain":"0.0", 降雨量 "rain24h":"0",
	 * "aqi":"214", "limitnumber":"", "aqi_pm25":"214", pm2.5
	 * "date":"01月03日(星期四)" }
	 */

	public void show(JSONObject jsonObject) {
    
		// 获取城市编号
		String cityId = jsonObject.getString("city");
		System.out.println(cityId);

		String cityName = jsonObject.getString("cityname");
		System.out.println(cityName);

		// 获取当前气温
		String temperature = jsonObject.getString("temp");
		System.out.println("当前气温" + ":" + temperature);

		// 获取当前风向
		String windDirection = jsonObject.getString("WD");
		System.out.println("风向:" + windDirection);
		// 获取当前风向
		String windDirectionEn = jsonObject.getString("wde");
		System.out.println("风向符号:" + windDirectionEn);

		// 获取当前风速等级
		String windPower = jsonObject.getString("WS");
		System.out.println("风力:" + windPower);

		// 获取当前风速
		String windSpeed = jsonObject.getString("wse");
		System.out.println("风力:" + windSpeed);

		// 获取当前湿度
		String humidity = jsonObject.getString("SD");
		System.out.println("湿度:" + humidity);

		String time = jsonObject.getString("time");
		System.out.println("时间:" + time);

		String weather = jsonObject.getString("weather");
		System.out.println("天气中文:" + weather);

		String weatherEn = jsonObject.getString("weathere");
		System.out.println("天气英文:" + weatherEn);

		String weatherCode = jsonObject.getString("weathercode");
		System.out.println("天气代号:" + weatherCode);

		String airPressure = jsonObject.getString("qy");
		System.out.println("气压:" + airPressure);

		String visibility = jsonObject.getString("njd");
		System.out.println("能见度:" + visibility);

		String rain = jsonObject.getString("rain");
		System.out.println("降雨量:" + rain);

		String rain24h = jsonObject.getString("rain24h");
		System.out.println("日降雨量" + rain24h);

		String aqi_pm25 = jsonObject.getString("aqi");
		System.out.println("PM2.5:" + aqi_pm25);

		String date = jsonObject.getString("date");
		System.out.println("时间" + date);

		Connection con = JButil.getConnection();
		String sql = "INSERT INTO weather_sk "
				+ "(cityId, cityName, lastUpdate, temperature, windSpeed, windPower,"
				+ " windDirectionEn, windDirection, humidity, weather, weatherEn, weatherCode,"
				+ " visibility, airPressure, rain24h, rain, aqi_pm25, time, date) "
				+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
		QueryRunner qr = new QueryRunner();
		int m = 0;
		try {
    
			m = qr.update(con, sql, cityId, cityName,
					Timestamp.valueOf(new Date().toLocaleString()),
					temperature, windSpeed, windPower, windDirectionEn,
					windDirection, humidity, weather, weatherEn, weatherCode,
					visibility, airPressure, rain24h, rain, aqi_pm25, time,
					date);
		} catch (SQLException e) {
    
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
    
			DbUtils.closeQuietly(con);
		}

	}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_38072683/article/details/85766118

智能推荐

dubbo源码解析-router_Franco蜡笔小强的博客-程序员宅基地_dubbo router 源码解析

Dubbo源码解析系列文章均来自肥朝简书前言估算了一下,dubbo里面涉及的东西还是比较多的.比如谈到框架的时候,设计模式都是一个老生常谈的话题,再比如我们开发中我们不常用的一些概念,spi、javassist,以及和zookeeper相关的一些知识,比如ZKClient的使用,这些和dubbo关系很密切,但是这些假如我不做一些前戏铺垫就直接把源码贴出来,那真的没啥意义.因为看源码还是要有...

Windows使用PPOCRLabel_uncle_ll的博客-程序员宅基地_ppocrlabel 运行崩溃

问题想使用PaddleOcr下面的标注工具PPOCRLabel进行图像文本检测标注,在windos下安装出现了一些问题,耗费了半天时间解决了这个问题项目地址:https://github.com/PaddlePaddle/PaddleOCR安装包requirements:shapelyscikit-imageimgaug==0.4.0pyclipperlmdbtqdmnumpyvisualdlpython-Levenshteinopencv-contrib-python==4.4.

本地地址映射成外网地址工具-ngrok_红的羊的博客-程序员宅基地

本地地址映射成外网地址工具-ngrok下载地址:https://ngrok.com/下载完成后,解压缩,已个ngrok.exe文件,直接双击启动。输入: ngrok http 82 这个命令 就是将本地的 82端口衍射成前面的地址,并且端口号是80,如下图这样映射地址就能访问地址...

ps在线版 Photoshop在线精简版-toolfk程序员在线工具网_Hi LearnFk的博客-程序员宅基地_线上ps

     本文要推荐的[ToolFk]是一款程序员经常使用的线上免费测试工具箱,ToolFk 特色是专注于程序员日常的开发工具,不用安装任何软件,只要把内容贴上按一个执行按钮,就能获取到想要的内容结果。ToolFk还支持  BarCode条形码在线生成、 QueryList采集器、 PHP代码在线运行、 PHP混淆、加密、解密、 Python代码在线运行、JavaScript在线运行、YAML格式...

USACO Transformations_Cifer的博客-程序员宅基地_usacotransformation

~~~题目链接~~~题目大意:一块N x N(1<=N<=10)正方形的黑白瓦片的图案要被转换成新的正方形图案。写一个程序来找出将原始图案按照以下列转换方法转换成新图案的最小方式:#1:转90度:图案按顺时针转90度。#2:转180度:图案按顺时针转180度。#3:转270度:图案按顺时针转270度。#4:反射:图案在水平方向翻转(形成原图案的镜像)。#5:

<em>标签与<strong>标签区别_weixin_33736649的博客-程序员宅基地

&lt;em&gt;标签与&lt;strong&gt;标签区别&lt;em&gt; 标签告诉浏览器把其中的文本表示为强调的内容。把这段文字用斜体来显示。尽 管现在 &lt;em&gt; 标签修饰的内容都是用斜体字来显示,但这些内容也具有更广泛的含义,将来的某一天,浏览器也可能会使用其他的特殊效果来显示强调的文本。如果只想使用斜体 字来显示文本的话,请使用 &lt;i&gt; 标签。除此之外...

随便推点

android使用高德地图api_大大蘑菇猪的博客-程序员宅基地

使用高德地图获取当前定位和显示定位地图。1、添加应用key 主要填写sha1安全码和包名 SHA1码:可在eclipse下,Window->Preferences->Android->Build,SHA1 fingerprint。 Package:自定义,后面开发时创建的包名要对应,例如:com.amap.navi.demo2、创建工程 创建时包名要定义为co

【剑指offer】29、 顺时针打印矩阵__Volcano_我是火山君的博客-程序员宅基地

题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。解题思路:矩阵的行数是rows,矩阵的列数是cols,有一个矩阵是这样的,设这个矩阵的左上角的点的坐标是(0,0),该矩阵如下:(0,0) (0,1) (0,2) (0,3)(1,0) (1,1) (1,2) (1,3)(2,0) (2,1) (2,2) (2,3)(3,0) (3,1) (3,2)...

渲染系统中MRT和RTT的应用组合(WEBGL2 GLSL ES3实现)_含影的博客-程序员宅基地

Demo:http://www.artvily.com/sample?sample=mrtgl2上图是先实施了两个目标输出的MRT然后绘再使用这个MRT的输出结果来制场景到一个RTT中最后又被一次绘制CUBE使用,之后输出到屏幕。(其实MRT和RTT只是输出目标是多个和一个的区别, 当然glsl代码也有区别)我的渲染系统的设计思路是用 RTT/MRT控制对象来管理这个流程。我的渲染系统...

ubuntu 18.04安装MariaDB 10.04并通过远程navicat连接_瑾怀轩的博客-程序员宅基地

一、安装mariadb-server换镜像sudo apt-get install software-properties-commonsudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8sudo add-apt-repository ‘deb [arch=amd64,arm64,ppc64el] http://mirrors.tuna.tsinghua.edu.cn/mar

css鼠标拖拉卡顿_vue中解决拖拽改变存在iframe的div大小时卡顿问题_白如冰的博客-程序员宅基地

写在最前针对于在vue中实现拖拽改变两左右个div大小的方式,请查看上一篇文章《vue中实现拖动调整左右两侧div的宽度》。此文章主要针对于实际应用中需要拖拽改变大小的组件中使用iframe框架时存在明显卡顿的问题,比如这样,右侧div中使用了一个iframe组件,导致实际操作中出现两个问题,一个是拖不动,另外一个是无法根据鼠标移动,快速响应,甚至在监听鼠标的按下和松开事件上都有明显的卡顿问题。如...

veeam虚拟机备份及恢复_nowaynoco的博客-程序员宅基地_veeam恢复

备份环境搭建1、系统准备:windows server 20192、软件准备压缩软件链接:https://pan.baidu.com/s/1shaKl1btZYiNSqjOljX2IQ提取码:7qus应用软件建一个veeam文件夹将第一个iso文件解压到veeam目录下解压veeam-Crack安装过程参考:https://blog.csdn.net/qq43748322/article/details/78730207连接虚拟机数据中心vsp...

推荐文章

热门文章

相关标签