241 lines
9.0 KiB
JavaScript
241 lines
9.0 KiB
JavaScript
const { Builder, By, until } = require('selenium-webdriver');
|
||
const fs = require('fs-extra');
|
||
const path = require('path');
|
||
const unzipper = require('unzipper');
|
||
const chrome = require('selenium-webdriver/chrome');
|
||
|
||
// 変数定義
|
||
const URL = 'https://andpad.jp/my/orders';
|
||
const LOGIN_ID = 'k.nogi@next-hd.co.jp';
|
||
const LOGIN_PASSWORD = 'Next79324$#';
|
||
// ORDER_NUMBERはコマンドライン引数で指定
|
||
const ORDER_NUMBER = process.argv[2];
|
||
if (!ORDER_NUMBER) {
|
||
console.error('エラー: オーダー番号をコマンドライン引数で指定してください。\n例: node andpad_getPhotos.js 21404611');
|
||
process.exit(1);
|
||
}
|
||
const BASE_FOLDER = 'C:\\Users\\k.nogi\\Desktop\\デザイノANDPAD'; // Chrome default download folder
|
||
|
||
let driver;
|
||
|
||
async function main() {
|
||
try {
|
||
const options = new chrome.Options();
|
||
options.addArguments('--log-level=3'); // エラーのみ表示
|
||
options.addArguments('--disable-logging');
|
||
|
||
// Seleniumドライバ初期化
|
||
driver = await new Builder()
|
||
.forBrowser('chrome')
|
||
.setChromeOptions(options)
|
||
.build();
|
||
|
||
// ログイン処理
|
||
await loginWithDriver(driver);
|
||
|
||
// URLを開く
|
||
const photoUrl = `${URL}/${ORDER_NUMBER}/photos/folder`;
|
||
await driver.get(photoUrl);
|
||
|
||
// 件名を取得
|
||
const subject = await getSubject();
|
||
console.log('件名:', subject);
|
||
|
||
// ログイン後の要素が現れるまで待機(タイムアウト20秒に延長)
|
||
await driver.wait(until.elementLocated(By.xpath('//*[contains(text(), "フォルダ")]')), 20000);
|
||
|
||
// フォルダリンククリック
|
||
await driver.executeScript(`$('a:contains("フォルダ")')[0].click()`);
|
||
await driver.sleep(2000);
|
||
|
||
try {
|
||
const photoTopLink = await driver.findElement(By.xpath('//a[contains(text(), "写真トップ")]'));
|
||
await photoTopLink.click();
|
||
await driver.sleep(1000);
|
||
} catch (e) {
|
||
// "写真トップ"リンクがなければスキップ
|
||
}
|
||
|
||
// 写真トップの写真を保存
|
||
try {
|
||
await driver.findElement(By.xpath('//a[contains(text(), "すべて選択")]'));
|
||
|
||
// hidden inputからphotoTagIdを取得
|
||
const photoTagIdInput = await driver.findElement(By.css('input[name="current_photo_tag_id"]'));
|
||
const photoTagId = await photoTagIdInput.getAttribute('value');
|
||
const photoTagLabel = '写真トップ';
|
||
|
||
console.log(`処理開始: ${photoTagLabel} (ID: ${photoTagId})`);
|
||
|
||
// フォルダ構造を作成
|
||
const folderPath = path.join(BASE_FOLDER, subject, photoTagLabel);
|
||
await fs.ensureDir(folderPath);
|
||
console.log('フォルダ作成:', folderPath);
|
||
|
||
// 写真ダウンロード処理
|
||
await photoDownload(folderPath, photoTagId);
|
||
} catch (e) {
|
||
// "すべて選択"がなければスキップ
|
||
}
|
||
|
||
|
||
// フォトフォルダリストを取得
|
||
const folderElements = await driver.findElements(By.css('div.grid-photo-folder__element'));
|
||
console.log('フォルダ数:', folderElements.length);
|
||
|
||
// driver.quit()はループ後に実行
|
||
// 各要素を処理
|
||
for (let element of folderElements) {
|
||
const photoTagId = await element.getAttribute('data-photo-tag-id');
|
||
const photoTagLabel = await element.getAttribute('data-photo-tag-label');
|
||
|
||
// 要素数を取得
|
||
const countElement = await element.findElement(By.css('div.grid-photo-folder-element__count'));
|
||
const count = parseInt(await countElement.getText());
|
||
|
||
if (count >= 1) {
|
||
console.log(`処理開始: ${photoTagLabel} (ID: ${photoTagId})`);
|
||
|
||
// フォルダ構造を作成
|
||
const folderPath = path.join(BASE_FOLDER, subject, photoTagLabel);
|
||
await fs.ensureDir(folderPath);
|
||
console.log('フォルダ作成:', folderPath);
|
||
|
||
// 写真ダウンロード処理
|
||
await photoDownload(folderPath, photoTagId);
|
||
}
|
||
}
|
||
await driver.quit();
|
||
|
||
} catch (error) {
|
||
console.error('エラー:', error);
|
||
}
|
||
// finally節でdriver.quit()を呼ばないことで、Chromeを自動終了させない
|
||
}
|
||
|
||
async function loginWithDriver(driver) {
|
||
// Chromeウインドウを最大化
|
||
await driver.manage().window().maximize();
|
||
|
||
// ログイン画面へ遷移
|
||
await driver.get('https://andpad.jp/login');
|
||
|
||
// 「ログイン画面へ」ボタンをクリック
|
||
try {
|
||
const loginPageBtn = await driver.findElement(By.xpath('//input[@value="ログイン画面へ"]'));
|
||
await loginPageBtn.click();
|
||
await driver.sleep(1000);
|
||
} catch (e) {
|
||
// ボタンがなければスキップ
|
||
}
|
||
// ログインIDとパスワード入力
|
||
const loginIdInput = await driver.findElement(By.id('email'));
|
||
await loginIdInput.clear();
|
||
await loginIdInput.sendKeys(LOGIN_ID);
|
||
const passwordInput = await driver.findElement(By.id('password'));
|
||
await passwordInput.clear();
|
||
await passwordInput.sendKeys(LOGIN_PASSWORD);
|
||
|
||
// ログインボタン押下
|
||
const loginButton = await driver.findElement(By.css('button[type="submit"]'));
|
||
await loginButton.click();
|
||
|
||
// ログイン後のURLとタイトルを出力
|
||
await driver.sleep(1000);
|
||
|
||
}
|
||
|
||
async function getSubject() {
|
||
const subjectInput = await driver.findElement(By.css('input[placeholder="台帳名"]'));
|
||
return await subjectInput.getAttribute('value');
|
||
}
|
||
|
||
async function photoDownload(folderPath, photoTagId) {
|
||
const tagUrl = `${URL}/${ORDER_NUMBER}/photo_tags/${photoTagId}`;
|
||
|
||
// Chrome Preferencesで自動ダウンロード設定
|
||
const chrome = require('selenium-webdriver/chrome');
|
||
const prefs = {
|
||
'download.default_directory': folderPath,
|
||
'download.prompt_for_download': false,
|
||
'safebrowsing.enabled': false
|
||
};
|
||
const options = new chrome.Options();
|
||
options.setUserPreferences(prefs);
|
||
options.addArguments('--log-level=3'); // エラーのみ表示
|
||
options.addArguments('--disable-logging');
|
||
|
||
// Selenium起動(driver再起動)
|
||
let subDriver = await new Builder()
|
||
.forBrowser('chrome')
|
||
.setChromeOptions(options)
|
||
.build();
|
||
|
||
await loginWithDriver(subDriver);
|
||
|
||
// 指定タグURLへ遷移
|
||
await subDriver.get(tagUrl);
|
||
await subDriver.sleep(2000);
|
||
|
||
// 「写真をもっと見る」ボタンが表示されている間クリックを繰り返す
|
||
while (true) {
|
||
try {
|
||
// ng-hideが親divに無い場合のみクリック
|
||
const moreDiv = await subDriver.findElement(By.xpath('//div[contains(@ng-show,"hasNextPage()") and not(contains(@class,"ng-hide"))]'));
|
||
const moreBtn = await moreDiv.findElement(By.xpath('.//a[contains(text(),"写真をもっと見る")]'));
|
||
await moreBtn.click();
|
||
await subDriver.sleep(5000); // ロード待ち
|
||
} catch (e) {
|
||
// ボタンが見つからない場合は終了
|
||
break;
|
||
}
|
||
}
|
||
|
||
// "すべて選択"と書かれているaタグをクリック
|
||
await subDriver.findElement(By.xpath('//a[contains(text(), "すべて選択")]')).click();
|
||
await subDriver.sleep(1000);
|
||
// "ダウンロード"と書かれているaタグをクリック
|
||
await subDriver.findElement(By.xpath('//a[contains(text(), "ダウンロード")]')).click();
|
||
await subDriver.sleep(1000);
|
||
// "ダウンロードする"と書かれているinputタグをクリック
|
||
await subDriver.findElement(By.xpath('//input[@value="ダウンロードする"]')).click();
|
||
await subDriver.sleep(2000);
|
||
|
||
// ブラウザを閉じる
|
||
await subDriver.quit();
|
||
|
||
// ダウンロードしたzipファイルを解凍
|
||
await extractDownloadedZip(folderPath);
|
||
|
||
}
|
||
|
||
// 指定フォルダ内の最新のzipファイルをそのフォルダに解凍
|
||
async function extractDownloadedZip(folderPath) {
|
||
const fsPromises = require('fs').promises;
|
||
const files = await fsPromises.readdir(folderPath);
|
||
// zipファイルのみ抽出
|
||
const zipFiles = files.filter(f => f.endsWith('.zip'));
|
||
if (zipFiles.length === 0) {
|
||
console.log('zipファイルが見つかりません:', folderPath);
|
||
return;
|
||
}
|
||
// 最も新しいzipファイルを選択
|
||
let latestZip = zipFiles[0];
|
||
let latestTime = (await fsPromises.stat(path.join(folderPath, latestZip))).mtime;
|
||
for (const zip of zipFiles) {
|
||
const stat = await fsPromises.stat(path.join(folderPath, zip));
|
||
if (stat.mtime > latestTime) {
|
||
latestZip = zip;
|
||
latestTime = stat.mtime;
|
||
}
|
||
}
|
||
const zipPath = path.join(folderPath, latestZip);
|
||
console.log('解凍開始:', zipPath);
|
||
await fs.createReadStream(zipPath)
|
||
.pipe(unzipper.Extract({ path: folderPath }))
|
||
.promise();
|
||
console.log('解凍完了:', folderPath);
|
||
}
|
||
|
||
main();
|