循环(更改日期)从网站中提取数据到工作表

时间:2019-04-29 09:49:49

标签: html excel vba web-scraping

首先,我是VBA的新手,并且想在Excel中建立天气统计信息。非常感谢您的帮助!

为此,我需要来自多个网站的数据,其中包括温度,气象站和时间的信息。

到目前为止,我已经在互联网上找到了一些VBA信息,并编写了代码,该代码仅在即时窗口中为我提供了一个网站所需的信息。

看起来像这样:

7.4°C | Wien-Mariabrunn (225m) | 14:00
7.6°C | Wien-Hohe Warte (198m) | 14:00
7.6°C | Wien-Unterlaa (200m) | 14:00
7.7°C | Wien-Schwechat (183m) | 14:00
7.8°C | Wien-Donaufeld (160m) | 14:00
8.1°C | Grossenzersdorf (154m) | 14:00
8.2°C | Wien-City (177m) | 14:00

Dim xmlReq As New MSXML2.XMLHTTP60
Dim HTMLDoc As New MSHTML.HTMLDocument

Dim Temps1 As MSHTML.IHTMLElementCollection
Dim temps2 As MSHTML.IHTMLElementCollection
Dim Temp As MSHTML.IHTMLElement

xmlReq.Open "GET", "https://kachelmannwetter.com/at/messwerte/wien/temperatur/20190101-1300z.html", False
xmlReq.send

If xmlReq.Status <> 200 Then
    MsgBox "Problem" & vbNewLine & xmlReq.Status & " - " & xmlReq.statusText
    Exit Sub
End If

HTMLDoc.body.innerHTML = xmlReq.responseText

Set Temps1 = HTMLDoc.getElementsByClassName("ap o o-1 o-tmp-5")
Set temps2 = HTMLDoc.getElementsByClassName("ap o o-1 o-tmp-1")

For Each Temp In Temps1
    Debug.Print Temp.Title
Next Temp

For Each Temp In temps2
    Debug.Print Temp.Title
Next Temp

我遇到的第一个问题是我不知道如何将这些信息放入表格或单元格中。

第二个问题是,从开始日期到结束日期,我需要来自多个网站的相同数据。 我在此示例代码中使用的网站是https://kachelmannwetter.com/at/messwerte/wien/temperatur/20190101-1300z.html。最后,您可以找到日期,在此示例中为“ 20190101”,时间为“ 1300”。 所以为此,我需要某种循环。

因此,最后我需要一个工作表,其中的A列带有日期,B列带有时间,C列带有数据(每个日期和时间)。

我希望这是可以理解的,我非常感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

以下内容适用于短日期范围。较长的日期范围会导致响应变慢。网站很可能被阻止/限制。为此:

  1. 我包含一个变量pauseIndex,这意味着每增加x个URL(= pauseIndex),在添加下一个请求之前将延迟y秒(由waitSeconds指定)。你可以玩这个。
  2. 您可能会考虑修改代码以按日/月批量运行,然后追加到现有数据集的底部,或使用当前的日/月作为标题写入新工作表。
  3. 也许轮换/更改IP并使用MSXML2.ServerXMLHTTP

使用上述修补程序获得最佳设置和批处理请求大小(如果进行批处理)。


指定开始日期时间和结束日期时间:

日期范围在名为Date ranges的工作表中指定。它具有以下设置:


构建数据集:

我建议您建立一个平面数据集,在其中指定要返回其信息的确切桩号。并非每个电台在每个时间日期都出现。

stations = Array("Wien-Schwechat", "Wien-Unterlaa", "Wien-Mariabrunn", "Wien-Hohe Warte", "Grossenzersdorf", _
                     "Wien-Donaufeld", "Wien-City")

您可以展开它。我包括一个词典变量newStations,该变量存储遇到的所有不在您的监视列表中的电台。您可以轻松地将其写出,以帮助确定要监视/包括在数据集中的其他电台。

缺少测站读数的占位符值用于确保完整的数据集。

您可能希望归一化“异常值”-例如,实际的小时值可以在范围内,而不是在小时上。在下面的演示中,对于一个工作站,准确地获取了16:20以下的内容。您可以将其标准化为16:00。


辅助功能/子:

代码中使用了许多辅助功能和1个子功能。

  1. GetAllLinks。生成开始日期时间和结束日期时间之间的所有请求URL。请参阅代码中的注释。这些可以循环发出每个数据请求
  2. EmptyDict-确保在请求之间清除站点数据
  3. UpdateDictForNoReading。处理在指定的日期时间未报告受监视的电台的情况。它使用"No reading"
  4. 更新温度和长站描述
  5. WriteOutResults。产生"flat"(即非嵌套的2D数组结构)并将结果写入指定的输出工作表

检索电台和电台数据:

我使用css attribute = value选择器和contains运算符来定位电台数据。

使用示例电台的HTML

<a class="ap o o-1 o-tmp--1" data-target="#obs-detail-3h" data-toggle="modal" data-left="635" data-top="545" onclick="obs_detail_3h('-1.0°C', 'Wien-Schwechat (183m)', '16:20','110360', '201901031500');" title="-1.0°C | Wien-Schwechat (183m) | 16:20" style="left: 408.533px; top: 337.757px;">-1</a>

如果我们查看class属性,我们将看到如下所示:

class="ap o o-1 o-tmp--1"

class属性的值为"ap o o-1 o-tmp--1",实际上是一系列用空格分隔的类。每个站类值具有相同的子字符串o-tmp。您可以对此稍作更改。我使用querySelectorAll返回所有在class属性值中具有此子字符串的元素的nodeList。

Set mapStations = html.querySelectorAll("[class*='o-tmp']")

这与页面(地图)上的所有站点相匹配。

nodeList(title)中每个节点的mapStations属性包含感兴趣的数据:

title="-1.0°C | Wien-Schwechat (183m) | 16:20"

该字符串包含竖线(|)分隔符。我可以使用split()来生成包含每个信息位的数组:

arr = Split(mapStations.item(i).Title, " | ")

这将生成一个数组,该数组将在不同的索引处具有-1.0°CWien-Schwechat (183m)16:20。在此示例中,我将-1.0°C存储在变量temp中,将Wien-Schwechat (183m)存储在变量stationFull中,而将站名Wien-Schwechat存储在station中,{{ 1}}在16:20中。


待办事项:

  1. 重构以减少嵌套级别
  2. 错误处理状态代码<> 200 .....
  3. 将变量声明移近使用范围

要求:

  1. VBE>工具>参考>添加对Microsoft HTML对象库的参考
  2. 名为time的工作表
  3. 名为Date ranges的工作表

Output中的数据应按照上图所示进行布局。


VBA:

Date ranges

示例输出: