Last active
March 7, 2024 10:00
ナウキャストの画像取得(20240307版) by puppeteer/node.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const puppeteer = require('puppeteer'); | |
const fs = require('fs'); | |
require('date-utils'); | |
const { WebClient } = require('@slack/web-api'); | |
const setTimeout = require("node:timers/promises").setTimeout; | |
async function selectorClicker(page, selector) { | |
await page.waitForSelector(selector); | |
const elementHandleList = await page.$$(selector); | |
await elementHandleList[0].click(); | |
await setTimeout(1000); | |
} | |
const isExists = filePath =>{ | |
try{ | |
const data = fs.statSync(filePath); | |
return data; | |
}catch(e){ | |
return false; | |
} | |
} | |
async function slack_notify(msg) { | |
const token = 'your token'; | |
// #チャンネル名 of @ユーザー名 | |
const channel = '#somewhere'; | |
const username = 'username'; | |
// メッセージ | |
const client = new WebClient(token); | |
const params = { | |
channel: channel, | |
text: msg, | |
username: username | |
}; | |
const response = await client.chat.postMessage(params); | |
console.log(response); | |
} | |
(async () => { | |
const now = new Date(); | |
let dateTemp = new Date(); | |
dateTemp.setHours(now.getHours()-1); | |
const saveTimeOntheClock = new Date(dateTemp.getFullYear(), dateTemp.getMonth(), dateTemp.getDate(), dateTemp.getHours()); | |
// ブラウザ表示域のサイズで画像サイズが決まる | |
const browser = await puppeteer.launch({headless: true, 'defaultViewport' : { 'width' : 1048, 'height' : 1000 }}); | |
console.log(saveTimeOntheClock.toFormat('YYYY/MM/DD HH24:MI:SS')); | |
try{ | |
let loop_count = 0 | |
const page = await browser.newPage(); | |
const cdpSession = await page.target().createCDPSession(); | |
await cdpSession.send("Browser.setDownloadBehavior", { | |
behavior : 'allow', | |
downloadPath: fs.realpathSync('./') | |
}); | |
await page.goto('https://www.jma.go.jp/bosai/nowc/#zoom:N/lat:AAA.AAAAAA/lon:OOO.OOOOOO/colordepth:normal/elements:hrpns', {waitUntil: "domcontentloaded"}); | |
//await page.screenshot({path: 'jma000.png'}); | |
// 最新時刻への更新 | |
{ | |
const selector = 'button[id^="jmatile_refresh_"]'; | |
await selectorClicker(page, selector); | |
} | |
//await page.screenshot({path: 'jma001.png'}); | |
// セーブしたい時刻(X時55分)への移動 | |
while (loop_count < 60) { | |
const selector = 'div[class="noUi-tooltip"]'; | |
await page.waitForSelector(selector); | |
const elementHandleList = await page.$$(selector); | |
let value = await (await elementHandleList[0].getProperty('textContent')).jsonValue(); | |
console.log(value); | |
let clock = value.split(':'); | |
let hour = saveTimeOntheClock.getHours(); | |
//await page.screenshot({path: 'jma002_' + clock[0] + clock[1] + '.png'}); | |
if (clock[0] == hour && clock[1] == '55') { | |
loop_count = 0; | |
break; | |
} | |
// 5分過去へ | |
loop_count++; | |
{ | |
const selector = 'button[id^="jmatile_time_prev_"]'; | |
await selectorClicker(page, selector); | |
} | |
} | |
if (loop_count > 0) { | |
throw 'Give up!'; | |
} | |
// X時(5*i)分の画像取得 リカバリ分を含めて120分 | |
for(let i=0; i<120/5; i++){ | |
let clock = []; | |
{ | |
const selector = 'div[class="noUi-tooltip"]'; | |
await page.waitForSelector(selector); | |
const elementHandleList = await page.$$(selector); | |
const value = await (await elementHandleList[0].getProperty('textContent')).jsonValue(); | |
console.log(value); | |
clock = value.split(':'); | |
} | |
// 保存先を決める | |
const srcDate = new Date(dateTemp.getFullYear(), dateTemp.getMonth(), dateTemp.getDate(), clock[0], clock[1]); | |
const srcDateUTC = srcDate.toUTCFormat('YYYYMMDDHH24MISS'); | |
const srcfile = fs.realpathSync('./') + '/' + 'nowc_hrpns_' + srcDateUTC + '_' + srcDateUTC + '.png'; | |
const dstdir = fs.realpathSync('./') + '/' + saveTimeOntheClock.toFormat('YYYY/MM/DD'); | |
const dstfile = dstdir + '/' + saveTimeOntheClock.toFormat('YYYYMMDD') + '_' + clock[0] + clock[1] + '.png'; | |
fs.mkdirSync(dstdir, { recursive: true }); | |
if (isExists(dstfile) == false){ | |
// メニュー出し | |
{ | |
const selector = 'div[class="area-toggle"]'; | |
await selectorClicker(page, selector); | |
} | |
// 画像保存 | |
{ | |
const selector = 'div[class="area-download"]'; | |
await selectorClicker(page, selector); | |
} | |
let retry = 0; | |
while (isExists(srcfile) == false && retry < 10) { | |
await setTimeout(1000); // sure to be created | |
retry++; | |
} | |
if (isExists(srcfile) == false){ | |
console.log(srcfile + ' not found.'); | |
} | |
else{ | |
console.log(srcfile); | |
fs.renameSync(srcfile, dstfile); | |
} | |
} | |
// 5分過去へ | |
{ | |
const selector = 'button[id^="jmatile_time_prev_"]'; | |
await selectorClicker(page, selector); | |
} | |
//await page.screenshot({path: 'jma003_' + clock[0] + clock[1] + '.png'}); | |
} | |
} catch(e) { | |
console.log(e.message); | |
slack_notify('jmacapt.js: Error. check log.') | |
} finally { | |
await browser.close(); | |
} | |
})(); | |
waitForXPathやwaitForTimeoutが使えなくなるため、代替手段に切替え。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
久しぶりにOSを更新したついでに
Chrome DevTools Protocol
というものでダウンロード先を指定するようにした。