LoginSignup
17
20

More than 5 years have passed since last update.

[Ruby]確定申告の時期だからAmazonから注文履歴をスクレイピングしてみるメモ

Posted at

はじめに

確定申告用にAmazonでの買い物履歴が欲しかったのだが、Amazon.jpではCSVとかがなさそうなので、自動で取ってくるスクリプトを書いてみました。
一度に5件(?)以上買い物していると「◯個すべての商品を表示」みたいなリンクが出るのですが、そこは対応していません。

Rubyは初心者なので、注文履歴をゲットしてくる処理と、HTMLを読み込んでCSVに変換する処理をわけて書いています。

準備

mechanizeとnokogiriを使用するのでインストールしておきます。

$ gem install mechanize
$ gem install nokogiri

HTMLの取得

下記のソースでHTMLを取ってきます。

get.rb
require 'open-uri'
require 'mechanize'
require 'kconv'

agent = Mechanize.new
agent.user_agent = 'Mac Safari'

url = 'https://www.amazon.co.jp/ap/signin?_encoding=UTF8&openid.assoc_handle=jpflex&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.mode=checkid_setup&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.ns.pape=http%3A%2F%2Fspecs.openid.net%2Fextensions%2Fpape%2F1.0&openid.pape.max_auth_age=0&openid.return_to=https%3A%2F%2Fwww.amazon.co.jp%2F%3Ftag%3Dhydraamazonav-22%26hvadid%3D39595899217%26hvpos%3D1t1%26hvexid%3D%26hvnetw%3Dg%26hvrand%3D8895839300291415748%26hvpone%3D%26hvptwo%3D%26hvqmt%3De%26hvdev%3Dc%26ref%3Dnav_custrec_signin'

page = agent.get(url)

login_form = page.forms_with(:name => 'signIn').first
login_form.fields_with(:name => 'email').first.value = "Amazon登録メールアドレス"
login_form.fields_with(:name => 'password').first.value = "Amazonパスワード"
page2 = login_form.click_button

this_year =2015 #任意の集計開始年
charset = nil
index = 1

url = "https://www.amazon.co.jp/gp/your-account/order-history?opt=ab&digitalOrders=1&unifiedOrders=1&returnTo=&__mk_ja_JP=カタカナ&orderFilter=year-#{this_year}"
puts 'get', url
page = agent.get(url)
puts page.title
has_emphasis = agent.page.search('//a[@class="a-size-medium a-link-emphasis"]')
if !has_emphasis.empty? then
  puts "page1 に未取得のデータがあります。"
end

s = agent.page.body.to_s
File.open("#{this_year}-#{index}.html", "w"){ |f|
  f.puts s
}

while true do
  href = agent.page.search('//ul[@class="a-pagination"]/li[@class="a-last"]/a/@href').text
  puts 'get', 'https://www.amazon.co.jp' + href
  button = agent.page.link_with(:href => href)
  if button == nil then
    break
  end
  page = button.click

  has_emphasis = agent.page.search('//a[@class="a-size-medium a-link-emphasis"]')
  if !has_emphasis.empty? then
    puts "page#{index+1} に未取得のデータがあります。"
  end

  index += 1
  s = agent.page.body.to_s
  File.open("#{this_year}-#{index}.html", "w"){ |f|
    f.puts s
  }
end

$ ruby get.rbで実行すると、2015-01.htmlなどのファイルが作成されます。

CSVへ変換

csv.rb
require 'open-uri'
require 'nokogiri'
require 'csv'
require 'kconv'
require 'date'

this_year =2015 # 集計する年
format = '%Y年%m月%d日'

current_dir = Dir.pwd
dataList = []
num = 0

while true
  num += 1
  filename = "#{this_year}-#{num}.html"
  if ! File.exist?(filename) then
    break
  end

  puts "Loading... #{filename}"
  html = Nokogiri::HTML(File.open(filename))

  orders = html.xpath('//div[@class="a-box-group a-spacing-base order"]')

  orders.each_with_index.map do | row, index |
    if row.respond_to?('xpath') then

      order_number = row.xpath('div[1]/div/div/div/div[2]/div[1]/span[2]').text.strip
      puts "order: #{order_number}"

      date_label = row.xpath('div[1]/div/div/div/div[1]/div/div[1]/div[1]/span[@class="a-color-secondary label"]').text.strip
      date_value = row.xpath('div[1]/div/div/div/div[1]/div/div[1]/div[2]/span[@class="a-color-secondary value"]').text.strip
      date = DateTime.strptime(date_value, format)

      sum_label = row.xpath('div[1]/div/div/div/div[1]/div/div[2]/div[1]/span[@class="a-color-secondary label"]').text.strip
      sum_value = row.xpath('div[1]/div/div/div/div[1]/div/div[2]/div[2]/span[@class="a-color-secondary value"]').text.strip.gsub(/¥ */, "").gsub(/,/, "").to_i

      to_label = row.xpath('div[1]/div/div/div/div[1]/div/div[3]/div[1]/span[@class="a-color-secondary label"]').text.strip
      to_value = row.xpath('div[1]/div/div/div/div[1]/div/div[3]/div[2]/span[@class="a-color-secondary value"]').text.strip

      lefts = row.xpath('.//div[@class="a-fixed-left-grid-inner"]')
      lefts.each do | node |
        title = node.xpath('div[2]/div/a[@class="a-link-normal"]').text.strip
        if title == nil then
          next
        end
        if title.length == 0 then
          title = node.xpath('div[2]/div[1]').text.strip # Androd app
        end

        img = node.xpath('div[1]/div[1]/a/img/@src').text.strip
        author = node.xpath('div[2]/div/span[@class="a-size-small"]').text.strip
        seller = node.xpath('div[2]/div/span[@class="a-size-small a-color-secondary"]').text.strip.gsub(/([\s| | ]+)/," ")
        price = node.xpath('div[2]/div/span[@class="a-size-small a-color-price"]').text.strip.gsub(/¥ */, "").gsub(/,/, "").to_i
        if price == 0 then
          price = sum_value
        end
        ver = node.xpath('div[2]/div/span[@class="a-size-small a-color-secondary a-text-bold"]').text.strip  # Kindle?

        hash = Hash.new
        hash['order'] = order_number
        hash['date'] = date
        hash['title'] = title
        hash['author'] = author
        hash['price'] = price
        hash['ver'] = ver
        hash['img'] = img
        hash['seller'] = seller        

        if hash.size > 0 then
          dataList << hash
        end
      end

    end

  end

end

dataList = dataList.sort_by { |hash| hash['date'] }

CSV.open('amazon.csv','wb') do |csv|
  csv << ['date','order', 'title','author','price', 'kindle']

  dataList.each do |hash|
    date = hash['date'].strftime('%Y年%m月%d日')
    order = hash['order']
    title = hash['title']
    author = hash['author']
    kindle = hash['ver']
    price = hash['price']

    csv << [date, order, title, author, price, kindle]
  end

end

$ ruby csv.rbで上記のスクリプトを実行すると、amazon.csvというファイルが作成されます。

注意

個人的にはそこまで正確な金額が必要なかったので、きちんと取得していない部分もありますのでご注意ください。

参考

[Mechanize]リンクをクリックする方法いろいろ
RubyのMechanizeとNokogiriで読書メーターをスクレイピング

個人的覚書

スクレイピング対象のHTMLページの構造を把握するには、Chromeなどで構造をみるのが良い。

また、XPATHを知りたい場合には、右クリックして「Copy」->「Copy XPATH」を駆使すると比較的簡単。

17
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
20