Ruby、UdemyAPIを使ってUdemyの講座情報を取得してみた

# はじめに
Rubyを使って、UdemyAPIを叩き、講座情報を取得してみました。
Rubyのバージョンは2.6.5です。

# やったこと
キーワードに該当するUdemyの講座情報を取得し、その講座タイトルを出力する処理をしました。
※取得する講座情報は、日本語の講座のみに限定しました。
※[こちらの記事](https://programming-beginner-zeroichi.jp/articles/215)をとてもとても参考にさせていただきました。
※[UdemyAPIの公式ドキュメント](https://www.udemy.com/developers/affiliate/)を確認し、作成しました。

## 手順1.UdemyAPIの取得
手順は次のとおり。
①Udemyアカウントを作る。
②Udemyにログインし、アカウント設定画面に移動
③アカウント設定画面にてAPIクライアントをクリック
④APIクライアント（下記画面）にて、[AffiliateAPIクライアントをリクエストする]をクリックし、必要事項を入力し、[保存]をクリックする。
⑤リクエストが承認されると、クライアントIDとクライアントパスワードがUdemyから付与される。
このクライアントIDとクライアントパスワードをUdemyAPIから情報を取得する時に使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629310/37e54a53-fe0c-4377-7dd8-dfdc87c3e1a8.png)

## 手順2.使用するGemをインストールします。
```
gem install faraday
```

[Faraday](https://lostisland.github.io/faraday/)を使用するので、上記コマンドをターミナル上で入力し、インストールします。
※Faradayとは、HTTPクライアントライブラリ。
UdemyAPIはHTTPでのやり取りが必要なAPIなのでFaradayを使用します。


## 手順3.コードを書く
先に今回のコードをすべて記載して、それぞれ説明していきます。
※特に関係ありませんが、私はUdemy.rbというファイルに下記コードを書いてます。

```Ruby:Udemy.rb
require 'json'
require 'faraday'

class Udemy
  # Udemyにアクセスする際のベースとなるURLを定義する
  URL = 'https://www.udemy.com/api-2.0/courses/?language=ja&search='.freeze

  def initialize(keyword)
    self.search(keyword)
  end

  def search(keyword)
    # URI.encodeをしないと、日本語が文字化けするため使用
    search_url = URI.encode("#{URL}#{keyword}")
    udemy_response = self.get_response(search_url)
    # udemy_responseのstatusが200の場合は、response_success_actionを実行する
    return response_success_action(udemy_response) if udemy_response.success?
  end

  def get_response(search_url)
    response = Faraday.new(url: search_url) do |req|
      # 毎回基本認証パラメーターをリクエスト情報に含める必要があるため
      req.basic_auth(UDEMYクライアントID, UDEMYクライアントパスワード)
      # Net::HTTPライブラリを使って、HTTP形式にするためのコード（たぶん。。。）
      req.adapter :net_http
    end
    # UdemyAPIへGETリクエストをする
    response.get
  end

  def response_success_action(udemy_response)
    # udemy_response.bodyをRubyオブジェクトに変換
    udemy_info = JSON.parse(udemy_response.body)
    # 最大100ページまで結果を取得できるので,1..100に設定
    (1..100).each do |i|
      udemy_info["results"].each do |info|
        p info["title"] # タイトルを出力
      end
      # 1リクエストあたり20件の情報がudemy_responseに格納される。
      # 21件目以降の情報を取得するには、udemy_info["next"]のURLにリクエストを送る必要がある。
      # udemy_info["next"]がnilの場合は、該当の情報がこれ以上ないため、breakで処理を終える。
      break if udemy_info["next"].nil?
      udemy_info = JSON.parse(self.get_response(udemy_info["next"]).body)
    end
  end
end

```

#### ① Udemyにリクエストする際のベースとなるURLを定義する。
```Ruby
  URL = 'https://www.udemy.com/api-2.0/courses/?language=ja&search='.freeze
```
- `https://www.udemy.com/api-2.0/courses/`は、Udemyのコース情報を取得する際のベースとなるURLです。
- `language=ja`は、日本語の講座情報を取得するためです。
- `search=`は、キーワード検索のためです。

#### ② UdemyAPIにリクエストする情報をHTTP形式にして、UdemyAPIへGETリクエストをする。
```Ruby
  def get_response(search_url)
    response = Faraday.new(url: search_url) do |req|
      # 毎回基本認証パラメーターをリクエスト情報に含める必要がある。
      # 実際に記述する場合は.envファイルにIDとパスワードを記述してENV[UDEMY_CLIENT_ID]のように取得してください。
      req.basic_auth(UDEMYクライアントID, UDEMYクライアントパスワード)
      # Net::HTTPライブラリを使って、HTTP形式にするためのコード（たぶん。。。）
      req.adapter :net_http
    end
    # UdemyAPIへGETリクエストを送信
    response.get
  end
```

##### ・基本認証パラメーター
[UdemyAPIの公式ドキュメント](https://www.udemy.com/developers/affiliate/)には、下記のとおり記載されてます。
> Udemy Affiliate APIには、クライアント認証のための基本認証パラメーターが必要です。認証パラメーターは呼び出しのたびに送信されなければなりません。認証パラメーターが送信されなかった場合は、 401 UNAUTHORIZEDエラーが表示されます。

つまり、UdemyAPIにリクエストする際には、毎回基本認証パラメーター（1で取得したクライアントID, クライアントパスワード）も含めて、リクエストする必要があります。`req.basic_auth(クライアントID, クライアントパスワード)`はそのためのコードです。

##### ・Net::HTTPライブラリを使って、HTTP形式にする。
[こちらのサイト](http://nekorails.hatenablog.com/entry/2018/09/28/152745#001-Faraday%E3%81%A8%E3%81%AF)から下記説明を引用しました。
> 実はFaraday自体は実際にはHTTPリクエストをしないんだ。リクエスト処理はNet::HTTPやHTTPClientなどの他のライブラリに任せているんだよ。これらをアダプタと言うよ。アダプタを切り替えることで、FaradayのAPI(メソッド)は同じまま、リクエスト部分のライブラリを変えることができるよ。

`req.adapter :net_http`は、Net::HTTPライブラリを使って、HTTP形式にするためのコードです。（たぶん・・・）

##### ・UdemyAPIへGETリクエストをする。
上記処理でresponseに格納されたHTTP形式のリクエスト情報を、`get`メソッドを使ってGETリクエストをし、UdemyAPIからのレスポンスを取得します。

#### ③ ②で取得したUdemyAPIからの情報を基に、講座のタイトルを出力する。
```Ruby
  def response_success_action(udemy_response)
    # udemy_response.bodyをJSON形式に変更
    udemy_info = JSON.parse(udemy_response.body)
    # 最大100ページまで結果を取得できるので,1..100に設定
    (1..100).each do |i|
      udemy_info["results"].each do |info|
        p info["title"] # タイトルを出力
      end
      # 1リクエストあたり20件の情報がudemy_responseに格納される。
      # 21件目以降の情報を取得するには、udemy_info["next"]のURLにリクエストを送る必要がある。
      # udemy_info["next"]がnilの場合は、該当の情報がこれ以上ないため、breakで処理を終える。
      break if udemy_info["next"].nil?
      udemy_info = JSON.parse(self.get_response(udemy_info["next"]).body)
    end
  end
```
##### ・UdemyAPIの仕様について
UdemyAPIでは、キーワードに該当する講座が100件あったとしても、UdemyAPIからのレスポンスに格納される講座情報は、1リクエストあたり20件までです。
21件目以降の講座情報を取得するには、レスポンス内の["next"]キー内に格納されたURLにリクエストすることで、都度20件ずつ情報を取得できます。
レスポンス内の["next"]キー内がnilになった場合、これ以上は該当の講座はないという意味になります。

##### ・今回のコードについて
UdemyAPIからのレスポンスに格納される講座情報の講座タイトルを都度出力し、レスポンス内の["next"]キー内に格納されたURLがあれば、そのURLにリクエストして、講座情報を取得・出力する。["next"]キーがnilだったら、処理を終えることにしてます。

## コードの実行結果について
Railsというキーワードに該当する講座情報を取得するために、`Udemy.new("Rails")`とし、実行した結果が下記のとおりです。
ちなみに、この下にも講座タイトルがズラッと表示されますが、タイトルにRailsが含まれていない講座もあり、何故その講座情報を取得しているかは、検証しておりません・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629310/322d849f-3aa2-2034-cf42-026a912b2071.png)

## 最後に
かなりざっくりした説明になっており恐縮ですが、UdemyAPIの参考記事があまりなかったので、書いてみました。
間違いなどあれば、ご指摘いただければ幸いです。

## 参考
- https://programming-beginner-zeroichi.jp/articles/215
- https://www.udemy.com/developers/affiliate/
- http://nekorails.hatenablog.com/entry/2018/09/28/152745#001-Faraday%E3%81%A8%E3%81%AF
- https://codezine.jp/article/detail/11462