33
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[ruby] webdriver を使って amazon.co.jp での購入履歴情報を取得する

Last updated at Posted at 2014-08-09

課題

amazon.co.jp での今年に購入した電子書籍の電子領収書を一括で取得する。(MacOSX, firefox の環境で)

電子書籍を大量に購入していて、その購入領収書を提出する必要がある場合に利用する。

方法

  1. ruby で selenium-webdriver をつかって firefox を自動操作して、
    amazon の購入履歴ページ (2014 年分) の全画面の html ソースを取得する。
    amazon のページにアクセスするための情報はコマンドラインの引数として渡す。
  2. 各購入品の領収書のページにアクセスして、スクリーンショットを取得する。
$ ruby amazon.rb 登録メールアドレス パスワード

プログラムコードはこの記事の末尾に示す。

プログラムを実行すると、firefox が自動起動する。
自動で amazon へのログイン、購入履歴ページの閲覧、ログアアウト、firefox の終了が行われる。
実行後は、./screeshots 以下に (page-*.html:一覧ページ、 order-*.png:領収書) が保存される。
./scrennshots フォルダはあらかじめ作成しておく。

後処理の例

上のプログラム実行で情報は取得できるが、これを加工する例を2つ示す。

  1. 電子領収書を1つの PDF ファイルにまとめる。
convert -resize 575x823 -gravity north -background white -extent 595x842 *.png 1.pdf

生成した PDF の例:
amazon-orders.png

  1. 表計算ソフトで管理できるように [購入日、価格、値引き額、タイトル、商品情報 URL] を
    csv 形式で得る。
$ ruby make-index.rb > 1.csv
$ cat head.csv 1.csv > 2.csv

プログラムコードはこの記事の末尾に示す。

生成した csv を元に列名、価格合計セルを追加した例:

amazon-order-index.png

ソースコード

Gemfile

source "https://rubygems.org"

gem 'selenium-webdriver'
gem 'nokogiri'

amazon.rb

amazon.rb
# -*- coding: utf-8 -*-

# 1. amazon の購入履歴を取得する。(scrennshots/* に保存される)
#   $ ruby amazon.rb email password
#
# 2. 取得した情報から、明細書(*.png) を1つの PDF にまとめたものを作成する。
#    (imagemagic の convert コマンドを使う)
#   $ convert -resize 575x823 -gravity north -background white -extent 595x842 screenshots/ord*.png 1.pdf
#
# 3. 取得した情報から、csv 形式で購入物一覧表を作成する。
#   $ ruby make-index.rb > 1.csv

require 'rubygems'
require 'selenium-webdriver'

SCREENSHOTS_DIR = './screenshots'

module Amazon
  class Driver
    # 新しいタブで 指定された URL を開き、制御をそのタブに移す。
    def open_new_window(wd, url)
      a = wd.execute_script("var d=document,a=d.createElement('a');a.target='_blank';a.href=arguments[0];a.innerHTML='.';d.body.appendChild(a);return a", url)
      a.click
      wd.switch_to.window(wd.window_handles.last)

      wd.find_element(:link_text, '利用規約')
      yield
      wd.close
      wd.switch_to.window(wd.window_handles.last)
    end

    # 現在の画面からリンクが張られている購入明細を全て保存する。
    def save_order(wd)
      wd.find_element(:link_text, '利用規約')
      orders = wd.find_elements(:link_text, '領収書/購入明細書')
      orders.each do |ord|

        open_new_window(wd, ord.attribute('href')) do
          @order_seq += 1
          wd.save_screenshot("#{SCREENSHOTS_DIR}/order_#{format('%03d', @order_seq)}.png")
        end
      end
    end

    def save_order_history(wd, auth)
      @page_seq = 0
      @order_seq = 0

      # 購入履歴ページへ
      wd.get 'https://www.amazon.co.jp/gp/css/order-history'

      # ログイン処理
      wd.find_element(:id, 'ap_email').click
      wd.find_element(:id, 'ap_email').clear
      wd.find_element(:id, 'ap_email').send_keys auth[:email]

      wd.find_element(:id, 'ap_password').click
      wd.find_element(:id, 'ap_password').clear
      wd.find_element(:id, 'ap_password').send_keys auth[:password]

      wd.find_element(:id, 'signInSubmit-input').click

      unless wd.find_element(:xpath, "//form[@id='order-dropdown-form']/select//option[4]").selected?
        wd.find_element(:xpath, "//form[@id='order-dropdown-form']/select//option[4]").click  # 今年の注文
      end
      wd.find_element(:css, "#order-dropdown-form > span.in-amzn-btn.btn-prim-med > span > input[type=\"submit\"]").click

      # [次] ページをめくっていく
      loop do
        wd.find_element(:link_text, '利用規約')
        @page_seq += 1
        wd.save_screenshot("#{SCREENSHOTS_DIR}/page_#{format('%03d', @page_seq)}.png")
        open("#{SCREENSHOTS_DIR}/page_#{format('%03d', @page_seq)}.html", 'w') {|f|
          f.write wd.page_source
        }

        # ページ中の個々の注文を閲覧する。
        save_order(wd)

        elems = wd.find_elements(:link_text, '次へ »')
        break if elems.size == 0
        elems[0].click
      end

      # サインアウト
      wd.get 'http://www.amazon.co.jp/gp/flex/sign-out.html/ref=gno_signout'
    end
  end
end

include Amazon

if ARGV.size != 2
  puts "usage: ruby #{$PROGRAM_NAME} account password"
  exit 1
end

wd = nil
begin
  ad = Amazon::Driver.new
  wd = Selenium::WebDriver.for :firefox
  wd.manage.timeouts.implicit_wait = 20 # 秒
  ad.save_order_history(wd, email: ARGV[0], password: ARGV[1])
ensure
  wd.quit if wd
end

ポイント

一覧ページの領収書のリンクを click すると、ページ遷移して領収書画面が表示される。
1つの一覧ページは 10 個の領収書のリンクがある。
"領収書リンクの click", "スクリーンショット取得"、"前ページに戻る" の繰り返し操作では一覧ページを何度もロードすることになる。
そこで、領収書画面は別ウィンドウで開くようにし、スクリーンショットを取得したら、そのウィンドウを閉じる という操作をプログラムで行わせることにした。一覧ページを何度もロードすることを避けている。

webdriver では、

  1. javascript をつかって別 window を開く
  2. 開いた window に制御を移す。そして 領収書画面を load する。
  3. 処理が終わったら、window を close する。
  4. 一覧ページの window に制御を戻す。
    という操作を行わせている。

make-index.rb

make-index.rb
# -*- coding: utf-8 -*-

require 'open-uri'
require 'nokogiri'
require 'csv'

# 値引き額を得る
# @param url 書籍情報ページの IRL
# @return 値引き額
def saving(url)
  off = 0
  begin
    charset = nil
    html = open(url) do |f|
      charset = f.charset
      f.read # htmlを読み込んで変数 htmlに渡す
    end
    doc = Nokogiri::HTML.parse(html, nil, charset)
    # 値引きデータを抜き出す。
    price = doc.css('.savingsRow .price')
    if price && price.size > 0
      off = price.text.scan(/.+/)[0].scan(/\d+/).join('')
    end
  rescue => ex
    STDERR.puts url
    STDERR.puts ex
  end
  off.to_i
end

# 購入物の一覧を csv 形式で得る。(購入日、価格、値引額、タイトル)
# @return csv 形式の文字列
def generate_csv
  csv_string = CSV.generate do |csv|
    Dir::glob("screenshots/**/*.html").each do |path|
      f = File.open path

      page = Nokogiri::XML f
      orders = page.css('.action-box')
      orders.each do |order|
        date = order.css('.order-level > h2').text
        price = order.css('.price').text.scan(/\d+/).join('')
        title =  order.css('.item-title').text.strip
        url = order.css('.shipment a').attribute('href').value
        off = saving(url)
        csv <<  [date, "#{format('%8d', price)}", off, title, url]
        end
      f.close
    end
  end
  csv_string
end

puts generate_csv

head.csv

head.csv
日付,価格,OFF,書名,URL
,=SUM(B3:B300),=SUM(C3:C300),

ポイント

値引き額は、購入一覧ページ、領収書ページには記載されていない。
商品ページを訪れ、その中の値引き額情報を検索するようにしている。
購入一覧先にある商品ページは存在していないことがある。(価格や情報が更新された?)
その場合はプログラムを中断せずに、STDERR にメッセージを出して、最後の商品まで処理を行うようにしている。

更新履歴

  • 2019-03-09
    png から pdf を作る ruby スクリプトを追加した。
    1 ページに2件、10ページの pdf を生成することができる。

  • 2014-01-04
    amazon.co.jp からの購入履歴 (領収書ページ) を得るスクリプトを更新した。
    (amazon の購入履歴ページも構造が半年前から変化したため)
    https://github.com/katoy/amazon-orders/blob/master/amazon.rb

33
30
1

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
33
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?