Kandatsu
@Kandatsu

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

@cosme(アットコスメ)の商品口コミをpythonで収集する方法を知りたい

@cosme(アットコスメ)の商品口コミをpythonで収集する方法を知りたい

大学の研究でテキスト分析をすることになり、化粧品の口コミを集めることにしました。
利用規約にスクレイピングに関することが記載されていましたが、学術利用であることを問い合わせたら許諾されたので、倫理的に問題はありません。

商品レビューのURL例(https://www.cosme.net/products/10258796/review/?page=2)

このURLのページから「年齢」「肌情報(乾燥肌、混合肌など)」「口コミ」「星」のようにカテゴリを分けてデータを集めたいです。

該当するソースコード

from bs4 import BeautifulSoup
import requests
import pandas as pd
import time
from datetime import datetime as dt
from google.colab import files


def get_10reviews_list(soup):
   """
   10件ごとの一覧ページから、指定範囲の投稿日の口コミurlを取得
   投稿日をkey、口コミurlをvalueとした辞書を出力
   """
   read = soup.select("p.read")
   review_urls = [s.a.get("href") for s in read]
   #各口コミのURLはp class="read"内のaタグに格納されているので
   #p class="read"をリスト取得して、それぞれのhrefを取得

   for i in range(len(read)):
       soup.select_one("p.reviewer-rating").decompose()
   #投稿日はdiv class="rating clearfix"内のp class="date"もしくは"mobile-date"
   #に格納されている。deteとmobiledateで2種類あるため、rating clearfixにある
   #ほかの内容(p class="reviewer-rating)を削除して、div内に投稿日情報のみ残す。

   div = soup.select("div.rating.clearfix")
   review_dates = [dt.strptime(i.text.strip(),"%Y/%m/%d %H:%M:%S") for i in div]
   #投稿日を上から順に、datetime情報に変換しながらリストとして取得

   dic = dict(zip(review_dates,review_urls))
   #投稿日をkey、口コミURLをvalueとした辞書を作成
   return dic


def next_check(soup):
   """10件ごとの一覧ページに「次に」があるかどうかを判定
   「次に」があればTrue、なければFalseを返す"""
   next = soup.select_one("li.next span") 
   return next == None
   #最終ページの「次へ」がspanになっていることを利用。


def get_review_data(url):
   """
   指定のアットコスメ口コミurlからデータを抽出
   投稿者情報などをリストで出力
   """
   rev_response = requests.get(url).text
   soup = BeautifulSoup(rev_response,"html.parser")
   #レビューページのレスポンス

   review = soup.select_one("div.body p.read").text

   if soup.select_one("span.buy"):
       buy = soup.select_one("span.buy").extract().text
       #購入情報があれば抽出してテキスト化
   else:
       buy = "-" #購入情報がない場合はハイフン
       #購入情報がないことがあるため、ifで分岐
   
   if soup.select_one("span.repeat"):
       repeat = soup.select_one("span.repeat").extract().text
       #リピート情報があれば抽出してテキスト化
   else:
       repeat = "-"#購入情報がない場合はハイフン
       #リピート情報がないことがあるため、ifで分岐

   if soup.select_one("div.rating.clearfix p.reviewer-rating"):
       score = soup.select_one( \
	    "div.rating.clearfix p.reviewer-rating").extract().text
   else:
       score = "-"
	#スコア情報を取得。スコアなしのエラーを防ぐためif分岐

   if soup.select_one("dl.item-status.clearfix ul"):
       status = soup.select_one( \
	    "dl.item-status.clearfix ul").text
   else:
       status = "-"
	#購入品、サンプル品、プレゼント情報を取得。スコアなしのエラーを防ぐためif分岐

   if soup.select_one("div.rating.clearfix p.date"):
       date = soup.select_one("div.rating.clearfix p.date").text
   else:
       date = soup.select_one("div.rating.clearfix p.mobile-date").text
	#日付情報を取得

   reviewer_name = soup.select_one( \
	"div.reviewer-info span.reviewer-title").text
   #投稿者名を取得

   revewer_profs = soup.select("div.reviewer-info li")
   age = revewer_profs[0].text.replace("","")
   skin = revewer_profs[1].text
   #年齢と肌情報が並列で並んでいるため、リストとして入手して分割
   
   datalist = [date,reviewer_name,age,skin,score,buy,review,status,repeat]
   return datalist


#ここからメイン

code = input("クチコミを読みたい商品コードを入力してください")
start = dt.strptime(input("開始日時 yyyy/mm/dd hh:mm:ss"),"%Y/%m/%d %H:%M:%S")
end = dt.strptime(input("終了日時 yyyy/mm/dd hh:mm:ss"),"%Y/%m/%d %H:%M:%S")
file_name = input("口コミリストを保存するファイル名を入力してください")

product_url = "https://www.cosme.net/product/product_id/" + str(code) + "/reviews/p/"
page_count = 0 #アットコスメは0ページ目スタート
all_urls = [] #ループの外側にてURLsをリストと宣言
craw_frag = True

while craw_frag:
   print(f"{page_count + 1}ページ目") #進捗表示
   list_response = requests.get(product_url+str(page_count)).text
   soup = BeautifulSoup(list_response,'html.parser')
   dic = get_10reviews_list(soup) 
   
   for date in dic.keys():
       if start <= date <= end:
           all_urls.append(dic.get(date))

       elif date < start:
           craw_frag = False
           break
   #投稿日が設定範囲に入る場合、その口コミurlをall_urlsリストに蓄積。
   #アットコスメ口コミはデフォで新しい順に並ぶので収集開始日時よりも
   #古い口コミが出てきた時点で一覧のクローリングをストップする。

   if next_check(soup):
       page_count += 1
       time.sleep(3)
       continue
   else:
       craw_frag = False

print(f"一覧読み込み完了 レビュー{len(all_urls)}件 各レビュー読み込みを開始します")

rev_count = 1
columns = ["date","name","age","skin","score","buy","review","status","repeat",]
df = pd.DataFrame(columns=columns)
error_url_list = []

for url in all_urls:
   print(f"{rev_count}件目 / {len(all_urls)}件 / エラー{len(error_url_list)}")

   #口コミ取得の際にエラーが出るとこれまでの読み込みが止まってしまうので
   #回避のtryブロックを追加。その場合は結果のエクセルとリストに対象urlを吐き出す。
   try:
       se = pd.Series(get_review_data(url),columns).str.strip()
   except:
       error_message_dl = ["Error",f"No.{rev_count}","-","-","-","-",url]
       se = pd.Series(error_message_dl,columns).str.strip()
       error_url_list.append(url)
       
   df = df.append(se, columns)
   rev_count += 1
   time.sleep(3)

df.to_csv(file_name+".csv", index = False, encoding = "utf_8_sig")
files.download(file_name+".csv")
print("プログラムを終了します")

自分で試したこと

URLの様式が古かったのでその部分だけ修正したが、うまくできない。

0

2Answer

URLの様式が古かったのでその部分だけ修正したが、うまくできない。

上記はどういう意味でしょうか?ネットから拾ってきたコードを転用しているということでしょうか?そうであれば引用元を明記するべきではないでしょうか?

また「うまくできない」では状況が伝わりませんので具体的に説明いただけないでしょうか?(エラーが出るならエラーメッセージ、期待する結果と実際の結果 など)

1Like

Comments

  1. @Kandatsu

    Questioner

    megchandesu 様
    引用元を明記するべきでした。以後、注意します。

    引用元(https://note.com/trykid/n/n4febeee5ea30)

    image.png

    商品IDをcode = input()内に入力し、product_urlを現在の仕様(https://www.cosme.net/products/" + str(code) + "/review/?page=p)に置き換えて実行してみましたが、上の写真の様子で数十分以上変わらず、

    image.png

    実行を停止するとこのように出てきます。

  2. 引用元を明記するべきでした。以後、注意します。

    質問を編集して追記しましょう。

    実行を停止するとこのように出てきます。

    これは手動で止めたので出ているエラーですね。
    念のための確認ですが「商品IDをcode = input()内に入力し」た後はエンターキーを押してますよね?
    画像を見るとプログラム内で数値を出力していますがユーザー入力は何もされていないように見えます。

コードが作成された時から、ウェブサイトが変更され、htmlのタグが変わっているのだろうと思います。
BeautifulSoupは下記の記事が参考になるのではないでしょうか

またスクレイピングするサイトがどのような構造になっているか把握した上で収集する必要があるので、ブラウザでF12を押すなどして"デベロッパーツール"を表示し、"要素"から目的のタグがどのようになっているか確認してください。

image.png

0Like

Your answer might help someone💌