先日、Qiitaの大型アップデートが発表されました🎉
Qiita大型アップデートのベータ版リリースのお知らせ

その中でも特に魅力的だと感じたのが、記事のストックをカテゴリごとに分類出来る
というものでした!

ストック機能のリニューアル

ストック機能は文字通り、Qiitaで得た知識をストックできる機能として提供を行なってきました。
しかし、知識をストックする機能としては利便性に欠ける部分も多かったかと思います。
今回、ストック機能のリニューアルを行い、よりQiitaが知識のストック場所として機能できることを目指しました。

〜 中略 〜

カテゴリの追加

今までのストック機能では、タグによる絞り込みしか行えませんでした。今回、カテゴリという概念を追加し、ユーザーごとに自由に記事の分類を行えるようにしました。

これはありがたい!!
今までこんな無駄な作業をしていた覚えはありませんか?

  • 気になる記事はとりあえずストック!
  • あとから見返そうと思ったらどこに行ったか分からず…
  • 結局また検索
  • ストック済だったことを知る

僕はこんな作業を100万回経験しました🥺
そんな無駄な作業もリニューアル版Qiitaなら無縁ですね♫

そういえばストック記事ってどのくらいあるんだろう?と思って確認してみたところ、
僕は93件でした。
※リスティング広告はお気になさらず。。笑

スクリーンショット 2021-02-23 12.19.26.png

多分これってめっちゃ少ないですよね??
(他のみんなはどのくらいストックしてるんだろう??)
ただ、たったの93件でも手作業で消していくのって物凄く面倒なんですよ。。
ストックマークをポチポチポチ、次のページでもまたポチポチポチ。。。

ちょうどいい機会なのでQiita APIを使ってストック済の記事を全解除したろうと思います。
どうせ今のストック記事からは探さないし、カテゴライズし直すのも面倒だし、ね😉 💛

筆者の環境

OS: macOS Big Sur
Editor: VScode

下準備

  • curlのバージョン確認(なければインストール)
  • jqのバージョン確認(なければインストール)
  • Qiitaアカウントでアクセストークン発行

curlのバージョン確認(なければインストール)

僕自身、APIを叩いたことがほぼないので、まずは勉強がてらcurlコマンドとやらを使って
Qiita APIを叩いてみます。
そのためにはまずcurlが使えるか確認します。
ターミナルで下記コマンドを実行し、curlのバージョンを確認しましょう。

curl --version

もし入っていなければインストールします。

brew install curl

jqのバージョン確認(なければインストール)

jqはcurlで取得したjsonデータを見やすく整形してくれるコマンドです。
(正しくはコレも機能の一つに過ぎない。)
curlのときと同様にバージョン確認、なければインストールをします。

jq --version

もし入っていなければインストール。

brew install jq

Qiitaアカウントでアクセストークン発行

Qiitaのアカウント管理画面からアクセストークンを発行してください。
トークンはこのあと使うのでコピーしておいてください👍

この後もコピペをたくさんするので、上書きされない様にどこかにペーストしておくか、
Clipyのようなスニペットツールを使ってね😉

トークン発行後、タブやブラウザを閉じたり別のページに遷移してしまうと、
添付画面のようにトークンは非表示となってしまうのでご注意あれ。

スクリーンショット 2021-02-23 12.50.13.png

まずはQiita API(公式)を眺めてみる

Qiita API v2 公式ドキュメント
上記の公式ドキュメントをさらっと眺めてみます。

その中から、まずは試しに簡単そうなGET系APIを叩いてみたいと思います。
curlコマンドが分からなかったら自分で調べてね😉

タグ一覧を取得する

Qiita API v2 公式ドキュメント: タグ一覧を作成日時の降順で返します。

一番簡単なのはコレですかね🤔
タグの一覧を作成日時の降順(作成日時の新しい順)に取得する、というもの。

コマンドはとっても簡単です♫
ターミナルで以下のコマンドを実行します。

curl -X GET "https://qiita.com/api/v2/tags" | jq -C .

するとこのような結果が返ってきたでしょうか?
(最新タグ20件が取得されるので全く同じではありません。)

出力結果(例)

 % Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1420 0 1420 0 0 4947 0 --:--:-- --:--:-- --:--:-- 4947
[
{
"followers_count": 0,
"icon_url": null,
"id": "Printed",
"items_count": 0
},
{
"followers_count": 0,
"icon_url": null,
"id": "ライン出力",
"items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
 "id": "#radiko",
 "items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
"id": "keydriver",
"items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
"id": "teleop-bot",
"items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
"id": "SMTLIB",
"items_count": 0
},
{
"followers_count": 0,
"icon_url": null,
"id": "VCV",
"items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
"id": "紗々",
"items_count": 0
},
{
"followers_count": 0,
"icon_url": null,
"id": "ISM",
"items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
"id": "U-Command",
"items_count": 0
},
{
"followers_count": 0,
"icon_url": null,
 "id": "#scilab",
 "items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
"id": "YUV420_888",
"items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
 "id": "#bayes",
 "items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
 "id": "#optuna",
 "items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
"id": "GoogleChromeOS",
"items_count": 0
},
{
"followers_count": 0,
"icon_url": null,
"id": "あんど",
"items_count": 0
},
{
"followers_count": 0,
"icon_url": null,
"id": "artms007",
"items_count": 0
},
{
"followers_count": 0,
"icon_url": null,
"id": ",V8",
"items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
"id": "instagram_basic_display",
"items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
"id": "DEPLOYMENT_TARGET",
"items_count": 1
}
]

取得したjsonには20組のデータが入っていますね。
無事にAPIが叩けたようです🎉

ストック済記事を一括解除する

一旦Qiita APIが叩けることは確認出来たので、次はいよいよストック済記事の
解除に向けて段取っていきます。

自分がストックしている記事一覧を取得する

ストックを解除しようと思ったら、まずストック済の記事を知っておく必要があります。
Qiita API v2 公式ドキュメントの中から該当のAPIを探しましょう。

はい、これですね😊
Qiita API v2 公式ドキュメント: 指定されたユーザがストックした記事一覧を、ストックした日時の降順で返します。

記載の通りにAPIを叩きます。
※コマンドの中にある自分のQiitaIDは、マイページのURLに記載されてる
https://qiita.com/ここの部分

curl -X GET "https://qiita.com/api/v2/users/自分のQiitaID/stocks" | jq -C .

するとストックしていた全記事のjsonデータが容赦なく押し寄せます。
中見はさほど気にせず、取得できたことだけ確認できればヨシ👈❗️

ストック解除に必要な情報を調べる

Qiita API v2 公式ドキュメントから、ストック解除APIを叩くために必要な情報を調べます。

コチラです☺️
記事をストックから取り除きます。

これをみる限り、必要なのはストック記事のIDだけのようです。
(あと自分のアクセストークン)

しかし困りました。
先ほど実行したcurlコマンドではタイトルや記事内容まで全てを取得してしまっています。
必要なのは記事IDだけ。。。🤔
どうにかIDだけ取得することは出来ないものだろうか。。。

実はここでもjqコマンドが役に立ちます♫

curl -X GET "https://qiita.com/api/v2/users/自分のQiitaID/stocks" | jq '.[].id'

出力結果(例)

"e85931bf8276da43cc97"
"7957e71968aefc6558be"
"d3c36b769bb1a995832a"
"0a0b46106681f41f2f0e"
"73040df85b7bd4e9ecfc"
"b8ba525d31ea7c522856"
"81dbbd3ea6211af71648"
"f7358a227dfe2640cce3"
"f59d61cd63a321418a8a"
"cbe58a72a715b540c60f"
"09d791363364a876996f"
"bd0a5295e540e27936b3"
"bd4ba36a58ad602a9d8b"
"fd14a2a2480cab6fe9d1"
"b5bb9c0366f21bcbea62"
"40e0c7d32fde352607be"
"066421bce820e3c73ce9"
"6f39498c86fb592b2baf"
"8f350f78193d8b222078"
"9920bb6918b4d1fe5d58"

おお!なんとストック記事のIDだけが取得できました🎉
jqコマンド恐るべし!!

しかしここでひとつ気付きませんか?
そう、ストック記事は93件あるはずなのに、返ってきたIDは20件だけ🤔

実はこれは、APIのデフォルト返り値が20件になっているためなのです。
(厳密には1ページあたりの表示件数が20件なので、1ページ目の20件が返り値となっている。)

なので93件全てを表示するには、curlコマンド実行時にパラメータを与えてあげる
必要があります。

パラメータ付与前

curl -X GET "https://qiita.com/api/v2/users/自分のQiitaID/stocks" | jq '.[].id'

パラメータ付与後
per_page=ストック記事数というパラメータを追加してます。自分の場合は93。
1ページあたり何件表示させるか、というパラメータ。デフォルトは20。

curl -X GET "https://qiita.com/api/v2/users/自分のQiitaID/stocks?per_page=ストック記事数" | jq '.[].id'

ここまででようやく全ストック記事のIDを取得することが出来ました。
ふー。

試しにストック記事を1つ削除してみる

まずは試しに上記で取得できた記事IDを使ってストック解除APIを叩いてみます。
記事をストックから取り除きます。

今回ストック解除する記事のIDはコチラ ▶︎ e85931bf8276da43cc97
ストック解除する記事 ▶︎ VSCode に必ず入れておきたい拡張機能

VScodeユーザーなら絶対にストックしておきたい記事の1つですね!
(また後ですぐにストックするのでご勘弁を🙏)

curlコマンドが正しく実行されるか確認するため、間違いなくストックされていることをブラウザから確認します。
ストックマークに✅ 入ってますね😊
スクリーンショット 2021-02-23 14.04.52.png

では、公式ドキュメント通りにcurlコマンドを実行します!

curl -X DELETE "https://qiita.com/api/v2/items/e85931bf8276da43cc97/stock" 

ええーい!!ポチッとな♫

ん??、、、あ、あれ??

{"message":"Unauthorized","type":"unauthorized"}%

権限がない、と怒られちゃいました🤔
それもそのはず、だって誰でも彼でも僕のストック記事を解除できちゃったら困りますからね!
というかこのコマンドだと、誰のストック記事を解除しようとしてるのかすら分かりません。

このときに必要なのがアクセストークンですね!
アクセストークンは第三者に知られてはならない大事な情報なので絶対に公開しないように!!

この記事の序盤で発行したアクセストークン、ここでようやく出番が来ました笑
もしトークンを忘れちゃった!コピーしたのに残ってない!という人は新たにトークンを作り直せばOK👍

僕のアクセストークンはcatcatcatcatcatcatcatcatcatcatcatcatだとします。
その場合、ストック解除APIの正しいcurlコマンドはコチラ!

curl -sSH "Authorization: Bearer catcatcatcatcatcatcatcatcatcatcatcat" -X DELETE "https://qiita.com/api/v2/items/e85931bf8276da43cc97/stock"

ターミナル「しーん・・・」
無事にコマンドが通れば特に返り値の表示などはありません。

では再びブラウザから記事を確認してみましょう!
VSCode に必ず入れておきたい拡張機能

スクリーンショット 2021-02-23 14.20.01.png

おお!見事にストックの✅ が外れています!
無事にストック解除API通信も成功しました🎉

いよいよストック記事の全解除

長くなりましたが、いよいよストック記事の全解除に移ります。
ここまでで出来たことを簡単に整理するとこんな感じ。

  1. curlコマンドが使えるようになった
  2. jqコマンドでデータ整形できるようになった
  3. Qiita APIを扱える様になった
  4. 自分のストック記事一覧を取得できた(IDだけの取得も可)
  5. 自分のストック記事を1つ削除できた

以上のことから、4と5を組み合わせればストック記事の全解除はできそうです😊

問題発生

ここでまさかの事態が発生しました。
これは完全に筆者のcurl知識不足なんですが、取得した複数の記事IDを
どのようにストック解除APIに渡せばいいのか分からず🤔
なのでここは妥協策でRubyに頼ろうと思います。。。

Rubyプログラムによるストック記事解除

今までやってきたことは何だったんだ、、、って感じですが、
curlコマンドに触れたお陰でどんなことをしたいかが明確になりました。

release_stocked_items.rb
require 'net/http'
require 'json'

# 下準備
base_url = URI.parse("https://qiita.com")
https = Net::HTTP.new(base_url.host, base_url.port)
https.use_ssl = true

# ストック記事ID取得
get_stocked_items_api = "/api/v2/users/自分のQiitaID/stocks"
query = "?per_page=ストック記事数"
get_fullpath = base_url.path + get_stocked_items_api + query

get_request = Net::HTTP::Get.new(get_fullpath)
get_response = https.request(get_request)

stocked_items_ids = JSON.parse(get_response.body).map{ |item| item['id'] }
puts "#{stocked_items_ids.size}件のストック記事があります"

# ストック記事解除
release_stocked_items_api = ["https://qiita.com/api/v2/items/", "/stock"]
authorization_token = { "Authorization": "Bearer catcatcatcatcatcatcatcatcatcatcatcat" }
item_count = 0
stocked_items_ids.each do |id|
  release_fullpath = release_stocked_items_api.join(id)
  relaese_request = Net::HTTP::Delete.new(release_fullpath, authorization_token)
  https.request(relaese_request)
  item_count += 1
end

puts "#{item_count}件のストック記事を解除しました"

出力結果

93件のストック記事があります
93件のストック記事を解除しました

はい、無事に全解除できました!
もちろんブラウザから確認してもストック記事は綺麗サッパリ消えています✨

スクリーンショット 2021-02-23 16.04.03.png

ただ、変数名長いしベタ書きなのでコードの可読性が低く、だいぶパフォーマンスも悪いですね。
とはいえ一旦ストック解除という最低限の目的は達成しました🥺

今回は備忘録のような記事になってしまいましたが、ご指摘もドシドシ募集しております🙏

参考にさせて頂いた記事

jq コマンドを使う日常のご紹介