1
2

More than 3 years have passed since last update.

食べログ3.8問題を時系列で解析してみる(その1)

Last updated at Posted at 2019-11-17

はじめに

一部報道konkon3249さんの記事に興味があり、スクレイピングやデータ分析の題材に良さそうな気がした。

konkon3249さんは、最新のデータ(直接食べログのサイトをスクレイピング)について網羅的に調べているようなので、私は時系列で評価の推移を観察して、何か傾向が見られるかどうかPythonや定番のライブラリを使ってやってみた。

やりたいこと

過去のある時点での評価と口コミ数から、口コミ数が変わっていないのに直近の評価が上下しているケースの有無を調べる。

とりあえず、適当に選んだ高評価の店舗について、試してみることにする。

過去の情報を得るためのデータソースは、インターネットアーカイブの大手であるWayback Machine(https://archive.org/web/) を使った。

その他、実行環境は以下の通り。

実行環境

MacBook Pro(13inch, 2016, macOS 10.15.1)
Python 3.7.3
beautifulsoup4 4.8.1
selenium 3.141.0 (webdriver:PhantomJS) など

食べログのページはWayback Machineに存在するか?

Webブラウザでhttps://archive.org/web/ にアクセスして、食べログのサイト内で検索した適当な店舗のページのURLを検索欄にコピペしてみて確認。

直近のページと過去のページ(約200)が記録されていることが確認できた。

食べログWebスクレイパーの作成

アーカイブされたデータの所在は確認できたが、手動でいちいち過去のデータをクリックして確認するのは面倒なので、自動化したい。

Wayback Machineが所得したアーカイブにアクセスするためのURL一覧を得る。

以下のようなスクリプトを実行すると、アーカイブされたデータのタイムスタンプ等の情報がJSON形式で得られる。

参照:Wayback CDX Server API - BETA

import requests
import json

url =   "http://web.archive.org/cdx/search/cdx?url=<オリジナルのページのURL>&output=json"
r = requests.get(url)
res = json.loads(r.text)

print (res)


# 出力例(http://www.archive.org:80/)
[["urlkey","timestamp","original","mimetype","statuscode","digest","length"],
["org,archive)/", "19970126045828", "http://www.archive.org:80/", "text/html", "200", "Q4YULN754FHV2U6Q5JUT6Q2P57WEWNNY", "1415"],
["org,archive)/", "19971011050034", "http://www.archive.org:80/", "text/html", "200", "XAHDNHZ5P3GSSSNJ3DMEOJF7BMCCPZR3", "1402"],
["org,archive)/", "19971211122953", "http://www.archive.org:80/", "text/html", "200", "XAHDNHZ5P3GSSSNJ3DMEOJF7BMCCPZR3", "1405"],
["org,archive)/", "19971211122953", "http://www.archive.org:80/", "text/html", "200", "XAHDNHZ5P3GSSSNJ3DMEOJF7BMCCPZR3", "1405"],
(後略)


ここで得られた各行のtimestamporiginalを使ってアーカイブされたページへアクセスするためのURLを作る。

url_to_archive = "https://web.archive.org/web/" + str(timestamp) + "/" + original"

アーカイブされたページを取得し、評価口コミ数を取り出す。

スクレイピングのテストを繰り返しているうちに判明したのだが、最近のページと過去のページのソースコードが異なっていた。以下のように、平均評価(value)と口コミ数(count)を取り出すには2パターンの場合分けが必要。

from bs4 import BeautifulSoup
import certifi
import json
import lxml
from selenium import webdriver

browser.implicitly_wait(3)    
browser.get(url_to_archive)

html = browser.page_source

bsObj = BeautifulSoup (html,'lxml')

# 評価と口コミ数を得る。
# 最近の場合
elems = bsObj.find_all(attrs={"type": "application/ld+json"})
data = json.loads(elems[0].text)
value = ( data['aggregateRating']['ratingValue'])
count = ( data['aggregateRating']['ratingCount'])

# 2012年頃〜の場合
value =  (bsObj.find(property="v:average").string)
count = (bsObj.find(property="v:count").string)  

一連の処理を実行してその結果をcsv形式で出力するスクリプトを書いて、とある店舗について実行した結果が以下のとおり。(途中、変化のない区間は省いている。)

#タイムスタンプ(YYYYMMDDHHMMSS), 平均の評価, 口コミ数
20120712001811,4.00,178
20120823204734,3.98,176
20121010100313,3.95,179
20121228094542,3.94,190
20121229233853,3.94,190
20121230044025,3.94,190
20130301010937,4.00,196
20130501005416,3.96,199
20130516032644,3.98,205
20130530171236,3.98,206
20130608090337,3.98,207
20130807220240,4.00,210
20130810025822,4.00,210
20130826151204,4.00,211
20130917231828,4.00,214
20130919180620,4.00,214
20131116054758,4.02,222
20131117042544,4.02,222
(中略)
20131128010751,4.02,222
20131129010046,4.02,222
20131130014555,4.02,223
20131205004811,4.03,223
20140103115852,4.01,226
20140107174211,4.01,226
20140108173338,4.01,226
20140109213307,4.01,226
20140110185815,4.01,226
20140111185636,4.01,226
20140112202818,4.01,226
20140113201409,4.01,226
20140114035500,4.01,226
20140115051519,4.01,227
20140116060105,4.01,227
(中略)
20140120074835,4.01,227
20140121103358,4.01,227
20140122130311,4.01,228
20140123130001,4.01,228
20140124130025,4.01,228
20140125133138,4.01,228
20140125165250,4.01,228
20140126191900,4.01,228
20140127154431,4.01,228
20140128210518,4.03,228 <== 口コミ数が増減していないが、評価が0.02pt上昇
20140129175006,4.03,228
(中略)
20140209054119,4.03,228
20140404234713,4.01,231
20140626065615,4.02,243
20141013163812,3.98,253
20141222194224,4.04,257
20150116150712,4.04,257
20150311192445,4.04,261
20150322104024,4.04,262
20150527061422,4.00,271
20150814120559,3.96,278
20150923190523,3.98,284
20160420053800,3.94,268
20160520025127,3.94,273
20160521155101,3.94,274
20160722070049,3.95,278
20161222130858,4.04,287
20161223133251,4.04,287
(中略)
20170115083632,4.04,287
20170116094551,4.04,287
20170207071014,4.05,291
20170208090014,4.05,291
20170209094354,4.05,291
20170210104538,4.05,291
20170211121243,4.05,291
20170213031841,,      <== データなしorスクレイピング失敗
20170214083436,4.05,291
20170215095625,4.05,291
20170216105449,4.05,291
20170217102729,4.05,291
20170218105257,4.05,291
20170219113547,4.05,291
20170222033727,4.04,291 <== 口コミ数が増減していないが、評価が0.01pt下落
20170223094742,4.04,291
20170224110800,4.04,291
20170225115208,4.04,291
20170227122522,4.04,291
20170228135545,4.04,291
20170301142247,4.04,291
20170302155058,4.04,292
20170303162840,4.04,292
(中略)
20170312000538,4.04,292
20170313014106,4.04,292
20170314025314,4.04,291
20170315033510,4.04,291
20170316041606,4.04,293
20170317052049,4.04,293
20170318042722,4.04,293
20170319063727,4.04,293
20170320071830,4.04,293
20170321080150,4.03,293 <== 口コミ数が増減していないが、評価が0.01pt下落
20170322083720,4.03,293
(中略)
20170328140815,4.03,293
20170330000530,4.03,293
20170418061540,4.03,292
20170419070929,4.03,292
20170420075553,4.03,292
20170421075837,4.03,292
20170423053124,4.03,292
20170424065419,4.03,293
20170425075803,4.03,293
20170426083535,4.03,293
20170620210216,4.02,295
20170621214058,4.02,295
20170622221115,4.02,295
20170623230711,4.02,295
20170624233253,4.02,295
20170626011722,4.02,295
20170921172613,3.96,301 <== 口コミ数が6増加して、評価が0.06pt下落(詳細は後述)
20180112191631,3.96,304
20190311140653,3.94,335
20190312150237,3.94,335
20190313162633,3.94,335
20190314170559,3.94,335
20190315182415,3.94,335
20190316191126,3.94,335
20190317194209,3.94,335
(中略)
20190510141143,3.94,334
20190511134833,3.94,335
20190512142049,3.94,335
20190513161349,3.94,334
20190514171536,3.94,334
20190515181514,3.94,334
20190516191005,3.94,334
20190517181103,3.94,334
20190518202325,3.94,334
20190519210735,3.94,335

データから見える興味深い点

1 当初期待していた口コミ数が増減せずに、評価が変化

たまたま選択した店舗が良かったのか、期待していた結果が得られた。

20140127154431,4.01,228
20140128210518,4.03,228 <== 口コミ数が増減していないが、評価が0.02pt上昇
20170219113547,4.05,291
20170222033727,4.04,291 <== 口コミ数が増減していないが、評価が0.01pt下落
20170320071830,4.04,293
20170321080150,4.03,293 <== 口コミ数が増減していないが、評価が0.01pt下落

概ね1〜2日の間で平均評価が0.01-0.02pt程度変化している。

サンプル数を増やすと何か傾向が見えてくるかも知れない。

2 ガチの低評価? 嫌がらせ?
20170626011722,4.02,295  <== 口コミ数が6増加して、評価が0.06pt下落(詳細は後述)
20170921172613,3.96,301

これは、およそ3ヶ月(2017年6月下旬〜同年9月下旬)の間に、口コミ数が6増加して、評価が0.06pt下落している。

増加分の口コミ数の平均評価をxとし、純粋に6名分の口コミが増加したものと仮定すると、

(4.02*295)+(x*6) = 3.96*301

の方程式が成り立ち、

x= (3.96*301 - 4.02*295) / 6

これをPython電卓で計算すると、

>>> (3.96*301-4.02*295)/6
1.0100000000000289

計算に用いた2つの平均評価4.023.96自体が端数を切り捨てられているはずなので、その点を考慮すると、増加分の6名分の評価は全て1の可能性が極めて高い・・・。

まとめ

今回は、食べログの過去のデータを参照し、特定の店舗について平均評価と口コミ数を抽出するPythonプログラミングの一部を紹介し、サンプルで実行した結果について簡単な考察をしてみた。

次回は、サンプル数を増やしていろいろ分析できるようにしたい。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2