最近は、スクレイピングが趣味になってきました。
ある意味、スクレイピングとはサイト側との戦いでもあるわけです。
もちろん、戦いと言っても暴力による殴り合いではありません。
例えれば、知恵比べのような感じですね。
もっと砕けた表現にすると、ゲームみたいなモノです。
ただ、ゲームのような遊びだけではありません。
なんと、コーディングの勉強にもなるのです。
各サイトによって、コーディングのやり方もマチマチです。
教科書通りにコーディングするサイトもあります。
それとは逆に、プログラムによる自動生成のようなサイトもあります。
このような例を見ていくことは、必ず活きた知識になります。
さて、その楽しくてためにもなるスクレイピングを本日もやっていきます。
ターゲットは、FANZA動画(旧DMM)です。
本記事の内容
- FANZA動画(旧DMM)には数年前にボロ負けしている
- FANZA動画(旧DMM)レビューのスクレイピング仕様
- 【サンプルコード】FANZA動画(旧DMM)レビューのスクレイピング
それでは、上記に沿って解説をしていきます。
FANZA動画(旧DMM)には数年前にボロ負けしている
最近は、スクレイピングでは連戦連勝です。
主な戦いを取り上げます。
他にも転職サイトに絞ってスクレイピングを行っています。
転職サイトをスクレイピングしているのは、理由があります。
ブラック企業をぶっこわすためです。
それについては、以下の記事で解説しています。
まだまだ、本ブログではスクレイピングによる戦いの記録があります。
興味がある方は、是非とも他もご覧ください。
また、以下のような方にも参考となる情報を多く載せています。
- スクレイピングを学びたい
- 機械学習用のデータ収集をしたい
- Nintendo Switchの在庫を知りたい
ここまで見てきたように、スクレイピングに関してはかなりの経験値があります。
それもこれもPythonと出合ったからです。
そこに加えて、Seleniumの存在ですね。
PythonにSeleniumは、鬼に金棒のようなモノです。
これらを武器にした今は、「オラもっと強い敵と戦いてえ」状態です。
だから、日々その相手を探しています。
そんなときに、忌々しい過去を思い出したのです。
FANZA動画(旧DMM)にボロ負けしたことを・・・
そのときは、まだPythonも手元にはありません。
あるのは、PHPとPhantomJSでした。
PhantomJSは、動的コンテンツにも対応はできます。
しかし、FANZA動画(旧DMM)には全く通用しませんでした。
私のスキル不足なのか、PhantomJSの機能的な限界なのか、本当の原因はわかりません。
とにかく、全く通用せずにボロ負けした事実だけがありました。
その過去を思い出し、「リベンジだ!!」となったわけです。
FANZA動画(旧DMM)レビューのスクレイピング仕様
今回は、レビューワーのページをスクレイピングします。
http://www.dmm.co.jp/review/-/list/reviewer/=/id=169208/
動画ページを起点にスクレイピングをやろうかどうか迷いました。
ただ、今回はリベンジがメインです。
前回にボロ負けしたのは、レビューワーのページになります。
だから、同じレビューワーのページでやり返したいのです。
また、必要であれば、動画ページを起点にスクレイピングを別記事で解説します。
レビューワーのページをスクレイピングする上でのポイントは、一つだけです。
改ページ対策です。
ボタン(リンク)先にはページが指定されていません。
つまり、hrefのある普通のaタグではないということです。
<div class="pfReview__pagenation"> <ul> <li><span>1</span></li> <li><a href="#review" onclick="review_load('2', 'yes_desc', ''); ">2</a></li> <li><a href="#review" onclick="review_load('2', 'yes_desc', ''); ">次へ</a></li> </ul> </div>
上記が、改ページ部分のhtmlソースになります。
JavaScriptにより、ページを非同期で呼び出しています。
よって、静的ページの対応ではなく、動的ページの対応が必要となります。
そして、ページ遷移(コンテンツ追)には、クリック処理が必要です。
Seleniumを使えば、簡単です。
では、以下で解説します。
【サンプルコード】FANZA動画(旧DMM)レビューのスクレイピング
サンプルコードは以下。
2020年08月22日時点は動いています。
from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from bs4 import BeautifulSoup import time import pandas as pd import re reviewer_id = "34391" chromedriver = "chromedriver.exeのパス" file_path = "./dmm_data/" + reviewer_id + ".json" def get_review(reviewer_id): url = "http://www.dmm.co.jp/review/-/list/reviewer/=/id=" + reviewer_id + "/" review_list = [] # ヘッドレスモードでブラウザを起動 options = Options() options.add_argument('--headless') # ブラウザーを起動 driver = webdriver.Chrome(chromedriver, options=options) driver.get(url) # pfReview__pagenationクラスが読み込まれるまで待機(最大15秒) WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.CLASS_NAME, "pfReview__pagenation"))) review_list = get_article(driver, review_list) next_flg = True # データ取得 while next_flg: next_flg = next_btn_click(driver) review_list = get_article(driver, review_list) # ブラウザ停止 driver.quit() return review_list def get_article(driver, review_list): elems_article = driver.find_elements_by_tag_name("article") for elem_article in elems_article: tag = elem_article.get_attribute("innerHTML") info = get_info_of_article(tag) review_list.append(info) time.sleep(1) return review_list def get_info_of_article(data): soup = BeautifulSoup(data, features="lxml") # コンテンツタイトル elem_c_title = soup.find(class_="pfReview__goodsinfo--name").find("a") c_title = get_text_by_elem(elem_c_title) # コンテンツURL url = elem_c_title.get("href") # コンテンツid cid = "" find_dic = re.findall('\/cid=(.*)$', url) if find_dic: cid = find_dic[0] # 投稿日 elem_postdate = soup.find(class_="pfReview__label--postdate") postdate = get_text_by_elem(elem_postdate) # タイトル elem_title = soup.find(class_="pfReview__title--text") title = get_text_by_elem(elem_title) # 評価 elem_rating = soup.find(class_="pfReview__title").find("span") rating_dic = elem_rating.get("class") rating = "" if rating_dic: rating = rating_dic[0] rating = rating.replace("d-rating-", "") # コメント elem_comment = soup.find(class_="pfReview__comment--text") comment = get_text_by_elem(elem_comment) info = {} info["c_title"] = c_title info["c_url"] = url info["c_id"] = cid info["postdate"] = postdate info["title"] = title info["rating"] = rating info["comment"] = comment return info def get_text_by_elem(elem): try: text = elem.text text = text.strip() return text except Exception as e: return None def next_btn_click(driver): # pfReview__pagenationクラスが読み込まれるまで待機(最大15秒) WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.CLASS_NAME, "pfReview__pagenation"))) try: elem_pagenation = driver.find_element_by_class_name("pfReview__pagenation") elem_next = elem_pagenation.find_element_by_link_text("次へ") if elem_next: elem_next.click() return True else: return False except: return False if __name__ == "__main__": # 結果取得 result = get_review(reviewer_id) # データフレームに変換 df = pd.DataFrame(result) # jsonとして保存 df.to_json(file_path, orient='records')
モジュール・ライブラリをインストールしておいてください。
わからない方は、この記事で載せた過去記事を参考にしてください。
レビューワーの指定は以下で行っています。
reviewer_id = "34391"
レビューワーのIDは、レビュアーランキングを参考にすれば取得できます。
http://www.dmm.co.jp/review/-/ranking/
なお、コピペで動かす場合には、ディレクトリ・フォルダを掘ってください。
サンプルコードでは、プログラムファイルと同一階層にディレクトリ・フォルダを作成しています。
file_path = "./dmm_data/" + reviewer_id + ".json"
上記により、作成したディレクトリ・フォルダにレビューワーID名称のjsonファイルが作成されます。
そのjsonファイルに、該当レビューワーの全レビューが保存されます。
コードの詳細は説明しません。
これぐらいのコードを解読できないと、スクレイピングをやってはダメだと思うからです。
そのスクレイピングに対する考え方は、過去記事で説明しています。
興味がある方は、ご覧ください。
最後にまとめます。
FANZA動画(旧DMM)へのリベンジは、成功です。
勝負になりませんでした。
やはり、難攻不落のTwitterをスクレイピングした際にかなりレベルアップできたようです。
最後に募集です。
意外とスクレイピングに興味のある方は多いことがわかってきました。
私への連絡には、以下の手段があります。
私にスクレイピングして欲しいサイトがあれば、その旨をお伝えください。