JavaRush /Java 博客 /Random-ZH /我们在几个晚上为 Telegram 创建了一个简单的天气机器人
Philip J.
第 40 级
Днепр

我们在几个晚上为 Telegram 创建了一个简单的天气机器人

已在 Random-ZH 群组中发布
大家好!为 Telegrams 创建机器人的主题有些陈词滥调,并且已经编写了很多指南(例如,这个。因此,我们最好仔细研究一下使用一些第三方 API,因为这对于任何 Web 开发人员来说都是一项关键技能。我马上就会说,该应用程序并没有打算提供最实用、最有用的预报;与天气网站竞争是没有意义的;学习如何使用 Java 进行远程连接和数据解析非常重要。所以,我们首先要弄清楚我们需要什么。我们的应用程序本质上由三个逻辑部分组成:
  • 接受用户的消息
  • 处理该消息,如果它是有效命令,则为响应准备数据。在我们的例子中,如果用户输入了正确的城市,则准备天气预报
  • 在聊天中向用户发送准备好的信息
Первый и третий пункт довольно прост, и касается только работы с Telegram API, желающие могут изучить ссылку, оставленную выше. Мы же заострим внимание на втором пункте. API используют тогда, когда одни разработчики хотят предоставить доступ к своим данным другим разработчика. Возьмём, к примеру, Вконтакте. У каждого есть список друзей, он хранится где-то в базе данных на serverах ВК. Предположим, что Howой-то программист решил создать игру в шашки с друзьями. Whatбы его приложение работало правильно, программа должна уметь получать список друзей любого игрока. Для этого программист находит documentацию к ВК API, и смотрит, Howой request нужно сделать, чтобы это список получить. Этот request называется HTTP-requestом. А два самых распространённых HTTP-requestа — это GET и POST. Про них в сети тоже достаточно, останавливать не буду. Для наших целей, а именно получения данных прогноза погоды будет достаточно простого GET-requestа. Если обратиться с GET requestом к обычному веб-serverу, он, зачастую, вернёт html-code, который браузер преобразует в удобную для пользователя page, применив стor, скрипты и пр. Если же мы обращаемся с таким requestом к API serverу, в ответ обычно возвращаются только сырые данные без стилей и скриптов. В окне браузера это выглядит примерно так: 我们在几个晚上为 Telegram 创建了一个简单的天气机器人 - 1Эти данные предназначены не для людей, а для других программ, поэтому ничего лишнего, кроме самой информации в таких pageх нет. Сырые данные чаще всего пересылают по одному из двух стандартов: JSON or XML. У каждого свои плюсы и минусы, однако, важно разбираться в обоих. JSON вы уже видели на скрине выше, а ХМL выглядит вот так: 我们在几个晚上为 Telegram 创建了一个简单的天气机器人 - 2После недолгого поиска, был найден англоязычный проект Open Weather Map, который отдаёт данные бесплатно, если не делать больше 50 requestов в minutesу. Для нас этого вполне достаточно, регистрируемся, получаем уникальный токен(code) по которому server будет знать, что мы не самозванцы, а прorчные будущие разработчики. Заходим на page с API-documentацией (тыц), и выясняем, что прогноз на 5 дней по любому городу можно получить, если отправить request вида.
https://api.openweathermap.org/data/2.5/forecast?q=(город)&APPID=(уникальный токен, полученный при регистрации)
Можно посмотреть How ответ на такой request выглядит в браузере тыц Мы уже выяснor, что, по сути, нужно просто перейти по правильной ссылке и server выдаст нужные данные. Осталось научиться это делать средствами Java. Простой GET request на Java выглядит так:
//создаём строку со ссылкой на нужную page,
//я тут её склеиваю из заранее определённых констант, меняя только сам город
String urlString = API_CALL_TEMPLATE + city + API_KEY_TEMPLATE;
//создаём an object который будет содержать ссылку
URL urlObject = new URL(urlString);
//создаём соединение, используя an object
HttpURLConnection connection = (HttpURLConnection) urlObject.openConnection();
//выбираем тип requestа (GET)
connection.setRequestMethod("GET");
//тут мы указываем, данные о себе, что мы можем принять всё то,
//что примет и любой современный браузер
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
//В начало ответа server всегда вставляет число, по которому можно судить, прошло ли всё хорошо.
//200 - значит OK
int responseCode = connection.getResponseCode();
//на несуществующий город or город с опечаткой, server выдаст code ответа 404,
//бросаем на него исключение, чтобы обработать на уровне повыше и предложить
//пользователю ввести город заново
if (responseCode == 404) {
     throw new IllegalArgumentException();
}
// создаём поток, вычитываем все строки, и склеиваем в одну большую строку,
//которую будем потом обрабатывать в других методах
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
     response.append(inputLine);
}
in.close();
return response.toString();
如果请求正确并且服务器可用,我们将收到一张数据,其中有用的信息与现在不需要的信息混合在一起。为了方便地从 JSON 和 XML 中提取必要的数据,已经为 Java 编写了大量的库来满足各种需求。由于我更喜欢​​ JSON,所以我选择了一个非常流行的库 Jackson 来处理它。顺便说一句,在 JavaRush 中对它进行了一些高水平的研究。要处理大量 JSON 数据,了解文档的结构非常重要。像这样的有用网站可以拯救你。左边是原始 JSON,右边是结构化 JSON: 我们在几个晚上为 Telegram 创建了一个简单的天气机器人 - 3 可以看出,响应由 5 个顶级 JSON 对象组成,其中 2 个很复杂,是以下分支的节点。我们感兴趣的数据存储在列表节点中。内部列表是一个由 38 行 JSON 行组成的数组,每行描述特定时间的天气。也就是说,它是一种树状结构,有根、树枝、细枝甚至叶子:)并且在节点处发生分支。幸运的是,Jackson 可以将任何有效的 JSON 表示为树。因此,知道我们需要的属性的名称(例如,气温)以及它位于树的哪一层,获取它就不成问题了。首先,我从“list”数组中提取所有行并将它们添加到一个单独的列表中。粗略地说,我将包含数据的工作表切成了几块,每块都是一个单独的预测。小零件更容易记住和操作。
//JsonNode - это один из узлов в древовидной иерархии, от которого идут ветви
//получаем узел, который называется "list"
JsonNode arrNode = new ObjectMapper().readTree(data).get("list");
//если это действительно массив узлов
if (arrNode.isArray()) {
//выполняем для каждого узла, который содержится в массиве
      for (final JsonNode objNode : arrNode) {
//в атрибуте "dt_txt" каждого маленького узла хранилось время прогноза, я отобрал данные за 9 утра и 6 вечера
                String forecastTime = objNode.get("dt_txt").toString();
                if (forecastTime.contains("09:00") || forecastTime.contains("18:00")) {
                weatherList.add(objNode.toString());
            }
      }
}
这为我们提供了一个字符串列表,其中每个字符串代表特定时间天气的 JSON 报告。剩下的就是提取您想要的内容并对其进行格式化。如果我们有这样一行:
"main":{"temp":261.45,"temp_min":259.086,"temp_max":261.45,"pressure":1023.48,"sea_level":1045.39,"grnd_level":1023.48,"humidity":79,"temp_kf":2.37}
那么这是一个名为“main”的节点。要从中获取任何数据,例如海平面,以下代码就足够了:
ObjectMapper objectMapper = new ObjectMapper();
//line - это наша JSON-строка
mainNode = objectMapper.readTree(line).get("main");
String seaLevel = mainNode.get("sea_level");
Jackson 允许您立即以数字格式表示数据:
double seaLevel = mainNode.get("sea_level").asDouble();
现在我们可以从预测中提取任何数据,剩下的就是根据需要将其粘合在一起并将其发送给 Telegram 中的用户。完整的源代码位于我的github上,但您可以使用链接尝试运行该机器人,或者在 Telegram 搜索中查找 @denifoBot。城市名称必须用拉丁语音译书写,例如“基辅”或“莫斯科”。谢谢,如果你坚持到了最后,我接受合理的批评,因为我只是在 GitHub 上学习和开发简单的项目,以便与我所在城市的饥饿人才竞争:) 大家再见!PS我相信这里可能有错别字,所以你可以在私人消息中发送所有内容,或者如果你真的很生气,也可以在评论中发送:)
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION