流石に業務時間内で堂々とバイク販売サイトを見るのもあれですが、
仕事中の暇な時間を利用して、グーバイクに販売中のバイク一覧をスクレイピングしてみたよ。
今回の流れについて解説しましょう。
なぜスクレイピング?
暇だから
主な原因は、KTM 390 Duke から 890 SMT に乗り換えようと考えています。
そのため、いつもサイトを見るより、コーディング画面のほうが上司から指摘されないから
(小声)
まず調査を行おう
グーバイク側では、データベースから抽出された結果を動的にフロント側で同じdiv構成によってloopしてレンダリングするはずです
(多分。。。。)
パッと見たら、確かに一台ずつ同じbike_sec
との class のdiv
で構成されていますね。
なので今回は、Python のBeautifulSoup
モジュールのfind()
またはfind_all()
を利用して、特定div
の内容を抽出すればいいですよ。
画面構成
詳しく調査した結果、基本的に一台のバイクに対して、<div class="bike_sec">
と<div class="shop_info>
という2個のdiv要素が付与されていることが分かりました。
bike_sec
はバイク情報、shop_info
は販売店情報になっております。
<div class="bike_sec"> 検索結果
<div class="clear_fix">
<div class="bike_img"> 画像
<div class="bike_info"> 車体情報
<div class="model_title"> モデル
<div class="fav_btn"> 検討ボタン
<div class="detail_cont"> 車体情報エリア
<div class="cont01">
<table> 車両価格 + 支払総額
<ul>
<li> 0番
<span> モデル年式
<b> 年式数字 + 年
<li> 1番
<span> 初度登録年
<b> 新車(在庫あり)/ 年式数字 + 年
<li> 2番
<span> 走行距離
<b> ― / 距離数字 + Km
<li> 3番
<span> 車検/自賠責保険
<b> 検 + yyyy/mm
<li> 4番
<span> 修復歴
<b> (不明)
* 修復歴のあるバイク見かけたことない
<div class="cont02">
<div class="shop_info>
<div class="bxl>
<div class="shop_name"> 販売店名前
<dt> 店名
<dd> 場所 + 営業時間
<div class="cus_voice"> 販売店へのお客様の声
メインに調べたい情報は、<li>
の0番目から3番目の
値段
、年式
、初度登録年
、走行距離
、車検
、店名(場所)
です。
また、グーバイクの画面ではデフォルトで50個の結果しか表示しないため、ソース上で画面のページネーションの統計文から、最大ページ数を取得し、for文で繰り返します。
なので、bxr
class の<p>
要素の全{num}ページ
の数値を抽出すればいいです。
使用例
$ python goobike_scraper.py
メーカー: ktm
モデル名: 390_duke
年式を指定するか (y/n): y
年式: 2020
支払総額を昇順で表示するか (y/n): y
メーカー名とモデル名を入力した後、スクレイピングが始めます。
その後、年式を指定するか、または昇順で表示かを聞いてくれます。
出力例
47万円, 2020年, 2021年, 14245Km, 検2026/01, C204 (大阪府守口市佐太中町3丁目9−16ドミール佐太1F)
47万円, 2020年, 2021年, 17380Km, 検2024/08, バイク王 伊丹店 (兵庫県伊丹市北伊丹5−96−1)
47万円, 2020年, 2021年, 14245Km, 検2026/01, C204 (大阪府守口市佐太中町3丁目9−16ドミール佐太1F)
47万円, 2020年, 2021年, 17380Km, 検2024/08, バイク王 伊丹店 (兵庫県伊丹市北伊丹5−96−1)
49万円, 2020年, 2020年, 19239Km, 検無し, バイク館富田林店 (大阪府富田林市喜志町5丁目4−30)
50万円, 2020年, 2020年, 17565Km, 検2025/06, KTM小山 (栃木県小山市雨ケ谷新田69−1)
51万円, 2020年, 2020年, 7422Km, 検無し, ビジョギマンケーブ (埼玉県戸田市美女木4−19−27)
51万円, 2020年, 2021年, 2131Km, 検無し, バイクランド直販センター 環七鹿浜店 (東京都足立区椿2−2−2)
割と綺麗に抽出していますね。
分かったこと
グーバイク側では検索用APIが公開されていないため、あくまでもユーザー側がアクセスできる画面に対した調査結果となります。
また、コマンド引数のメーカー
とモデル名
によって自由に調べられるよう色々実装しましたが、なぜかグーバイク側で生成された固定URLが 規則的ではない と分かりました。
不規則なURL
最初に実装するとき、URLのパラメータは全部
www.goobike.com/maker-{manufacturer}/car-{manufacturer}_{model}/index{page_number}.html
との解釈で大丈夫かと思いましたが、実際に一部のモデル名がアンダーバー付いてたり、付いていなかったりしています。
たまに
maker-{manufacturer}/car-{model}/index.html
の場合もありますが...
以下は例です
https://www.goobike.com/maker-ktm/car-ktm_890smt/index.html
https://www.goobike.com/maker-ktm/car-ktm_890duke_r/index.html
https://www.goobike.com/maker-ktm/car-ktm_390_adventure/index.html
https://www.goobike.com/maker-bmw/car-bmw_f900xr/index.html
https://www.goobike.com/maker-honda/car-honda_cbr250rr/index2.html
外車
まず大好きな KTM のほうから、Dukeシリーズの場合でも
390_duke
890duke
890duke_r
1290_super_duke_r
のようなめちゃくちゃなネーミングルール、何なんだよ。
アドベンチャーシリーズの場合は結構いいです。
790_adventure
890_adventure_r
1290_super_adventure_s
他の外車勢も割とマシだろうと。
BMW は f900xr
r1250gs_adventure
、MVアグスタは f3_800
日本車
次に日本車の場合、
Honda は cbr250rr
crf1000l_africa_twin
、Yamaha は yzf-r6
統一しろよおい(怒)
単に {num}_{seriesModel}
では無理のようです。
修正するなら?
外車の場合、改めてグーバイク側で表示するモデル名の辞書を用意して、アンダーなどを付くか付かないかを変換します。
日本車の場合、URLパラメータは maker-{manufacturer}/car-{model}/
で解釈できそうな感じなので、if文で4大メーカーの場合だけURLを書き換えれば実現できるかと思います。
今後の課題
- 抽出結果をSQLに保存して、新規追加した分のみ提示するように修正すること
- モデル色は今回判別できていないため、画像認識とかを実装して、更にモデル色が分かるようにすること
- 今回は類似csv形式で抽出したため、今度はjson形式で出力し、上記のモデル色など要素も追加し、結果分析を容易にすること
- モデル名の入力規則を追加・修正する