仕事と婚活と私~ITエンジニアの末路~

仕事(IT/SE)・婚活・ツイッターの話とか、いろいろ TW:@mara_ashida_

MENU

【IT】JavaScriptで食べログの店舗情報を取得する方法/Webスクレイピング/csvデータ #食べログ #JavaScript

導入

blogさぼってましたが、IT関連は自身の備忘のために記録したいと思います。

★テーマ

JavaScript食べログの店舗情報をWebスクレイピングする方法について記録します。

HTMLのxpathの勉強のためにやってみました。

食べログAPIサービスは停止しているとのことで、店舗情報を取得するには下記のように地道にWebスクレイピングするしかないようです(?)(2019/04現在)。

https://cdn-ak.f.st-hatena.com/images/fotolife/m/maramara_ashida/20190414/20190414022211.png

目次・流れ

No タイトル
1 概要/事例/目的
2 コード/実行結果
3 解説・補足
4 ソース改良版(2019/04/20)
<前提>
JavaScript/CSS/HTMLの違いを理解していること
JavaScriptの基本文法について理解していること
JavaScriptは完全独学のため、コーディング規約等遵守していないです。
<実行環境>
・OS:Windows10
・ブラウザ:Chrome バージョン: 73.0.3683.103(Official Build) (64 ビット)

概要/事例/目的

テストで利用するDBにサンプルデータを登録しようと思ったのですが
実データっぽいものがないかな?と思い、食べログのデータを抽出してみました。

下記の赤枠(※)の箇所に関して、カンマ区切りのデータを抽出するscriptを記載しました。
※店名,場所,ジャンル,点数,口コミ数(銀座のお店を検索しました。)

https://cdn-ak.f.st-hatena.com/images/fotolife/m/maramara_ashida/20190414/20190414013508.png

銀座・新橋・有楽町のグルメ情報 口コミ数順 [食ベログ]

コード/実行結果

実行画面

実行方法は下記の通り。gifをご参考ください。

  • ①対象食べログ画面を表示
    (※【任意】口コミ数順にしてます)
  • Chrome>F12キー>Consoleタブ にて下記コードを張り付けて、Enterキーで実行

https://cdn-ak.f.st-hatena.com/images/fotolife/m/maramara_ashida/20190414/20190414013601.gif

コード

スマホだと意味不明だと思うので、PCでご覧ください。

/* 初期化*/
var work = '';
var result
= 'No,店名,場所,ジャンル,点数,口コミ数\r\n'; 
/*出力header情報*/

/*●変数
・shop_name:店名(class名で取得)
・shop_summry:概要(class名で取得)
・shop_rate:点数(class名で取得 ※点数が表示されない場合がある。要素の個数に応じて処理を分岐。)
・shop_reviews:口コミ数(class名で取得)

※class名やHTMLの構造は食べログサービスによって変更されるものであり、動作を常に保証するものではありません。
*/
var shop_name 
= document.getElementsByClassName("list-rst__rst-name-target cpy-rst-name");
var shop_summry 
= document.getElementsByClassName("list-rst__area-genre cpy-area-genre");
var shop_rate 
= document.getElementsByClassName("list-rst__rate");
var shop_reviews 
= document.getElementsByClassName("list-rst__rvw-count-num cpy-review-count");

for(var i = 0; i < shop_name.length; ++i){
    
  /*点数エリアの要素数*/
  var val_area_cnt 
  = document.getElementsByClassName("list-rst__rate")[i].children.length;

  /*点数が表示される場合*/
  if ( val_area_cnt == 2){
    work = (i+1) 
    +','+ shop_name[i].innerText 
    +','+ ((shop_summry[i].innerText).replace(' / ',',')).trim()
    +','+ shop_rate[i].children[0].innerText 
    +','+ shop_reviews[i].innerText 
    + '\r\n';
result = result + work;
  /*点数が表示されない場合*/
  } else if (val_area_cnt == 1){
    work = (i+1) 
    +','+ shop_name[i].innerText 
    +','+ ((shop_summry[i].innerText).replace(' / ',',')).trim()
    +','+ 'No Score' 
    +','+ shop_reviews[i].innerText 
    +  '\r\n';
    result = result + work;
  }
};

実行結果

食べログは1ページごとに20店舗表示されるので、2ページ以降もページ遷移して、地道に取得してみてください。

No,店名,場所,ジャンル,点数,口コミ数
1,支那麺 はしご 本店,銀座駅 251m,担々麺、ラーメン,3.59,857
2,グルガオン,銀座一丁目駅 33m,インド料理、インドカレー,3.64,760
3,銀座 久兵衛 銀座本店,新橋駅 357m,寿司,3.79,718

(~省略~)

18,銀座千疋屋 銀座本店 フルーツパーラー,銀座駅 25m,フルーツパーラー、パフェ、ケーキ,3.59,392
19,銀座 しまだ,新橋駅 375m,割烹・小料理、魚介料理・海鮮料理、居酒屋,3.92,371
20,インペリアルバイキング サール,日比谷駅 300m,バイキング、洋食・欧風料理(その他)、西洋各国料理(その他),3.63,367

下記のように、textデータを整形してエクセルに表示してみると、結構壮観です。

https://cdn-ak.f.st-hatena.com/images/fotolife/m/maramara_ashida/20190414/20190414013511.png

解説

<操作>

  • getElementsByClassName("Class名")メソッド
  • 返り値:HTMLCollection ※配列(Array)ではないです。

上記メソッドでHTMLCollection[0]~[19](合計20店舗分)を取得できます。
本HTMLCollectionをfor文でLoop処理して、innerTextを取得しているだけです。

<補足>

  • 【場所とジャンル】は' / '(スラッシュ)で1つのデータとして結合されている(例:「銀座駅 251m / 担々麺、ラーメン」)ので、 カンマ区切りに置換してます。また、前後の半角スペースを削除してデータクレンジングしてます。

  • 食べログに点数が表示されない場合があるため、「No Score」と表示するようにしてます。 異常値データが混在しており、どこまでプログラムで場合分けするか悩ましいですね。

  • 大前提ですが、class名やHTMLの構造は食べログサービスによって変更されるものであり、本scriptの動作を常に保証するものではありません。 ご注意ください。

以上。

【婚活】非モテ・エンジニア流!食べログでデートに使えるお店を知る3つのコツ - 仕事と婚活と私~ITエンジニアの末路~

ソース改良版(2019/04/20)

■修正内容
・夜の価格帯・昼の価格帯の追加
・分岐のコードの共通化

/* 初期化*/
var work = '';
var result
= 'No,店名,場所,距離,ジャンル,点数,口コミ数,夜価格(From),夜価格(To),昼価格(From),昼価格(To)\r\n'; 
/*出力header情報*/

/*●変数 下記class名で取得
・shop_name:店名
・shop_summry:概要
・shop_rate:点数
・shop_reviews:口コミ数
・shop_budget_dinner:夜の価格帯
・shop_budget_lunch:昼の価格帯
※class名やHTMLの構造は食べログサービスによって変更されるものであり、動作を常に保証するものではありません。
*/
var shop_name 
= document.getElementsByClassName("list-rst__rst-name-target cpy-rst-name");
var shop_summry 
= document.getElementsByClassName("list-rst__area-genre cpy-area-genre");
var shop_rate 
= document.getElementsByClassName("list-rst__rate");
var shop_reviews
= document.getElementsByClassName("list-rst__rvw-count-num cpy-review-count");
var shop_budget_dinner
= document.getElementsByClassName("c-rating__val list-rst__budget-val cpy-dinner-budget-val");
var shop_budget_lunch
= document.getElementsByClassName("c-rating__val list-rst__budget-val cpy-lunch-budget-val");

for(var i = 0; i < shop_name.length; ++i){
    
  /*点数エリアの要素数*/
  var val_area_cnt 
  = document.getElementsByClassName("list-rst__rate")[i].children.length;
  
  work = (i+1) 
  +','+ shop_name[i].innerText 
  +','+ shop_summry[i].innerText.replace(' / ',',').trim().replace(' ',',');
  /*点数が表示される場合*/
  if ( val_area_cnt == 2){
    work = work 
    +','+ shop_rate[i].children[0].innerText;
  /*点数が表示されない場合*/
  } else if (val_area_cnt == 1){
    work = work 
    +','+ 'No Score' ;
  }
  work = work 
  +','+ shop_reviews[i].innerText.replace(' - ','0')
  +','+ shop_budget_dinner[i].innerText.replace(/¥/g,'').replace(/,/g,'').replace('~',',')
  +','+ shop_budget_lunch[i].innerText.replace(/¥/g,'').replace(/,/g,'').replace('~',',')
  + '\r\n';
  result = result + work;
};

某4人兄弟バンドに関して宣伝させてください。

2019年内に私の好きなバンド【SaToMansion】が解散の危機を迎えています!

マジで勘弁して欲しいけど。彼らも本気ということです。

彼らの曲を聴いて下さい。マジで掛値なしにかっこいい。

そしてYou Tubeのチャンネル登録をお願いします!

www.youtube.com www.youtube.com www.youtube.com