読者です 読者をやめる 読者になる 読者になる

プロクラシスト

みんなのProcrastination(先延ばし、回り道)を少しでも有意義に。

MENU

はてブ情報をpythonで可視化できるようにしたよ!

f:id:imslotter:20170409153655p:plain

こんにちは!ほけきよです!

はてなブログをしている皆さんなら、当然アレ、気になりますよね??

そうはてブです。

自分の記事が拡散された時の満たされる感じは、クセになりますよね。 今回は、自分の記事のはてブがどのように増えて行くかを、APIを使って取得できるようにしてみました。

また、はてブが伸びるかどうかを左右する情報も取得できるようにしました!! 基本コピペでOKになっているはずなので、pythonを使っている方は、一回試してみると楽しいですよ!

必要なモジュール

まず、モジュールをインポートしていきましょう。デフォルト以外で入れなければ行けないのは以下の二つ

pipで入れちゃいましょう。 windowspythonを使いたい方は、関連記事へGO. Anacondaには上の二つが含まれているのでそれだけでOKですよ。

コード

#coding:utf-8
%matplotlib inline #jupyterを使う方はこれを入れておく
import urllib2 
from bs4 import BeautifulSoup
import json
import datetime
import matplotlib.pyplot as plt
url = "http://www.procrasist.com/entry/2017/01/07/154441"

はてブの情報を取得

はてなブックマークには、簡単に情報をGETできるように、APIが設計されています。 公式HPか、Syncerさんのページがとてもわかりやすいので参照するにはオススメです

はてブの情報をとってくるためには、自分が調べたいurlの前にhttp://b.hatena.ne.jp/entry/json/をくっつける必要があります。ここにアクセスすることで、情報をGETできます。

ここで一つ注意です。ブックマークを非公開にしている人の情報の詳細ブックマーク情報(時間・コメント等)は取得することができません。ですので、結果的には本来のブックマーク総数より小さく表示されてしまうこともあります。

コード

def get_timestamps(url):
    """
    はてブのタイムスタンプをget
    """ 
    data = urllib2.urlopen("http://b.hatena.ne.jp/entry/json/{}".format(url)).read()
    info = json.loads(data.strip('(').rstrip(')'))
    timestamps = list()
    if info != None: # 公開ブックマークが存在する時に、それらの情報を抽出
        bookmarks=info["bookmarks"]
        title = info["title"]
        for bookmark in bookmarks:
            timestamp = datetime.datetime.strptime(bookmark["timestamp"],'%Y/%m/%d %H:%M:%S')
            timestamps.append(timestamp)
        timestamps = list(reversed(timestamps)) # ブックマークされた時間を保存しておく
    return info, timestamps

はてブの伸びを左右する情報たち

以前もブログで少し話したことがありますが、はてブの伸びは、 エントリーに掲載されるかどうかによって左右されます。

私が伸びるかなーどうかなー。と気になるときにチェックするのは以下の5箇所です

  • カテゴリ新着エントリ
  • カテゴリ人気エントリ
  • 総合新着エントリ
  • 総合人気エントリ
  • はてなブログトップ

新着エントリ < 人気エントリ < はてなブログトップ

の順で掲載期間が長くなっている気がします。これら5つについて、情報をGETできるようにしました!

他に集めておいたほうがいいデータあれば教えてください!

コード

# ページカテゴリ取得
def get_category(url):
    try:
        html = urllib2.urlopen("http://b.hatena.ne.jp/entry/{}".format(url))
        soup = BeautifulSoup(html,"lxml")
        return soup.find("html").get("data-category-name")
    except urllib2.HTTPError as e:
        print(e.reason)
    except urllib2.URLError as e:
        print(e.reason)

#はてブのエントリリストの順位チェック
def rank_checker(url,hatebu_url):
    try:
        html = urllib2.urlopen(hatebu_url)
    except urllib2.HTTPError as e:
        print(e.reason)
    except urllib2.URLError as e:
        print(e.reason)
    soup = BeautifulSoup(html,"lxml")
    a = soup.find("a",href=url)
    if a == None:
        rank = None
    else:
        rank = a.get("data-entryrank")
    return rank

# はてなブログのトップページに乗っているか
def is_hatenatop(url):
    try:
        html = urllib2.urlopen("http://hatenablog.com/")
    except urllib2.HTTPError as e:
        print(e.reason)
    except urllib2.URLError as e:
        print(e.reason)
    soup = BeautifulSoup(html,"lxml")
    a = soup.find("a",href=url)
    if a is None:
        return False
    return url == a.get("href")

def getdata(url):
    category = get_category(url)
    # カテゴリ新着エントリ
    category_entrylist = "http://b.hatena.ne.jp/entrylist/"+category 
    new_cate = rank_checker(url,category_entrylist) 
    # カテゴリホットエントリ
    category_hotentry = "http://b.hatena.ne.jp/hotentry/"+category 
    hot_cate = rank_checker(url,category_hotentry)
    # 総合新着エントリ
    entrylist = "http://b.hatena.ne.jp/entrylist"
    new_all  = rank_checker(url,entrylist)
    # 総合ホットエントリ
    hotentry = "http://b.hatena.ne.jp/hotentry/"      
    hot_all  = rank_checker(url,hotentry)
    # はてなトップ
    hatena_top = is_hatenatop(url)
    # データまとめ
    string  = "hatebu category entrylist rank  : {}\nhatebu category hotentry rank  : {}\nhatebu overall entrylist rank   : {}\nhatebu overall hotentry rank   : {}\nlisted up to hatenablog toppage : {}"\
    .format(rank_checker(url,category_entrylist),rank_checker(url,category_hotentry),
            rank_checker(url,entrylist),rank_checker(url,hotentry),is_hatenatop(url))
    print(string)
    return string

プロットをする

先ほどのタイムスタンプと、GETしたエントリ情報をグラフにしていきます。 visualize関数を作り、タイムスタンプで可視化。情報はannotate関数で入力します。

記事が初めてブクマされた時から2日間のプロットとしています。

※注 :グラフの範囲やannotateは、グラフの形によって変えたほうが見栄えが良くなることもあるので、dayrangeで範囲を、plt.annotate内のxytext=(timestamp[-1],5)`の座標を変えることで、調整してください。

コード

# プロット
def visualize(info, timestamp, data, label="", dayrange=2,annotate=True):
    plt.xkcd()
    count = len(timestamp)
    number = range(count)
    submit = timestamp[0]
    plt.plot(timestamp,number,"-",lw=3,label=label)
    plt.xlim(submit,submit+datetime.timedelta(days=dayrange))
    if annotate:
        plt.annotate(data,xy=(timestamp[-1], number[-1]), arrowprops=dict(arrowstyle='->'), xytext=(timestamp[-1],5))

実行してみる

以上が具体的な内容です。それでは、実際にはてブのリストからいくつか可視化したものをみてみましょう

  • かキタローさんのバズっている記事

www.kakitaroo.com

Udemy、いいですよね。プログラミングとかの勉強のために使ったりしています!この記事、今までのはてブの推移と、現在(2017/4/9 15:00)のステータスを見てみましょう。

コード

url = "http://www.kakitaroo.com/entry/Udemy"
plt.figure(figsize=(10,7))
entry_info = getdata(url)
hatebu_info, timestamps = get_timestamps(url)
visualize(hatebu_info, timestamps, entry_info, label="Udemy")
plt.legend()
plt.show()

結果

f:id:imslotter:20170409153056p:plain

まだまだ伸びそうですね!

比べることもできる

時系列データを見ることができるので、他のデータと重ねて比べることもできます。*1 先ほどの記事と、もう一つバズっている↓の記事を比べてみましょう

www.sekkachi.com

plt.figure(figsize=(10,7))
url2 = "http://www.kakitaroo.com/entry/Udemy"
url1 = "http://www.sekkachi.com/entry/Heikin_HyoujunHensa_Flow"
entry_info = getdata(url1)
hatebu_info, timestamps = get_timestamps(url1)
visualize(hatebu_info, timestamps, entry_info,label="Udemy", annotate=False)
entry_info = getdata(url2)
hatebu_info, timestamps = get_timestamps(url2)
visualize(hatebu_info, timestamps, entry_info,label="Ave, Std", annotate=False)
plt.legend()
plt.show()

結果

f:id:imslotter:20170409153109p:plain

後発記事が伸びてきていることがわかると思います! こうやって競争させるのも面白いですよね!

まとめと今後やりたいこと

今後やりたいのは、順位を使って、はてブ数を予測できないかな〜ってこと。 データを集める土台はできたので、あとは学習器に各情報と数分後のはてブ数のデータを投げてやって、伸びを予測してみたいなぁ。と思いました。

色々データを集められると、それを使って色々と考えることができるので、楽しいですね!

ブックマーク研究家、始めます。ではではっ!

*1:適当に重ねているだけなので、もうちょっと出力を整えたほうがいいかもしれません。