-
-
Save tkrkt/4a44950385fcdfbe0b01cb3f95f7bb68 to your computer and use it in GitHub Desktop.
Amazonで買ったもの集計
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
// 使い方: | |
// 1. 全部コピーする (右上の Raw をクリックした先でやるのが楽) | |
// 2. Amazon の注文履歴ページ ( https://www.amazon.co.jp/gp/css/order-history/ ) を開く | |
// 3. F12 または 右クリ→要素の検証 とかで出てくる開発者ツールのコンソール (JavaScript REPL) にペースト | |
// 4. エンターで実行 | |
// | |
// format: | |
// type outputJSON = Array<{ | |
// product: string; // ファイル名 | |
// href: string; // 商品ページURL | |
// asin: string; // ASIN | |
// date: string; // 購入日 "YYYY年MM月DD日" | |
// price: number; // 金額 | |
// }>; | |
(function() { | |
const inputYear = () => { | |
return +window.prompt('何年分の注文を集計しますか?', '2018'); | |
}; | |
const fetchDoc = async (year, page = 0) => { | |
const res = await fetch(`https://www.amazon.co.jp/gp/css/order-history?digitalOrders=1&unifiedOrders=1&orderFilter=year-${year}&startIndex=${page * 10}`, { | |
headers: { | |
'X-Requested-With': '' | |
} | |
}); | |
const text = await res.text(); | |
const doc = document.implementation.createHTMLDocument(""); | |
doc.documentElement.innerHTML = text; | |
return doc; | |
}; | |
const getText = (container, query) => { | |
const element = container.querySelector(query); | |
if (element) { | |
return element.textContent.trim(); | |
} else { | |
return ''; | |
} | |
}; | |
const getAttr = (container, query, attr) => { | |
const element = container.querySelector(query); | |
if (element) { | |
return element[attr] || ''; | |
} else { | |
return ''; | |
} | |
}; | |
const extractItems = (doc) => { | |
const result = []; | |
Array.from(doc.querySelectorAll('div.order')).forEach(container => { | |
const date = getText(container, 'div.order-info span.value'); | |
Array.from(container.querySelectorAll('.a-fixed-left-grid-col.a-col-right')).map(item => { | |
const product = getText(item, 'a.a-link-normal[href^="/gp/product/"]'); | |
const href = getAttr(item, 'a.a-link-normal[href^="/gp/product/"]', 'href'); | |
const group = /\/gp\/product\/([^\/]+)\//.exec(href); | |
const asin = (group && group[1]) ? group[1] : ''; | |
const price = +(getText(item, 'span.a-color-price') || getText(container, '.a-span2 .a-color-secondary.value')).replace(/[¥\s,]/g, '') || 0; | |
result.push({product, asin, href, date, price}); | |
}); | |
}); | |
return result; | |
}; | |
const hasNext = (doc) => { | |
const next = doc.querySelector('.pagination-full ul.a-pagination li:last-child'); | |
return next && !next.classList.contains('a-disabled'); | |
}; | |
const extractAllItems = async (year) => { | |
let page = 0; | |
let items = []; | |
while (true) { | |
console.log(`${year}年のデータ取得中... ${page+1}ページ目`); | |
// sleep | |
await new Promise(resolve => setTimeout(resolve, Math.random() * 2000)); | |
const doc = await fetchDoc(year, page); | |
items = [...items, ...extractItems(doc)]; | |
if (hasNext(doc)) { | |
page++; | |
} else { | |
break; | |
} | |
} | |
return items; | |
}; | |
const convertItemsToJSON = (items) => { | |
return JSON.stringify(items, null, 2); | |
}; | |
const convertItemsToCSV = (items, forExcel = false) => { | |
const lines = items.map(item => { | |
return `"${item.product.replace(/"/g, '\"')}",${item.date},${item.price}`; | |
}); | |
return `${forExcel ? 'sep=,\n' : ''}produce,date,price\n${lines.join('\n')}`; | |
}; | |
const downloadFile = (name, text) => { | |
const a = document.createElement('a'); | |
a.download = name; | |
a.href = URL.createObjectURL(new Blob([text], {type: 'text/plain'})); | |
a.click(); | |
}; | |
const main = async () => { | |
const year = inputYear(); | |
if (Number.isNaN(year) || !year) return; | |
const items = await extractAllItems(year); | |
console.log('取得完了', items); | |
console.log('合計金額', items.reduce((sum, i) => sum + i.price, 0)); | |
downloadFile(`amazon-${year}.json`, convertItemsToJSON(items)); | |
}; | |
main(); | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment