通过http请求获取天气信息:
这里借鉴一下 中国气象局网站举例
首先根据网址 分析:
http://weather.cma.cn/
通过vscode插件:REST Client 发送请求我们会得到内容
首先我们的打开浏览器调试工具查看请求格式
筛选以下几个关键的格式,试着用插件发送请求
GET /web/weather/57516.html HTTP/1.1
Host: weather.cma.cn
Referer: http://weather.cma.cn/web/weather/58238.html
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36
可以看到我们获取到了内容,所有这个请求格式是正确的
所以单片机请求应该这样写:
这里用的库为ArduinoHttpClient.h
拿到一个库,首先应该是先看例程:
/*Dweet.io GET 客户端示例,使用 ArduinoHttpClient 库每隔十秒连接到 dweet.io,发送一个 GET 请求和请求体。使用 SSL 加密。展示了如何使用 Strings 组装路径和解析响应内容。dweet.io 预期的请求格式如下:https://dweet.io/get/latest/dweet/for/thingName更多关于 dweet.io 的信息,请参考 https://dweet.io/play/创建于 2016 年 2 月 15 日最后更新于 2019 年 1 月 22 日作者:Tom Igoe此示例代码属于公共领域,可自由使用
*/// 包含 ArduinoHttpClient 库和 WiFi101 库
#include <ArduinoHttpClient.h>
#include <WiFi101.h>// 包含敏感数据的头文件,如 WiFi 凭证
#include "arduino_secrets.h"// WiFi 设置
// 注意:请在 Secret 标签下的 arduino_secrets.h 文件中输入您的敏感数据
char ssid[] = SECRET_SSID; // WiFi 网络名称
char pass[] = SECRET_PASS; // WiFi 密码const char serverAddress[] = "dweet.io"; // 服务器地址
int port = 80; // 服务器端口
String dweetName = "scandalous-cheese-hoarder"; // 在此处使用您自己的设备名称// 创建 WiFi 客户端和 HTTP 客户端实例
WiFiClient wifi;
HttpClient client = HttpClient(wifi, serverAddress, port);
int status = WL_IDLE_STATUS; // WiFi 连接状态变量// 初始化函数
void setup() {// 开始串行通信Serial.begin(9600);while (!Serial);// 尝试连接到指定的 WiFi 网络while (status != WL_CONNECTED) {Serial.print("尝试连接到名为: ");Serial.println(ssid); // 输出网络名称// 连接到 WPA/WPA2 网络status = WiFi.begin(ssid, pass);}// 输出已连接的 WiFi 网络的 SSIDSerial.print("SSID: ");Serial.println(WiFi.SSID());// 输出 WiFi 盾牌的 IP 地址IPAddress ip = WiFi.localIP();Serial.print("IP 地址: ");Serial.println(ip);
}// 主循环函数
void loop() {// 组装 GET 请求的路径String path = "/get/latest/dweet/for/" + dweetName;// 发送 GET 请求Serial.println("发送 GET 请求");client.get(path);// 读取响应的状态码和响应体int statusCode = client.responseStatusCode();String response = client.responseBody();Serial.print("状态码: ");Serial.println(statusCode);Serial.print("响应: ");Serial.println(response);// 典型响应示例:// {"this":"succeeded","by":"getting","the":"dweets","with":[{"thing":"my-thing-name","created":"2016-02-16T05:10:36.589Z","content":{"sensorValue":456}}]}// 我们关心的是 "content": 数值// 现在解析响应,寻找 "content" 字段int labelStart = response.indexOf("content\":");// 找到 "content" 后的第一个 {int contentStart = response.indexOf("{", labelStart);// 找到紧接着的 },并获取大括号之间的内容int contentEnd = response.indexOf("}", labelStart);String content = response.substring(contentStart + 1, contentEnd);Serial.println(content);// 接下来获取冒号后的数值,并转换成整数int valueStart = content.indexOf(":");String valueString = content.substring(valueStart + 1);int number = valueString.toInt();Serial.print("数值字符串: ");Serial.println(valueString);Serial.print("实际数值: ");Serial.println(number);// 等待十秒钟Serial.println("等待十秒钟\n");delay(10000);
}
示例程序二
/*简单的 WebSocket 客户端示例,使用 ArduinoHttpClient 库连接到 WebSocket 服务器,并每隔 5 秒发送一条问候消息创建于 2016 年 6 月 28 日作者:Sandeep Mistry修改于 2019 年 1 月 22 日修改者:Tom Igoe此示例代码属于公共领域,可自由使用
*/// 包含 ArduinoHttpClient 库和 WiFi101 库
#include <ArduinoHttpClient.h>
#include <WiFi101.h>
// 包含敏感数据的头文件,如 WiFi 凭证
#include "arduino_secrets.h"// WiFi 设置
// 注意:请在 Secret 标签下的 arduino_secrets.h 文件中输入您的敏感数据
char ssid[] = SECRET_SSID; // WiFi 网络名称
char pass[] = SECRET_PASS; // WiFi 密码char serverAddress[] = "echo.websocket.org"; // 服务器地址
int port = 80; // 服务器端口// 创建 WiFi 客户端和 WebSocket 客户端实例
WiFiClient wifi;
WebSocketClient client = WebSocketClient(wifi, serverAddress, port);
int status = WL_IDLE_STATUS; // WiFi 连接状态变量
int count = 0; // 计数器,用于发送消息// 初始化函数
void setup() {// 开始串行通信Serial.begin(9600);while (status != WL_CONNECTED) {Serial.print("尝试连接到名为: ");Serial.println(ssid); // 输出网络名称// 连接到 WPA/WPA2 网络status = WiFi.begin(ssid, pass);}// 输出已连接的 WiFi 网络的 SSIDSerial.print("SSID: ");Serial.println(WiFi.SSID());// 输出 WiFi 盾牌的 IP 地址IPAddress ip = WiFi.localIP();Serial.print("IP 地址: ");Serial.println(ip);
}// 主循环函数
void loop() {// 开始 WebSocket 客户端Serial.println("启动 WebSocket 客户端");client.begin();// 当客户端与服务器保持连接时while (client.connected()) {Serial.print("发送问候 ");Serial.println(count); // 输出计数器的值// 发送一条文本类型的消息client.beginMessage(TYPE_TEXT);client.print("hello "); // 发送问候语client.print(count); // 发送计数器的值client.endMessage(); // 结束消息发送// 增加计数器的值,用于下一次消息count++;// 检查是否有消息可接收int messageSize = client.parseMessage();if (messageSize > 0) {Serial.println("收到一条消息:");Serial.println(client.readString()); // 输出接收到的消息}// 等待 5 秒钟delay(5000);}Serial.println("已断开连接"); // 当不再连接时输出
}
单片机代码:
然而结果不太理想
那么这里就会有一个问题了:为什么没有反馈
我最开始的时候也是百思不得其解,甚至还尝试更换库
然而终于,在2024/7/27 今天的 0:15分我_悟了:想明白一件事是很有成就感的(被bug折磨得人都要疯了)
一切全部都是 网址的问题,因为我一开始就错了:
现在我们从头开始复盘:
首先,请求下发,得到如下结果:
最开始,我们以为请求结果由html生成
所以测试用例为:
当我们用这种方式去请求之后,确实如愿的取得了页面数据
当单片机发送请求之后,由于网页内容太多,加上,大部分内容都是由js控制动态生成.
所以获取页面信息然后走爬虫的路子,基本上要放弃.
直到刚刚,我才知道有这么一个请求,它就是数据!! json的交互数据请求: 是不是很眼熟? 53698!
打开看看: 原来如此
看ip地址大致能猜出意思
api嘛就是 接口 now 现在 后面这个数字多半就是 表某城市 数字
验证一下: 很明显,数据正常!
更改单片机HTTP请求之后结果:
串口输出结果正常
,那么问题解决了.下一步就应该是json解析了.
===前面的都是废话,点击 一键直达 结果===
获取天气,分3步:
第一步:得到请求地址: http://weather.cma.cn/api/now/54511
拆分为:
服务器地址:weather.cma.cn
天气请求地址:/api/now/
城市代码:54511 (北京)
第二步:编写请求代码,这里以 HttpClient.h 库 举例
WiFiClient ClientA;
// 设置一个 联网 后客户端实例 类似于打开浏览器HttpClient clienta(ClientA,"weather.cma.cn",80);
//把这个客户端 绑定到这个服务器 类似于用浏览器打开了这一个网页clienta.kUserAgent="User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36";
//一个通用的浏览器标识 类似于告诉服务器 我是一个正儿八经的浏览器 不要拒绝我
//实际上单片机不设置这条,它也能...................clienta.get("/api/now/53698");
//发送get请求 类似这样 GET /api/now/53698 HTTP/1.1String buf=clienta.responseBody();
//获取返回的结果 Serial.println(buf);
//串口输出
第三步:处理json数据了,这里用 ArduinoJson.h 库举例
//json数据解析
DynamicJsonDocument doc(1024); // 动态分配内存用于存储JSON数据
deserializeJson(doc, buf);// 将JSON数据反序列化为doc
JsonObject locationobj= doc["data"]["location"].as<JsonObject>();// 获取JSON对象 data中locationJsonObject nowobj = doc["data"]["now"].as<JsonObject>();// 获取JSON对象data中now
JsonObject lastUpdateobj= doc["data"]["lastUpdate"];// 获取JSON对象data中lastUpdate
String strjson=locationobj["path"];// 获取JSON对象data中location中的path属性int index1 = strjson.indexOf(","); // 查找第一个逗号的位置int index2 = strjson.indexOf(",", index1 + 1); // 查找第二个逗号的位置String part1 = strjson.substring(0, index1); // 截取第一个部分String part2 = strjson.substring(index1 + 1, index2); // 截取第二个部分String part3 = strjson.substring(index2 + 1); // 截取第三个部分Serial.println("国家"+part1);// 输出第一个部分Serial.println("城市"+part2);// 输出第二个部分 这一步:还需进一步处理,测试结果显示补全Serial.println("区县"+part3);// 输出第三个部分Serial.println("温度"+String(nowobj["temperature"])+"℃");Serial.println("湿度"+String(nowobj["humidity"])+"%");Serial.println("气压"+String(nowobj["pressure"])+"hPa");// 获取时间 这一步:还需进一步处理,测试结果显示补全
Serial.println("时间:"+String(lastUpdateobj["lastUpdate"]));