接着剤の精進日記

競プロでの精進や研究に関係したことを書いていきます。

Pythonでニコニコ動画をスクレイピングしてまとめてみた。

はじめに

完全に趣味用だが、ニコニコ動画スクレイピングしてみた。
内容としては、昨日一日で投稿された動画の一覧を取得し、
タイトル(動画への直リン付き)とサムネイルを表示するというものだ。
以下の画像のようなサイトとなっている。

f:id:tkm-kyudo:20171122000803p:plainf:id:tkm-kyudo:20171122000806p:plain
琴葉姉妹動画なのは趣味です。
せっかく作ったので、簡単に説明してみる。
コードの説明とかは需要あったら書くかも。

簡単に構造の説明

まず、ディレクトリの関係としては、

--index.html
 |-akane_aoi-2017-11-21.html...
 |                 
 |-honobono-2017.11.21.html...
 |           
 ...    
 |
  -img

のようになっている。
index.htmlの存在する階層にそれぞれの検索ワードのディレクトリがある。
また、そのティレクトリに日付で名前のつけたhtmlファイルが存在している。
imgディレクトリには動画のサムネイル画像が入っている。

index.html

index.htmlでは検索ワードごとの最新ページへとリンクしている。
また、スクレイピングのコードを実行するごとに最新版へリンクを更新するようにしている。

各htmlファイル

各htmlファイルは各検索ワードごとに最新の検索結果を取得して表示している。
タイトルは、その動画の直リンク(http://www.nicovideo.jp/watch/sm*)、
サムネイルは、imgディレクトリに保存したものを参照して使用している。

API用パラメータの指定、検索ワードなどの用意

q = ""
targets = "targets=tags"
fields = "fields=contentId,title,description,startTime"
filters = "filters[startTime][gte]=" + str(yesterday) + \
"T00:00:00%2B09:00&filters[startTime][lt]=" + str(today) + \
"T00:00:00%2B09:00
&_sort=-viewCounter&_offset=0&_limit=100&_context=apiguide"

詳しい説明は先ほどのAPIガイド(http://site.nicovideo.jp/search-api-docs/snapshot.html)を
参照していただくとして、簡単な説明をする。
qは検索ワードで、パーセントエンコードしたもので指定する。
qについては次に説明する。
今回はタグ検索したものを取得したいのでtarget="targets=tagsとしている。
filtersのところは昨日から今日までに投稿された動画を、
再生数が高い順に100件取得するという指定をしている。
today,yesterdayにはdatetime型の値が入っている。

検索ワードのエンコード

qで指定する検索ワードのエンコードについては、以下のサイトを利用するか
URLエンコード・デコード|日本語URLをサクッと変換 | すぐに使える便利なWEBツール | Tech-Unlimited
もしくは、

import urllib.parse as parse
search_word = "琴葉茜・葵実況プレイ"
parse.quote(search_word)

のようにurllibparse.quoteを利用することで
パーセントエンコード(urlエンコード)できる。

APIの利用

上記のような手順で出来たurlが以下である(少々長いので便宜上改行している)。

http://api.search.nicovideo.jp/api/v2/snapshot/video/contents/search?
q=%E7%90%B4%E8%91%89%E8%8C%9C%E3%83%BB%E8%91%B5%E5%AE%9F%E6%B3%81%E3%83%97%E3%83%AC%E3%82%A4
&targets=tags&fields=contentId,title,description,startTime
&filters[startTime][gte]=2017-11-20T00:00:00%2B09:00&filters[startTime][lt]=2017-11-21T00:00:00%2B09:00
&_sort=-viewCounter&_offset=0&_limit=100&_context=apiguide

ここまでで、API用のURLにパラメータを指定してきた。
上述のURLにアクセスすることで検索結果のデータがjson形式で渡される。
そのjson形式のデータを上手く活用することで今回のhtmlが作成できた。

終わりに

今回は、思っていた以上に文字数がかかってしまったので、
作成したサイトの説明だけに留めた。
json形式のデータの扱いについては、次あたりに書く予定。
これでアカネチャンアオイチャンカワイイヤッターができる。

ゼロからはじめるデータサイエンス 読了

データサイエンスについて学びたかったので
ゼロからはじめるデータサイエンスを勉強し、2週間ほどで読了。
ざっくりとした感想と書評を書いていく。


どんな感じの本なのか

まず、目次を見ると、

1章 イントロダクション
2章 Python速習コース
3章 データの可視化
4章 線形代数
5章 統計
6章 確率
7章 仮説と推定
8章 勾配下降法
9章 データの取得
10章 データの操作
11章 機械学習
12章 k近傍法
13章 ナイーブベイズ
14章 単純な線形回帰
15章 重回帰分析
16章 ロジスティック回帰
17章 決定木
18章 ニューラルネットワーク
19章 クラスタリング
20章 自然言語処理
21章 ネットワーク分析
22章 リコメンドシステム
23章 データベースとSQL
24章 MapReduce

というような感じとなっている
他にも25章と補足の章はあるが、おまけ的な感じなので割愛させて頂く。

目次を見ても分かる通り、かなり扱う範囲は広い。
データサイエンティストとして学ぶ必要のあるものを網羅しているためだろうか。
そのため、データサイエンティストを目指す人が何を学ぶといいかはわかる。
また、題名の通り、ゼロからはじめることを主としており、
標準搭載のライブラリやNumpyなどのライブラリなどは使用するが、それ以外は基本自分で作っていくことがメインとなっている。
12章以降の話になるが、pandasやscikit-learnなどのライブラリを使わずに、そのアルゴリズムを実装して勉強していく。

良い点

・上記でも述べたが、基本ライブラリを用いず、自分でゼロから作っていくので、
データサイエンティストとして必要な知識を理解しながら勉強していくことができる。
・データサイエンスに必要な知識が数多く網羅されているのでこれ一冊で多くを学ぶことができる。
・これからデータサイエンティストとして勉強していくにあたり、参考図書や
何を勉強すれば良いか紹介してくれているので、次に何をすればよいかがわかりやすい。

悪い点

・一番重要な点であるが、本書のコードはPython2系で書かれている。
githubに3系のコードを載せてくれているため、3系の人でも問題なく学べる。
しかし、本を見ながら、コードのところはgithubのコードを見て、打ち込むというのは非常に煩わしかった。
・本書では、数多くの知識を提供してくれているが、その反面、一つ一つに対しての説明が不十分になりがちで、
数学的知識や、Pythonに対しての知識が浅いと、理解が追いつかなくなっていく。
数学的知識などに関しては、ほとんど説明がないといってもいいので、適宜勉強していく必要がある。
特に統計は勉強しなければいけないな、と強く思わされた。

総評

以上のように良い点も悪い点もある本であるといえる。
ただし、データサイエンスについての網羅的な知識を得られ、
それをゼロから作っていくので、中身をよく知らずにライブラリを使っている人などは理解が深まるのではないか。
また、データサイエンスに興味を持っている人が、コードを流しつつざっくりと読む分には良いと思う。
データサイエンスに必要な知識や、何を学べばいいかは本書に示されているので、詳しい説明を求めないのであれば、本書はおすすめできる。
個人的には、読んで良かったと思え、データサイエンスについてもっと詳しく勉強していこうと思えた。

Python リスト内包表記

リスト内包表記について

リスト内包表記はPythonらしい書き方である。
言葉で表現すると、
ひとつ以上のイテレータからPythonデータ構造をコンパクトに作れる形式*1である。
内包表記を使えば、ループなどを簡潔に書けるし、この書き方のほうが高速。
詳しく知りたい人は以下の参考ページへ。
dsas.blog.klab.org

具体例

言葉だけで説明されてもよくわからないと思うので実際に見てみよう。

a = [i for i in range(5)]
print(a)
>[0, 1, 2, 3, 4]

といった感じになる。
これを内包表記を使わずに書くと、

a = []
for i in range(5):
    a.append(i)
print(a)
>[0, 1, 2, 3, 4]

と結果は同様のものとなる。
変数iにfor文が後置修飾のような形で、0,1,...,4と生成したものを順にiに渡していっている。
なので、for文を先に1つ処理し、その結果を前の変数に渡している感じと言えばだろうか。
あとは、上記の処理を終わるまで続ける。処理が終わると例のようなリストが完成する。

内包表記での条件演算子

例えば、1~10までの数の中で偶数のリストを作りたいとする。
それを普通に書くと以下のようになる。

a = []
for i in range(1,11):
     if i % 2 == 0:
             a.append(i)
print(a)
>[2, 4, 6, 8, 10]

内包表記を使うと以下のようになり、かなりスッキリと書くことができ、Pythonらしいコードとなる。

a = [i for i in range(1,11) if i % 2 == 0]
print(a)
>[2, 4, 6, 8, 10]

先程の例のようにi = 1,...,10までを一つずつ処理していく、
そしてi = 1のとき、if 1 % 2 == 0 はFalseなのでリストには入らない
i = 2のとき、if 2 % 2 == 0はTrueなのでリストに入れる
以下ループの終わりまで繰り返すといったような処理が行われる。
for文でiに値を入れ、その後if文にて判定を行うというような処理が行われる。

以下のように書くことで1~5までの二乗のリストを作るといったようなこともできる。

a = [i ** 2 for i in range(1, 6)]
print(a)
>[1, 4, 9, 16, 25]

内包表記での二重ループ

これまでは、内包表記でのfor文ループの書き方を記してきたが、
内包表記での二重ループももちろん書ける。具体的には以下のようなものとなる。

a = [(i, j)
       for i in range(5)
       for j in range(5)]
print(a)
>[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4),
  (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), 
  (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), 
  (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), 
  (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)]

通常の二重ループと同様にまずi = 0が生成され、
その後、内側のループに入り、j = 0,...,4と順に処理される。
内側のループが終わると、外側に戻り、i = 1となりループが終わるまで同様に繰り返す。


また、内包表記の二重ループを使って、
リストの中のリストを作ることも可能だ。

a = [[(i, j)
       for i in range(5)]
       for j in range(5)]
print(a)
>
[[(0, 0), (1, 0), (2, 0), (3, 0), (4, 0)], 
[(0, 1), (1, 1), (2, 1), (3, 1), (4, 1)], 
[(0, 2), (1, 2), (2, 2), (3, 2), (4, 2)], 
[(0, 3), (1, 3), (2, 3), (3, 3), (4, 3)],
[(0, 4), (1, 4), (2, 4), (3, 4), (4, 4)]]

ただし、この書き方だと、for i in range(5)が内側のループで
for j in range(5)が外側のループとなってしまうみたいだ。
これは、[A for j in range(5)]と解釈され、先に j = 0となる。
その結果をAに渡し、Aである[(i, j) for i in range(5)]が処理されるため
最初のループが、[(0, 0), (1, 0),...,(4,0)]となるようだ。

この多重ループによる処理は以下のサイトを参考にさせていただきました。
【Python】リスト内包表記 x 多重ループ - コンパイラかく語りき

終わりに

Pythonの内包表記は最初は理解しにくいし、とっつきにくい。
しかし、上記で見てきたように単なるfor文の書き方を変えたに過ぎず、
慣れるとコードも短くなり、読みやすくなる。
Pythonの本を読んでいると絶対出てくる表現でもあるので、
理解さえしておけば、後は書いていくうちに慣れてくるはず。

*1:入門Python3 P104

Pythonの文字列操作 join

Pythonの文字列操作、joinについて。
勉強中、コードの中に

return " ".join(result)

というコードを発見。

文字列操作はよく忘れるので、何だったかなと調べると
文字列リストを文字列に変換するものらしい。
なるほど、リストのままだと出力が面倒くさいし、これは便利かな。
使い方は以下の通り

str = ‘区切り文字’.join(list)

実際に使ってみる。

list = ['I', 'like', 'Python']
print(" ".join(list))

実行結果

I like Python

こんな感じでリストを簡単に文字列に変換してくれる。
CSV形式で出力したいときとかにも使うと便利かもしれない。