python スクレイピングの決定版！(対象サイト:ビックカメラ)

#やること
pythonを使って、ビックカメラのwebサイトをスクレイピングする。

#作成背景
僕が思う、「良いスクレイピング記事」が全然無かったので、
ヒカキンを超えるYoutuberとしての練習も兼ねて、コード＆Youtube動画を作成した。

#動画リンク
[リンク]https://www.youtube.com/watch?v=SZuNFDzJndA&list=PLzPCqF-heFHyFb_aoqnXc8GrECL6yMdvZ
※全部で2時間くらいある超大作

#環境
* python3.7.0
* jupyter notebook(動画内)

#必要スキル＆環境
* pythonの基礎構文は知っている
* jupyter notebook or google colaboratory環境がある

#このコードを通じて、得たい反応
* python初学者さんに「しゅ、しゅごい・・　プログラミング楽しそう　ｿﾞｸﾘｯ」
* 営業マンに「うっ、これがアレば地獄のコピペエクセルリストアップ作戦をやらずに済む　ｺﾞｸﾘｯ」
* マーケターに「これがアレば、市場の売れ筋商品を把握し、自社の打ち出し方を明確にできるぞ　ﾊﾞｸﾘｯ」
* 金融マンに「対象企業の発表を察知したり、PDFをデータ化(エクセル化)して、投資判断ができるぞ　ｻﾞｸﾘｯ」

#スクレイピングにおいての注意事項
下記のようなこともあるので、ご自身の責任で注意してスクレイピングを行ってください。
岡崎市立中央図書館事件
url : https://ja.wikipedia.org/wiki/%E5%B2%A1%E5%B4%8E%E5%B8%82%E7%AB%8B%E4%B8%AD%E5%A4%AE%E5%9B%B3%E6%9B%B8%E9%A4%A8%E4%BA%8B%E4%BB%B6

また、ビックカメラのサイトUIが変更されると動かなくなる可能性が高いので、
動かなかったらコメントください。　修正するかも。

#とりあえずコード全体
```python:scraping_biccamera.py
from bs4 import BeautifulSoup as bs
from datetime import datetime
import pandas as pd
import requests
import urllib
import time
import re


def get_html(url):
    """
    urlを与えたら、beautiful objectを返す関数
    """
    res = requests.get(url)
    return bs(res.content, "html.parser")


def next_url(res_bs):
    """
    htmlデータ(res_bs)を入れたら、次ページurl or Noneを返す関数
    """
    domain = "https://www.biccamera.com/"
    for res in res_bs.findAll(class_="bcs_l"):
        if "次" in res.text:
            if res.find("a"):
                next_url = domain + res.find("a").get("href")
                return next_url
    return None


def product_df(res_bs):
    """
    ビックカメラhtmlからDataFrameを取得する関数
    """

    # 空のリスト作成
    output = []

    # htmlから商品一覧情報を取得する
    item_list = res_bs.findAll(class_=re.compile(r"prod_box sku*"))

    # item_listから商品情報を1つずつ取得する
    for num, item in enumerate(item_list):
        # 商品詳細URLを取得
        item_url = item.find(class_="cssopa").get("href")
        # 商品タイトル取得
        title = item.find(class_="cssopa").find("img").get("alt")
        # 商品写真取得
        picture = item.find(class_="cssopa").find("img").get("src")
        # 商品のメーカー取得
        maker = item.find(class_="bcs_maker").text
        # 商品価格取得
        price = item.find(class_=re.compile(r"bcs_price*")).text
        # ポイント情報があれば取得、なければ、0を取得
        if item.find(class_="bcs_point"):
            point = item.find(class_="bcs_point").text
        else:
            point = "0"
        # 在庫情報があれば取得、なければ、0を取得
        if item.find(class_=re.compile(r"label_*")):
            stock = item.find(class_=re.compile(r"label_*")).text
        else:
            stock = "0"
        # 評価数情報があれば取得、なければ、0を取得
        if item.find(class_="bcs_star"):
            ratings = item.find(class_="bcs_star").find("a").text
        else:
            ratings = "0"
        # 配送日情報があれば取得、なければ、0を取得
        if item.find(class_="bcs_ship"):
            terms = item.find(class_="bcs_ship").text
        else:
            terms = "no ship info"
        # 上記で取得した商品ごとの情報をoutput(list)にappend(追加)していく
        output.append({
            "item_url": item_url,
            "title": title,
            "picture": picture,
            "maker": maker,
            "price": price,
            "point": point,
            "stock": stock,
            "ratings": ratings,
            "terms": terms,
        })
    # 全情報をoutputに保存し、outputをpandas dataframeに格納する
    df = pd.DataFrame(output)
    return df


def get_product_list(url, pages=10):
    """
    対象のurlを入れたら、ページ遷移し、
    すべての商品一覧情報をdataframeとして返す関数
    """

    # 空のdataframeを作成
    all_df = pd.DataFrame()

    for _ in range(pages):
        # 入力されたURLからhtml取得
        res_bs = get_html(url)

        # htmlからdataframe取得
        df = product_df(res_bs)

        # all_dfに上記で作成したdfをappend(追加)していく
        all_df = all_df.append(df)

        # all_dfの数をprintし、追加されていくことを確認する
        print("all_df:", len(all_df))
        print(f"sleeping.....{_}回目")

        # 相手のサイトに負荷を与えないように時間を5秒空ける
        time.sleep(5)

        # 次ページURL or None取得
        url = next_url(res_bs)

        # もしurl がNoneであれば、for文は終了
        if url is None:
            print("break")
            break
            return all_df
    return all_df


if __name__ == "__main__":
    # 取得したいデータがあるURLを定義
    url = "https://www.biccamera.com/bc/category/?q=laptop&rowPerPage=100#bcs_resultTxt"

    # 全ページ遷移し、データを取得する
    df = get_product_list(url)

    # urlから検索情報を取得
    qs = urllib.parse.urlparse(url).query
    # 検索情報を辞書にする
    kw = urllib.parse.parse_qs(qs)
    # 検索ワードを取得
    query = kw.get("q")[0]

    # 取得したデータをcsvとして保存する
    today = datetime.today().strftime("%Y%m%d_%H%M%S")
    df.to_csv(f"{today}_biccamera_{query}", index=False)

    # 下記のようにpagesに数値を代入すると、ページ遷移する回数を20回に変更することができる
    # df = get_product_list(url, pages=20)
```

#感想
上記のコードを1から説明はしないので、ご興味ある方を動画をご覧いただければと思う。

また、ビックカメラはスクレイピング難易度が若干高く(re.complileを使う)、
教材としては良いので、この原則を理解しさらなるスクレイピング生活を満喫していただきたいと思う。

今後もyoutuberとして、データの前処理、保存および可視化等をやっていき、
「好きなことで生きていく」を実現していく。