通过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 网络的 SSID
Serial.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 网络的 SSID
Serial.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.1
String 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中location
JsonObject 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"]));