Actually, that data does exist as a query on api.weather.gov (for /points/{lat,long}/forecast/hourly) which returns JSON like {
"@context": {
"wx": "https://api.weather.gov/ontology#",
"geo": "http://www.opengis.net/ont/geosparql#",
"unit": "http://codes.wmo.int/common/unit/",
"@vocab": "https://api.weather.gov/ontology#"
},
"geometry": "GEOMETRYCOLLECTION(POINT(-122.0220167 37.2668315),POLYGON((-122.0385572 37.275548,-122.0330058 37.2536879,-122.005479 37.2581132,-122.011025 37.2799738,-122.0385572 37.275548)))",
"updated": "2018-12-28T17:11:06+00:00",
"units": "us",
"forecastGenerator": "HourlyForecastGenerator",
"generatedAt": "2018-12-28T18:26:47+00:00",
"updateTime": "2018-12-28T17:11:06+00:00",
"validTimes": "2018-12-28T11:00:00+00:00/P8DT2H",
"elevation": {
"value": 121.0056,
"unitCode": "unit:m"
},
"periods": [
{
"number": 1,
"name": "",
"startTime": "2018-12-28T10:00:00-08:00",
"endTime": "2018-12-28T11:00:00-08:00",
"isDaytime": true,
"temperature": 48,
"temperatureUnit": "F",
"temperatureTrend": null,
"windSpeed": "12 mph",
"windDirection": "NNW",
"icon": "https://api.weather.gov/icons/land/day/few?size=small",
"shortForecast": "Sunny",
"detailedForecast": ""
},
{
"number": 2,
"name": "",
"startTime": "2018-12-28T11:00:00-08:00",
"endTime": "2018-12-28T12:00:00-08:00",
"isDaytime": true,
"temperature": 51,
"temperatureUnit": "F",
"temperatureTrend": null,
"windSpeed": "12 mph",
"windDirection": "NNW",
"icon": "https://api.weather.gov/icons/land/day/few?size=small",
"shortForecast": "Sunny",
"detailedForecast": ""
},
{
"number": 3,
"name": "",
"startTime": "2018-12-28T12:00:00-08:00",
"endTime": "2018-12-28T13:00:00-08:00",
"isDaytime": true,
"temperature": 54,
"temperatureUnit": "F",
"temperatureTrend": null,
"windSpeed": "12 mph",
"windDirection": "NNW",
"icon": "https://api.weather.gov/icons/land/day/few?size=small",
"shortForecast": "Sunny",
"detailedForecast": ""
},
{
"number": 4,
"name": "",
"startTime": "2018-12-28T13:00:00-08:00",
"endTime": "2018-12-28T14:00:00-08:00",
"isDaytime": true,
"temperature": 55,
"temperatureUnit": "F",
"temperatureTrend": null,
"windSpeed": "14 mph",
"windDirection": "N",
"icon": "https://api.weather.gov/icons/land/day/few?size=small",
"shortForecast": "Sunny",
"detailedForecast": ""
},
{
"number": 5,
"name": "",
"startTime": "2018-12-28T14:00:00-08:00",
"endTime": "2018-12-28T15:00:00-08:00",
"isDaytime": true,
"temperature": 57,
"temperatureUnit": "F",
"temperatureTrend": null,
"windSpeed": "14 mph",
"windDirection": "N",
"icon": "https://api.weather.gov/icons/land/day/few?size=small",
"shortForecast": "Sunny",
"detailedForecast": ""
},
...
]
}
In their wisdom, the request to /points/{lat,long}/forecast/hourly results in a 301 redirect to /gridpoints/{WFO}/{gridpoint}/forecast/hourly which has the real data. Grumble. But that's the way they've set it up.
I have a mod to the advforecast2.php JSON version which with auto-fetch the associated hourly forecast JSON every hour and save it as forecast-{ZONE}-{N}-json-hourly.txt in the cache location. I've attached the file if you are interested.