どこにも遊びに行けないなら”はてブ”のデータ分析をして遊べばいいじゃない


こんにちは、らくからちゃです
2年連続ステイホームのゴールデンウィークになりそうです。
もはやゴールデンウィークって普段何してたのか忘れかけてきたので、過去の履歴を漁ってみたら、一昨年は伊豆半島の東側をぐるぐる回りながら下田までいってたみたいです。
こんなどこにも行けない日には、家でデータ分析をするに限りますね!!(鼻息)
統計局が、e-statを使って遊ぶ方法も教えてくれるそうなので、ご興味がある方は是非!
統計として公開されているデータを眺めてみるのも面白いっちゃ面白いのですが、データ分析の醍醐味は統計処理される前の生々しいデータを弄くり倒すところにあると思うんすよ。
ただKaggleでランカー目指すぞい!!と殴り込みに行くにはハードルが高いなあとお思いでしたら、目の前になかなかいじり甲斐のありそうなデータがあるじゃあないですか。コイツです。
f:id:lacucaracha:20210503120304p:plain

はてブデータ収集ツールの使い方

定期的に「はてなスターランキング」と称した記事を書いておりますが、このランキングは、はてなの公開しているAPIを叩いて出てきたデータを使用しています。
これらのデータは、数年前にたまたまそのとき勉強していたJavaで書いた自家製ツールで収集していました。ただメンテ性も良くないし、いろいろと直しておきたい箇所もあったので昨年末にPythonで書き直してみました。
かなり扱い易くなったはずですし、個人的な備忘も兼ねて、データ収集の方法について整理したいと思います。
ツールの開発・実行環境は、
  1. iMac 27-inch,Late 2013(そろそろ買い替えたい)
  2. mac OS Catalina 10.15.7
  3. Python 3.7.1 (Catalinaの標準)
  4. SQLiteStudio 3.3.3
になります。特に有償のツールやサービスは使いません。同じような環境であれば、WindowsでもLinuxでも平気なはず。あとはネットと電気とやる気があれば大丈夫(たぶん)
データの収集・集計には、SQLを使用しますが、特に難しく考えなくて平気です。(むしろちょうどよいSQLの練習になるかも)
1〜3まではMacなら勝手についてきているはずです。Windows・Linuxの似たようなバージョンを突っ込んでください。4のSQLiteStudioは下記から別途インストールしておきましょう。
インストールできたら、データを保存するデータベースファイルを作りましょう。
f:id:lacucaracha:20210504102907p:plain 分かりやすくドキュメント直下にファイルを作ってみます。(別の場所でもOKですが、デスクトップみたいに権限が付与できない場所だとアレかも)
f:id:lacucaracha:20210503133421p:plain 作ったファイルの中に
  1. Page(対象となるページの情報)
  2. Bookmark(個別のブックマーク情報)
  3. Star(ブックマークに対するスター)
の3種類のデータを格納するテーブル(表)を作成します。以下の呪文を順番に詠唱すればOKです。(②→③をそれぞれ3回実行)

Page

CREATE TABLE Page (
    EID       VARCHAR (4000) PRIMARY KEY,
    DATE      DATE,
    CATEGORY  VARCHAR (4000),
    ENTRYRANK NUMERIC (5),
    COUNT     NUMERIC (5),
    TITLE     VARCHAR (4000),
    URL       VARCHAR (4000)
);

 Bookmark

CREATE TABLE Bookmark (
    EID          VARCHAR (4000),
    BOOKMARKUSER VARCHAR (4000),
    URL          VARCHAR (4000),
    STARCOUNT    NUMERIC (5),
    TIMESTAMP    DATETIME,
    COMMENT      TEXT,
    TAG          TEXT,
    PRIMARY KEY (
        EID,
        BOOKMARKUSER
    )
);

Star

CREATE TABLE Star (
    STARUSER     VARCHAR (4000),
    COLOR        VARCHAR (4000),
    BOOKMARKUSER VARCHAR (4000),
    EID          VARCHAR (4000),
    QUOTE        TEXT
);
作ったテーブル構造の説明もしておきたいところですが、データが入った段階じゃないとイメージがつきづらいでしょう。細かい解説は後回しにして、まずデータを取得しましょう。
さてここで、今回Python化した収集スクリプトの登場です。
 
import sys
import requests
import urllib.parse
import json
import sqlite3
import time
from bs4 import BeautifulSoup
from datetime import datetime as dt
pdate = sys.argv[1]
db = sys.argv[2]
url = “https://b.hatena.ne.jp/hotentry/all/” + pdate
r = requests.get(url)
soup = BeautifulSoup(r.text, ‘html.parser’)
conn = sqlite3.connect(db)
c = conn.cursor()
elems = soup.find_all(“div” , class_ = “entrylist-contents”)
print(“★★★★★” + pdate + “★★★★★”)
for i,e in enumerate(elems):
purl = e.find(“a”).get(“href”)
burl = “https://b.hatena.ne.jp/entry/jsonlite/?url=” + urllib.parse.quote(purl)
Page = requests.get(burl).json()
count = Page[“count”]
title = Page[“title”]
eid = Page[“eid”]
category = e.find(“li” , class_ = “entrylist-contents-category”).text.strip()
#EUCに変換できない文字が含まれる場合は標準出力出来ないため、エラーの場合は適当にスルー
try:
print(str(i+1) + “:” + str(count) + “:” + str(title))
except Exception as e:
print(str(i+1) + “:” + str(count))
check = c.execute(‘select count(*) “count” from Page where EID = ?’,(eid,)).fetchone()[0]
if check == 1:
continue
c.execute(“delete from Bookmark where EID = ?”,(eid,))
c.execute(“delete from Star where EID = ?”,(eid,))
c.execute(“insert into Page(EID,Date,category,Entryrank,Count,Title,URL) values(?,?,?,?,?,?,?)”,(eid,dt.strptime(pdate,‘%Y%m%d’),category,i+1,count,title,purl))
for bookmark in Page[“bookmarks”]:
buser = bookmark[“user”]
btags = bookmark[“tags”]
bcomment = bookmark[“comment”]
btimestamp = dt.strptime(bookmark[“timestamp”], ‘%Y/%m/%d %H:%M’)
surl = “https://s.hatena.com/entry.json?uri=” + urllib.parse.quote(“https://b.hatena.ne.jp/” + buser + “/” + btimestamp.strftime(‘%Y%m%d’) + “#bookmark-“ + eid)
StarData = []
err_count = 0
star_count = 0
if(len(bcomment)!=0):
while err_count < 10 :
try:
Bookmark = requests.get(surl).json()
stars = Bookmark[“entries”]
if len(stars)!=0:
for star in stars[0][“stars”]:
StarData.append((star[“name”],None,buser,eid,star[“quote”]))
star_count += 1
if(“colored_stars” in stars[0]):
for color_stars in stars[0][“colored_stars”]:
for color_star in color_stars[“stars”]:
star_count += 1
StarData.append((color_star[“name”],color_stars[“color”],buser,eid,color_star[“quote”]))
break
except Exception as e:
print(surl)
print(“エラー発生:” + str(err_count))
err_count += 1
time.sleep(err_count)
c.executemany(‘Insert into Star(STARUSER,COLOR,BOOKMARKUSER,EID,QUOTE) values(?,?,?,?,?)’,StarData)
c.execute(‘Insert into Bookmark(EID,BOOKMARKUSER,URL,STARCOUNT,TIMESTAMP,COMMENT) values(?,?,?,?,?,?)’,(eid,buser,purl,star_count,btimestamp,bcomment))
conn.commit()
conn.close()
view rawHatena.py hosted with ❤ by GitHub
 
Hatena
非エンジニアが「動けば良い」の崇高な精神のもと書いたスクリプトなので、ツッコミどころは満載だと思いますが、気にせず適当な名前で保存してください。そうですね “hatena.py” にでもしましょうか。
このスクリプトは、標準のライブラリに加えてBeautifulSoupを使います。ターミナルを起動し、以下のコマンドでインストールしましょう
pip install beatifulsoup4
これで準備完了です!!
このスクリプトは、
  • 第一引数 取得対象の年月日(YYYYMMDD)
  • 第二引数 格納先のデータベース
で実行できます。ターミナルで下記のように命令を実行ください。
python /Users/xxx/Documents/hatena.py 20210101 /Users/xxx/Documents/Hatena.db
こんな感じで、データの取得が始まれば成功です。
f:id:lacucaracha:20210504104148p:plain

はてブデータ収集ツールの仕組み

このツールがどんな手順でデータを取得しているのかは、中のデータの特性を抑えるためにも必要だと思うので、簡単に説明いたします。はてなブックマークには、デイリーのランキングページがあります。
例えば、2021年1月1日だとこんな感じ。末尾の部分が指定した引数と同じになります。
https://b.hatena.ne.jp/hotentry/all/20210101
ページを開いてデベロッパーツールで眺めてみると、”entrylist-contents”の中に、それぞれの要素が格納されていることが見て取れます。
f:id:lacucaracha:20210504124829p:plain この中から、記事のURLのリンク部分を取得して、はてな社が公開しているAPIに投げつけて、それぞれのページに対するブックマーク情報を取得します。全ブックマークデータを対象とすると処理に時間がかかりすぎるため、コメント付きブックマークに対してスターの付与状況を取得します。
APIの詳細は下記をご参照ください。
こうしてウェブページからデータをぶっこ抜いてくる手法を「スクレイピング」と言います。詳細は、さいとーさんの本が大変評判が良いので気になる人はご一読ください。
話を戻すと、このスクリプトでは
  1. デイリーランキングに乗っているページが対象
  2. 取得したページから、コメント付きのブックマークに対してスターを取得
になります。よって、
  • デイリーランキングに乗り切らなかったページへのコメント
  • タグだけ、コメントを後で消したコメントへのスター
は集計対象から外れますので、その前提で扱ってください。

はてブのデータを見てみよう

SQLiteStudioで、まずは単純に取得したページのデータを見てみましょう。以下の手順で操作してください。
f:id:lacucaracha:20210504130938p:plain 下記のようなデータが表示されました。
  1. EID
  2. DATE
  3. CATEGORY
  4. ENTRYRANK
  5. COUNT
  6. TITLE
  7. URL
列名でおおよそ推察がつくとおもいますが、この中で分かりづらいのは「EID」でしょうね。これは(おそらく)EntryIDの意味で、各ページに対してはてな側が付与している識別用IDです。
なおDATEはランキングに表示された日付(≠記事が投稿された日付)で、RANKは当日ランキング上の表示順です(≠ブクマ数順位)。
次に、特定ページに対するコメントをスター数の多い順番に並び替えて見ましょう。
f:id:lacucaracha:20210504132243p:plain Bookmarkテーブルを開いて、フィルター窓に抽出したいEIDを指定して、Starcountを降順ソートすれば結果が見れます。これくらいの内容であれば特にSQL文を書く必要もありません。
もう少し複雑なデータを取得する場合は、SQL文を書く必要があります。例えば下記は、「lacucarachaのブックマークに対してスターをつけた人を、ページ毎の重複ブックマークを除いて集計し、降順で表示する」といった命令になります。
select
 staruser,count(distinct EID)
from
 Star
where
 bookmarkuser = 'lacucaracha'
group by staruser
order by count(distinct EID) desc
実際にこのSQLを実行するとこんな感じに表示されました。ほお・・・。
f:id:lacucaracha:20210504133641p:plain SQL文が書ければ、かなり複雑なデータを抜き出すことも出来ますし、エクセルに結果を貼り付けることも出来ます。あとは解析してブログに書くもよし、増田に怪文書を作るもよし、機械学習の教師データとして使ってみても面白いでしょう。
取得したデータを個人の趣味の範囲で利用するぶんには問題ないと思いますが、念の為API規約についてもご一読ください。
この程度でどうにかなる、はてなのサーバーではないと思いますが、「当社または他者のサーバーに過剰に負荷をかける機能」にならない範囲でお楽しみくださいまし。
ではでは、今日はこのへんで