Java爬取中国天气网实况天气数据

版权声明:个人博客:yjz.life 欢迎前来交流!~本篇文章未经同意请勿转载! https://blog.csdn.net/m0_38072683/article/details/85766118

因实验室需求,需要找一个实况天气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);
		}

	}

猜你喜欢

转载自blog.csdn.net/m0_38072683/article/details/85766118