2018年8月15日水曜日

Webスクレイピングやってみた (Python, Beautiful Soup)

機械学習で遊んでみたくてですね。

それに使う文章を用意するのに、手作業でコピペ出来なくもない量だったんですけど、Twitterでちょうどこの記事が流れてきたので、Webスクレイピングにトライしてみました。
Python・PHP・Perl・Bash・Rubyで簡単にWebスクレイピングする方法を解説する - paiza開発日誌

Pythonでやりましたが、5年くらい前に勉強した記憶を元に動けばいいや方針でやってるので、色々間違いあるかもしれません…。

やりたいこと

やりたかったのは、アニメロミックスというサイトに掲載されている「神谷浩史・小野大輔のDear Girl~Stories~」というラジオ番組の企画で配信された着ボイス(Dear Voice)の一覧を取得することです。
趣味に走った題材です(笑)

方針

現状、アニメロのサイトにはDear Voiceだけがまとまっているページはないので、神谷さん、小野さんそれぞれのアーティストページの「ボイス」カテゴリの中から、番組名「神谷浩史・小野大輔のDear Girl~Stories~」が表示されているものだけを抽出します。

実装

主にこちらのページ参考にさせていただきました。
PythonとBeautiful Soupでスクレイピング - Qiita
PythonでWebスクレイピング - imoniの開発Blog

PaizaCloud のJupyter Notebook使いました。(最近Pythonを使ってる環境がそこだったので)

import


import requests
from bs4 import BeautifulSoup
from time import sleep

requests はWeb上のページを取得するのに必要と理解してます。
BeautifulSoup は、標準のHTMLParserだけでやるには難しい条件で要素を探したかったので。
sleep は、瞬間的に大量のリクエストを投げて負荷をかけるのを避けるため、待ち時間を入れるのに使いたくて。

対象となるページ


URL_C = "https://pc.animelo.....188/genre:voice" # 神谷浩史のボイス1ページ目
URL_D = "https://pc.animelo.....032/genre:voice" # 小野大輔のボイス1ページ目

1ページ目のURLがこんな感じで、
2ページ目になると https://pc.animelo.....188/genre:voice/page:2 みたいに末尾にページ数が付加される挙動でした。
(手動でアクセスすると1ページ目のURLにはページ数が出ませんが、page:1と入れてもアクセスできました。)

(後述の利用規約等あり、コピペだけで実行できるコードを載せるのにちょっと抵抗があったので、一応URL伏せさせていただきました)

取得したいテキストの周りはこんな感じの構造になってまして


<div class="detail">
  <p class="title">
    <a href="/portals/product/xxxxxx">ここに取得したいテキストがある</a>
  </p>
  <p class="sub-title">...</p>
  <p class="contnt-type">...</p>
  <p class="description">
    「神谷浩史・小野大輔のDear Girl~Stories~」
  </p>
</div>
  • class="detail" となっている要素をすべて探す(list的な感じで取得される)
    • 取得した class="detail" の要素に対し、 class="description" のテキストが 神谷浩史・小野大輔のDear Girl~Stories~ を含むかどうかをチェック
    • 含むなら最初の `a` タグのテキストを出力

という感じでやってみました。

関数を作成


def get_voices(initial_url):
    
    num = 1 # スタートするページ数
    
    while num <= 30: # 無限ループしないように最大30ページに制限
        
        url = initial_url + "/page:" + str(num) # ページ数付きのURLを作成
        num = num + 1 # ページ数を増やす(次のページ数)
        
        print(url) # 後から情報を精査したかったのでページごとにURLも出力
        
        # 指定のURLの情報を取得して、BeautifulSoupで扱えるようにする
        HTML = requests.get(url)
        SOUP = BeautifulSoup(HTML.content, "html.parser")

        # 「配信コンテンツは見つかりませんでした」のメッセージが
        # 表示されていたら最後のページ(を超えた)と判断。whileループを終了する。
        if SOUP.find(class_="product-list-not-found") is not None:
            print("---取得終了---")
            break
        
        else:
            # class="detail" の要素をすべて取得
            res_set = SOUP.find_all(class_="detail")

            # 取得したclass="detail"の要素ひとつずつに対して実行
            for res in res_set:
                # class="description" の要素を取得
                # ここが「神谷浩史・小野大輔のDear Girl~Stories~」ならDear Voice
                desc = res.find(class_="description")

                if desc is None:
                    # 画面右に表示されるタイアップ情報の欄もclass="detail"になっていたので、
                    # descriptionが見つからないときはこれ以降の処理は飛ばして次のforループへ
                    # (たぶんタイアップ情報が最後に来るんだけど一応)
                    continue

                # Dear Voiceの場合のみ出力する
                if "神谷浩史・小野大輔のDear Girl~Stories~" in desc.string:
                    # 最初のaタグにボイスの文言が入っているのでそれを出力する
                    voice = res.find("a")
                    print(voice.string) # 出力
                    
                # forループここまで

            sleep(3) # 瞬間的に大量にアクセスして迷惑かけないように待ち時間を…

            # whileループここまで

呼び出し


# 神谷さんのボイス一覧を取得
print("===神谷さん===")
get_voices(URL_C)

# 小野さんのボイス一覧を取得
print("===小野さん===")
get_voices(URL_D)

結果

こんな感じで出力されます。


補足:
アニメロの利用規約ではスクレイピング自体は禁止されてないようですが、「サービスの内容」の「再利用」はNGとのこと。
商品そのもの(Dear Voiceなら音声)を再利用するわけではなくて、誰でも見れる商品ラインナップを分析して楽しむだけなので大丈夫かなと思ってやっておりますが…
コード学習目的で公開しておりますが、アニメロさんにご迷惑をかけることの無いようご配慮いただけますと幸いです。

0 件のコメント:

コメントを投稿