search
LoginSignup
19

More than 3 years have passed since last update.

posted at

updated at

Qiitaの投稿記事からデータセット作った

はじめに

こんにちは。今年ももう終わりですね。
普段は記事を読むだけの僕ですが、初めて記事を書きたいと思います。

今回はQiitaのAPIを利用して、これまでのQiitaの記事について分析してみました。その過程で死ぬほどエラーでつまづいたので、二度とこんな無駄な作業が生まれないよう、ここにそのノウハウを共有します。
収集したデータセットも合わせて公開しますのでご査収ください。

やったこと

  • QiitaAPIをPythonで扱う練習
    • 月ごとの投稿数を調査
  • Qiitaの投稿データに関するデータセットを作成
    • 2011/9-2018/10までの全記事データを収集

Qiita APIの基本的な使い方

Qiita API v2ドキュメント - Qiita:Developer

PythonでAPIを扱うためにrequestモジュールを利用します。例えば投稿の一覧を取得してみます。1

import requests

url = 'https://qiita.com/api/v2/items'
params = {
    'page'     : 1,
    'per_page' : 100
}
headers = {
    'content-type'  : 'application/json',
    'charset'       : 'utf-8'
}

r = requests.get(url, params=params,  headers=headers)
print('Response={0}, Detail={1}'.format(r.status_code,r.headers))
print(r.json())

うまくいけばResponseが200で返ってきます。

Response=200,
Detail={
    'Date':'Thu,
    08 Nov 2018 11:30:05 GMT',
    'Content-Type':'application/json; charset=utf-8',
    'Transfer-Encoding':'chunked',
    'Connection':'keep-alive',
    'Server':'nginx',
    'X-Frame-Options':'SAMEORIGIN',
    'X-XSS-Protection':'1; mode=block',
    'X-Content-Type-Options':'nosniff',
    'Link':'<https://qiita.com/api/v2/items?page=1&per_page=100>; rel="first",
    <https://qiita.com/api/v2/items?page=2&per_page=100>; rel="next",
    <https://qiita.com/api/v2/items?page=3454&per_page=100>; rel="last"',
    'Total-Count':'345301',
    'ETag':'W/"b69bd1299e58ad6e0f3f0a54e937fae4"',
    'Cache-Control':'max-age=0, private, must-revalidate',
    'Rate-Limit':'60',
    'Rate-Remaining':'59',
    'Rate-Reset':'1541680205',
    'Vary':'Origin',
    'X-Runtime':'0.561610',
    'Strict-Transport-Security':'max-age=2592000',
    'X-Request-Id':'03beeecd-a39f-411c-9ad0-77e868584bea'

}

2018/11/8現在、Qiita上には345301件の記事が存在します。

レスポンスの中身はこんな感じです。

{'body': '# すべての開発者が学ぶべきたった一つの言語という記事\n\nなんかバズってるし、私もパクって駄文を一つ書いてみることにしました。\nヤマト運輸を待ってる最中なので暇なんですよねー。\nZOZOのジーンズ早く来ないかなー。\n\n## 職場でメインに使われてる言語\n\n例えば、日本国内の職場ならメインの言葉はたいてい日本語。\nそういう職場では日本語ができないと何もできない。\n仕事にならない、\n\n## 英語\n\n大半のライブラリーやプログラム言語の解説は英語。\nこの言語ができないと調べものもできない、知識の習得もできない。\n超重要な言語。\n\n## コミュニケーション能力\n\n言語能力についで必要な能力。言語が荷車の片輪だとするとこの能力はもう片方の車輪。\n\n上司がなにか言う前に忖度していろいろやる技術・・・・・ではもちろんない。\n\n**相手を尊重し、相手を理解し、その上で自分を理解してもらう能力である。**\n\n様々な書籍があり、さまざまな研究がなされているがこれ、ものすごく難しい。言語能力に加えてこの能力があれば最強である。\n\nプログラム言語だけできる人は多い。人間の言語がきちんと話せる人も多い。しかし、コミュニケーション能力がある技術者は少ない(ように思う、とくに某記事への叩き方を見ると)。\n\nこの能力があれば、\n\nなんの役にも立たない意見、人を馬鹿にしたような意見、があっても\n\n**よってたかって叩かず、ネタにせず、静かに笑っていられる人**\n\nになれます。(多分)\n\n',
  'coediting': False,
  'comments_count': 0,
  'created_at': '2018-11-09T09:50:52+09:00',
  'group': None,
  'id': '75890603ac6e6c3b2328',
  'likes_count': 0,
  'page_views_count': None,
  'private': False,
  'reactions_count': 0,
  'rendered_body': '\n<h1>\n<span id="すべての開発者が学ぶべきたった一つの言語という記事" class="fragment"></span><a href="#%E3%81%99%E3%81%B9%E3%81%A6%E3%81%AE%E9%96%8B%E7%99%BA%E8%80%85%E3%81%8C%E5%AD%A6%E3%81%B6%E3%81%B9%E3%81%8D%E3%81%9F%E3%81%A3%E3%81%9F%E4%B8%80%E3%81%A4%E3%81%AE%E8%A8%80%E8%AA%9E%E3%81%A8%E3%81%84%E3%81%86%E8%A8%98%E4%BA%8B"><i class="fa fa-link"></i></a>すべての開発者が学ぶべきたった一つの言語という記事</h1>\n\n<p>なんかバズってるし、私もパクって駄文を一つ書いてみることにしました。<br>\nヤマト運輸を待ってる最中なので暇なんですよねー。<br>\nZOZOのジーンズ早く来ないかなー。</p>\n\n<h2>\n<span id="職場でメインに使われてる言語" class="fragment"></span><a href="#%E8%81%B7%E5%A0%B4%E3%81%A7%E3%83%A1%E3%82%A4%E3%83%B3%E3%81%AB%E4%BD%BF%E3%82%8F%E3%82%8C%E3%81%A6%E3%82%8B%E8%A8%80%E8%AA%9E"><i class="fa fa-link"></i></a>職場でメインに使われてる言語</h2>\n\n<p>例えば、日本国内の職場ならメインの言葉はたいてい日本語。<br>\nそういう職場では日本語ができないと何もできない。<br>\n仕事にならない、</p>\n\n<h2>\n<span id="英語" class="fragment"></span><a href="#%E8%8B%B1%E8%AA%9E"><i class="fa fa-link"></i></a>英語</h2>\n\n<p>大半のライブラリーやプログラム言語の解説は英語。<br>\nこの言語ができないと調べものもできない、知識の習得もできない。<br>\n超重要な言語。</p>\n\n<h2>\n<span id="コミュニケーション能力" class="fragment"></span><a href="#%E3%82%B3%E3%83%9F%E3%83%A5%E3%83%8B%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E8%83%BD%E5%8A%9B"><i class="fa fa-link"></i></a>コミュニケーション能力</h2>\n\n<p>言語能力についで必要な能力。言語が荷車の片輪だとするとこの能力はもう片方の車輪。</p>\n\n<p>上司がなにか言う前に忖度していろいろやる技術・・・・・ではもちろんない。</p>\n\n<p><strong>相手を尊重し、相手を理解し、その上で自分を理解してもらう能力である。</strong></p>\n\n<p>様々な書籍があり、さまざまな研究がなされているがこれ、ものすごく難しい。言語能力に加えてこの能力があれば最強である。</p>\n\n<p>プログラム言語だけできる人は多い。人間の言語がきちんと話せる人も多い。しかし、コミュニケーション能力がある技術者は少ない(ように思う、とくに某記事への叩き方を見ると)。</p>\n\n<p>この能力があれば、</p>\n\n<p>なんの役にも立たない意見、人を馬鹿にしたような意見、があっても</p>\n\n<p><strong>よってたかって叩かず、ネタにせず、静かに笑っていられる人</strong></p>\n\n<p>になれます。(多分)</p>\n',
  'tags': [{'name': 'ポエム', 'versions': []},
   {'name': 'ネタ', 'versions': []},
   {'name': '駄文', 'versions': []},
   {'name': 'ジョーク', 'versions': []}],
  'title': 'すべての開発者が学ぶべき言語と技術',
  'updated_at': '2018-11-09T09:50:52+09:00',
  'url': 'https://qiita.com/TakaakiFuruse/items/75890603ac6e6c3b2328',
  'user': {'description': '',
   'facebook_id': '',
   'followees_count': 83,
   'followers_count': 13,
   'github_login_name': 'TakaakiFuruse',
   'id': 'TakaakiFuruse',
   'items_count': 36,
   'linkedin_id': 'tfuruse',
   'location': '',
   'name': '',
   'organization': '',
   'permanent_id': 52613,
   'profile_image_url': 'https://qiita-image-store.s3.amazonaws.com/0/52613/profile-images/1473692788',
   'twitter_screen_name': None,
   'website_url': ''}},

月別投稿数を取得してみた

APIの練習として2011-20182の投稿数を月別にプロットしてみます。

import pandas as pd

dic={}
for year in range(2011,2019):
  for month in range(1,13):
    key = "{}-{}".format(year,month)
    total_articles,total_pages = get_monthly_data(year,month)
    dic[key]=[total_articles, total_pages]
    print(year, month, total_articles, total_pages)
    time.sleep(60)

x = list(dic.keys())
y = [cnt[0] for cnt in list(dic.values())]
df = pd.DataFrame({'time':x,'count':y},index=x)
df = df.drop(index=['2018-11','2018-12'])
df['count'].plot(figsize=(16,9),
                 title='The Number of Monthly Posts in Qiita',
                 xticks=range(0,len(x))[::3],
                 rot=45)

qiita_analysis.png

2013年から周期的にスパイクが発生しています。毎年12月頃…そう、みなさんご存知AdventCalendarの影響です。2018年は平均8000postで推移しているので今年のAdventCalendarも前年を超えてきそうですね。

Qiitaのデータセット

「いいねがたくさん欲しいんだけど、どんなタイトルにすればいいのかな?」
「閲覧数、いいね数、コメント数の相関はあるのだろうか?」

色々調べたくなったのでデータセットを作ってみました。3

ぱっと見はこんな感じです。

qiita_data.png

ラベル 内容
created_at データが作成された日時
updated_at データが最後に更新された日時
id 投稿の一意なID
title 投稿のタイトル
user ユーザ名4
likes_count この投稿への「いいね!」の数
comments_count この投稿へのコメントの数
page_views_count 閲覧数
url 投稿のURL
tags 投稿に付いたタグ一覧

page_views_countは取得したのですが全部Noneでした。また、記事のタイトルに改行コード\rが混入しているため、少しハマりました。

11/30までのデータをQiita_dataset_1113 | Kaggleに置いておきます。練習用にどーぞ。

Qiita APIをうまく使うコツ

QiitaのAPIには色々と制約があります。ポイントは下記2点です。

APIの利用制限

認証している状態ではユーザごとに1時間に1000回まで、認証していない状態ではIPアドレスごとに1時間に60回までリクエストを受け付けます。

普通にループで繰り返し処理をするとすぐにエラーが返ってくるはずです。time.sleep(n)などで待ち時間を挿入することで解決しましょう。

参考に待ち時間の目安をまとめておきます。

制限対象 利用制限 待ち時間
認証あり5 ユーザ 1000回/時間 3.6秒/リクエスト
認証なし ipアドレス 60回/時間 60秒/リクエスト
pageの上限が100

pageの初期値は1、pageの最大値は100に設定されています。

import math
# 全記事数
total_articles = int(r.headers['Total-Count'])
total_pages = math.ceil(total_articles/float(params['per_page']))

print(total_articles, total_pages)

上記のコードを実行してみると、Qiitaの全記事を取得するためには3000page以上リクエストを投げる必要があります。そんなの絶対無理やん。と思っていたところ、とあるブログ6で紹介されていた「queryを細かく投げてpage数を抑える」という技で何とかなりました。感謝。

まとめ

今回はQiitaのAPIから過去の投稿記事のデータセットを作成してみました。ちなみにLSTMで2018/12の投稿数を予測してみたところ〇〇件でした。答えはお正月にでも。


  1. https://qiita.com/api/v2/docs#get-apiv2items 

  2. Qiitaのサービスインは2011/9/16 

  3. いいね数やコメント数はデータ取得当時のものなので注意してください。 

  4. twitterのフォロー数や紹介文など 

  5. ユーザ認証用のアクセストークンを用いた場合 

  6. Google Colaboratory で Qiita:Team の全投稿を取得して、可視化したり、人気ランキングを作ってみる - Kaizen Platform 開発者ブログ 

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
What you can do with signing up
19