Help us understand the problem. What is going on with this article?

rubyでredmineのAPIkeyを取得してみる

経緯

 仕事でrubyを使ってredmineのAPIkeyを取得できるようにしろと仰せつかった。
 ひとまずの完成を見たので、自分の備忘録代わりにまとめておく。
 
 記事としては独学で勉強しただけでプログラマに就職した社会人一年目初心者プログラマが検索とコピペを繰り返して作ったものでしかない。もし自分と同じような人間がいたら今度はこの記事だけで一発成功することを祈って記述する。

方法

 まずは検索して見たが、該当する記述は見つからなかった。APIkeyはredmineの個人設定画面から受け取ってコードに入力するのが前提で、APIkeyをわざわざプログラムで取得するなどという方法は全然見つからなかった。が、rubyという条件を除外して検索した結果、以下の記事を発見した。

RedmineのJavaScriptから各種データを取得する方法
https://qiita.com/forenoonM/items/7f42701b2ea40353a820

 この記事内にはJavaScriptを使ってAPIkeyを取得する方法が記述されている。ざっくりいえばスクレイピング を使った方法だ。

スクレイピング 実践

 なるほど、スクレイピング か。さて、恥ずかしながら私はスクレイピング がなんたるかすら知らなかった。なにせrubyを独学で勉強していただけの初心者なので。
 というわけで、rubyでスクレイピング する方法を検索し、以下のサイトにたどり着いた。

【5分で理解】Rubyでスクレイピングの基礎を解説!
https://www.sejuku.net/blog/13230

 こうして作ったコードがこれ

equire 'open-uri'
require 'nokogiri' #APIキー取得に使う
url = "http://192.168.33.11/my/api_key"

charset = nil

html = open(url) do |page|
 charaset = page.charset
 page.read
end

contents = Nokogiri::HTML.parse(html,nil,charset)
apikey = contents.search("div[@class='box']").search('pre')
puts apikey

 さて、実際に動かすと動かない。
 色々調べた結果、ログイン画面で止まっていることに気づいた。

 という事はログインの処理を作らねばならない

ログイン処理作成

 で、例によって検索でたどり着いたのがこのページ。

Mechanizeでログインしてスクレイピングする
https://qiita.com/katsuyuki/items/1a78360988d96eec1d54

 なるほど完全に理解した。

agent = Mechanize.new
agent.user_agent = 'Mac Safari'
agent.get("http://#{adress}/my/api_key") do |page|
  response = page.form_with(:action => '/login') do |form|
    formdata = {
       :mail => mail,
       :password => password,
    }
    form.field_with(:name => 'username').value = formdata[:mail]
    form.field_with(:name => 'password').value = formdata[:password]
  end.submit
 end
 url = "http://#{adress}/my/api_key"
 html = agent.get("#{url}").content.toutf8
 contents = Nokogiri::HTML(html, nil, 'utf-8')
 apikey = contents.search("div[@class='box']").search('pre')
 puts apikey

 追加部分だけを抜き出した。

 ちなみに#{adress}については「今はローカルでテストしているが、この後会社のredmineとクライアントのredmineでも動かすんだから」と

require 'yaml' #コンフィグを読むのに使う

set = YAML.load_file("config.yml")
puts set
adress = set["train"]["adress"]
puts adress

 こんな感じでconfig.ymlにアドレスを入力することで指定できるようにしたものだ。

結果

> <pre>[APIkey]</pre>

 これではダメだ。

stringkey = "#{apikey}"
stringkey.slice!("</pre>")
stringkey.slice!("<pre>")
puts "APIKey = #{stringkey}"

 これを直後に加えて、ひとまずAPIkeyを取得する処理は完成。

もしログイン済みだったら?

 実はこっちを先に取り掛かっていたのだが、もしログイン画面ではなかったらどうだろう? そんなことがあるのかわからないが、もしあったら逆にログインする処理でエラーを起こしてしまう。

page = RedmineController.new
pagetitle = page.Myaccount_access(adress)
puts "pagetitle = #{pagetitle}"

if pagetitle == "個人設定 - redmine" then # ログイン済みかどうかチェックする
     apikey = page.GetAPIkey(adress)
     puts "APIKey = #{apikey}"
else
     apikey = page.Login(adress,mail,password) 
end

 ログイン後の個人設定画面はタイトルが「個人設定 - redmine」になっていたので、これに対応した。

 ここでRedmineContorollerなるクラスが作られているが、このクラスについては後述する。

 この後、会社のredmineで試して気づいた。うちの会社のredmineは「個人設定 - [会社名]redmine 」になっている。
 調べたところ、トップページなども全て「[会社名]redmine」だ。つまり、「個人設定 - [ページ名]」というスタイルのようだ。対応しよう。

   page = RedmineController.new
   toptitle = page.toptitle(adress)
   pagetitle = page.Myaccount_access(adress)
   puts "pagetitle = #{pagetitle}"

   if pagetitle == "個人設定 - #{toptitle}" then # ログイン済みかどうかチェックする
     apikey = page.GetAPIkey(adress)
     puts "APIKey = #{apikey}"
   else
     apikey = page.Login(adress,mail,password) 
   end

 こうだ。つまり、トップのタイトルを先に把握しておく作戦だ。
 さて、ではクラス「RedmineController」を公開する。そう、toptitleメソッドのことを紹介しておかないと二度手間なので紹介した次第である。

class RedmineController

 def toptitle(adress)
    url = "http://#{adress}/"

    charset = nil

    html = open(url) do |page|
     charaset = page.charset
     page.read
    end

    contents = Nokogiri::HTML.parse(html,nil,charset)
 return contents.title
 end

 def Myaccount_access(adress)
    url = "http://#{adress}/my/account"

    charset = nil

    html = open(url) do |page|
     charaset = page.charset
     page.read
    end

    contents = Nokogiri::HTML.parse(html,nil,charset)
 return contents.title
 end

 def Login(adress,mail,password)
    agent = Mechanize.new
    agent.user_agent = 'Mac Safari'
    agent.get("http://#{adress}/my/api_key") do |page|
      response = page.form_with(:action => '/login') do |form|
        formdata = {
           :mail => mail,
           :password => password,
        }
        form.field_with(:name => 'username').value = formdata[:mail]
        form.field_with(:name => 'password').value = formdata[:password]
      end.submit
     end
     url = "http://#{adress}/my/api_key"
     html = agent.get("#{url}").content.toutf8
         contents = Nokogiri::HTML(html, nil, 'utf-8')
         apikey = contents.search("div[@class='box']").search('pre')
         stringkey = "#{apikey}"
     stringkey.slice!("</pre>")
     stringkey.slice!("<pre>")
     puts "APIKey = #{stringkey}"
     return stringkey
 end

 def GetAPIkey(adress)
        url = "http://#{adress}/my/api_key"

        charset = nil

        html = open(url) do |page|
         charaset = page.charset
         page.read
        end

        contents = Nokogiri::HTML.parse(html,nil,charset)
    apikey = contents.search("div[@class='box']").search('pre')
        stringkey = "#{apikey}"
    stringkey.slice!("</pre>")
    stringkey.slice!("<pre>")
    puts "APIKey = #{stringkey}"
    return stringkey
 end

end

そして完成へ

 で、この後APIを使ってなんやかんやする処理を作る必要があるので、APIkeyを得るための処理はメソッドに押し込もう。

class Getter
 def APIKey_Getter(adress,mail,password)
   page = RedmineController.new
   toptitle = page.toptitle(adress)
   pagetitle = page.Myaccount_access(adress)
   puts "pagetitle = #{pagetitle}"

   if pagetitle == "個人設定 - #{toptitle}" then # ログイン済みかどうかチェックする
     apikey = page.GetAPIkey(adress)
     puts "APIKey = #{apikey}"
   else
     apikey = page.Login(adress,mail,password) 
   end
   return apikey
 end

 ちなみにこの後、今はこのゲッタークラスに各種APIを呼び出すメソッドを追加している。

全容

 以上で持って完成。全容はこんな感じである。

require 'open-uri'
require 'nokogiri' #APIキー取得に使う
require 'mechanize' #ログインに使う
require 'yaml' #コンフィグを読むのに使う
require 'io/console' #パソワードの伏字に使う

class RedmineController

 def toptitle(adress)
    url = "http://#{adress}/"

    charset = nil

    html = open(url) do |page|
     charaset = page.charset
     page.read
    end

    contents = Nokogiri::HTML.parse(html,nil,charset)
 return contents.title
 end

 def Myaccount_access(adress)
    url = "http://#{adress}/my/account"

    charset = nil

    html = open(url) do |page|
     charaset = page.charset
     page.read
    end

    contents = Nokogiri::HTML.parse(html,nil,charset)
 return contents.title
 end

 def Login(adress,mail,password)
    agent = Mechanize.new
    agent.user_agent = 'Mac Safari'
    agent.get("http://#{adress}/my/api_key") do |page|
      response = page.form_with(:action => '/login') do |form|
        formdata = {
           :mail => mail,
           :password => password,
        }
        form.field_with(:name => 'username').value = formdata[:mail]
        form.field_with(:name => 'password').value = formdata[:password]
      end.submit
     end
     url = "http://#{adress}/my/api_key"
     html = agent.get("#{url}").content.toutf8
         contents = Nokogiri::HTML(html, nil, 'utf-8')
         apikey = contents.search("div[@class='box']").search('pre')
         stringkey = "#{apikey}"
     stringkey.slice!("</pre>")
     stringkey.slice!("<pre>")
     puts "APIKey = #{stringkey}"
     return stringkey
 end

 def GetAPIkey(adress)
        url = "http://#{adress}/my/api_key"

        charset = nil

        html = open(url) do |page|
         charaset = page.charset
         page.read
        end

        contents = Nokogiri::HTML.parse(html,nil,charset)
    apikey = contents.search("div[@class='box']").search('pre')
        stringkey = "#{apikey}"
    stringkey.slice!("</pre>")
    stringkey.slice!("<pre>")
    puts "APIKey = #{stringkey}"
    return stringkey
 end
end

class Getter
 def APIKey_Getter(adress,mail,password)
   page = RedmineController.new
   toptitle = page.toptitle(adress)
   pagetitle = page.Myaccount_access(adress)
   puts "pagetitle = #{pagetitle}"

   if pagetitle == "個人設定 - #{toptitle}" then # ログイン済みかどうかチェックする
     apikey = page.GetAPIkey(adress)
     puts "APIKey = #{apikey}"
   else
     apikey = page.Login(adress,mail,password) 
   end
   return apikey
 end
end


#ここから処理開始。実際にはsinatraでgetかpostで情報を受け取る
puts "アカウント名を入力してください"
mail = gets
mail.chomp!
puts "パスワードを入力してください(表示されませんが入力できています)"
password = STDIN.noecho(&:gets)
password.chomp!
#実際にはgetsを使って取得する訳ではないが、一応設定。最終的にはこの範囲は全て消える

set = YAML.load_file("config.yml")
puts set
adress = set["train"]["adress"]
puts adress
f = Getter.new
apikey = f.APIKey_Getter(adress,mail,password) 
puts "APIKey = #{apikey}"

以上、お目汚し失礼しました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした