なぜスケールで製品のレビューをScrape?
製品レビューは、電子商取引における最も貴重なデータソースの一つです。 顧客の感情、製品品質の問題、機能要求、競争力のある位置を明らかにし、他のデータソースが提供できない情報。 スケールで、レビューデータを有効化:
- センチメント分析: お客様が製品や競合他社の製品について、時間をかけてどのように感じているかを追跡します。
- プロダクト開発: クレームや機能リクエストを数千件のレビューで識別します。
- 競争力のあるインテリジェンス: 競合他社の強みや弱みをお客様自身の言葉から把握します。
- 市場調査: さまざまなカテゴリーでレビューパターンを分析することで、不要なニーズや新しいトレンドを発見。
- 質の監視: 製品の品質問題を早期に検出し、レビューの送信傾向を監視します。
課題は、データが複数のプラットフォーム(Amazon、Walmart、Best Buy、Trustpilot、Google)に分散し、それぞれ異なる構造とアンチボット保護を持つことです。 スケールでのスクレイピングレビューには、プラットフォーム固有の戦略と堅牢なプロキシインフラストラクチャが必要です。 基礎的なeコマーススクレイピングパターンについては、こちらをご覧ください eコマースデータスクレイピングガイド. .
プラットフォーム間でのデータ構造を確認する
| プラットフォーム | レビューフィールド | パジネーション | アンチボットレベル |
|---|---|---|---|
| アマゾン | 評価、タイトル、テキスト、日付、検証、有用な投票 | ページベース(10/ページ) | 高い |
| ウォルマート | 評価、タイトル、テキスト、日付、投稿ソース | オフセットベース API | メディア |
| 最高の購入 | 評価、タイトル、テキスト、日付、有用/非役立つ | ページベースAPI | メディア |
| トラストパイロット | 評価、タイトル、テキスト、日付、応答 | ページベース | 低媒体 |
| Googleショッピング | 評価、テキスト、日付、ソース | スクロールベース | 高い |
レビューのスクレイピングのためのプロキシ構成
スクラップレビューは、複数のリクエスト間でセッションを維持することを意味し、 paginatedナビゲーションを含みます。 ProxyHatのスティッキーセッションは、このパターンに理想的です。
ProxyHat セットアップ
# Per-request rotation for initial product lookups
http://USERNAME:PASSWORD@gate.proxyhat.com:8080
# Sticky session for paginating through reviews of one product
http://USERNAME-session-rev001:PASSWORD@gate.proxyhat.com:8080
# Geo-targeted for region-specific review pages
http://USERNAME-country-US:PASSWORD@gate.proxyhat.com:8080レビューのスクレイピングのために、粘りのあるセッションを使用して、すべてのレビューを1つの製品にペギンティングし、異なる製品間で移動するときに1回の探求回転を使用します。 この mimics は、次の製品に移動する前に、ユーザーが複数のレビューを 1 つの製品に読む自然な閲覧行動を模倣します。
Pythonの実装
ここでは、マルチプラットフォームのレビュースクレーパーを使用して ProxyHatのPython SDK. .
Amazon レビュー スクレーパー
import requests
from bs4 import BeautifulSoup
import json
import time
import random
from dataclasses import dataclass
from datetime import datetime
PROXY_URL = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
]
@dataclass
class Review:
platform: str
product_id: str
rating: float
title: str
text: str
date: str
author: str
verified: bool
helpful_votes: int
def scrape_amazon_reviews(asin, max_pages=10):
"""Scrape all reviews for an Amazon product."""
reviews = []
session_id = f"rev-{asin}-{random.randint(1000, 9999)}"
proxy = f"http://USERNAME-session-{session_id}:PASSWORD@gate.proxyhat.com:8080"
session = requests.Session()
session.proxies = {"http": proxy, "https": proxy}
session.headers.update({
"User-Agent": random.choice(USER_AGENTS),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
})
for page in range(1, max_pages + 1):
url = (f"https://www.amazon.com/product-reviews/{asin}"
f"?pageNumber={page}&sortBy=recent")
try:
response = session.get(url, timeout=30)
if response.status_code != 200:
break
if "captcha" in response.text.lower():
print(f"CAPTCHA on page {page}, switching session")
break
soup = BeautifulSoup(response.text, "html.parser")
review_divs = soup.find_all("div", {"data-hook": "review"})
if not review_divs:
break
for div in review_divs:
review = parse_amazon_review(div, asin)
if review:
reviews.append(review)
print(f"Page {page}: {len(review_divs)} reviews (total: {len(reviews)})")
time.sleep(random.uniform(2, 5))
except requests.RequestException as e:
print(f"Error on page {page}: {e}")
break
return reviews
def parse_amazon_review(div, asin):
"""Parse a single Amazon review element."""
try:
rating_el = div.find("i", {"data-hook": "review-star-rating"})
rating = float(rating_el.get_text().split(" ")[0]) if rating_el else None
title_el = div.find("a", {"data-hook": "review-title"})
title = title_el.get_text(strip=True) if title_el else ""
body_el = div.find("span", {"data-hook": "review-body"})
text = body_el.get_text(strip=True) if body_el else ""
date_el = div.find("span", {"data-hook": "review-date"})
date_str = date_el.get_text(strip=True) if date_el else ""
author_el = div.find("span", {"class": "a-profile-name"})
author = author_el.get_text(strip=True) if author_el else ""
verified = bool(div.find("span", {"data-hook": "avp-badge"}))
helpful_el = div.find("span", {"data-hook": "helpful-vote-statement"})
helpful = 0
if helpful_el:
text_h = helpful_el.get_text()
if "one" in text_h.lower():
helpful = 1
else:
nums = [int(s) for s in text_h.split() if s.isdigit()]
helpful = nums[0] if nums else 0
return Review(
platform="amazon",
product_id=asin,
rating=rating,
title=title,
text=text,
date=date_str,
author=author,
verified=verified,
helpful_votes=helpful,
)
except Exception:
return Noneマルチプラットフォームレビューコレクター
class ReviewCollector:
"""Collect reviews from multiple platforms for a product."""
def __init__(self):
self.scrapers = {
"amazon": scrape_amazon_reviews,
}
def collect_all(self, product_ids: dict) -> list[Review]:
"""
Collect reviews from all platforms.
product_ids: {"amazon": "B0CHX3QBCH", "walmart": "12345"}
"""
all_reviews = []
for platform, product_id in product_ids.items():
if platform in self.scrapers:
print(f"\nScraping {platform} reviews for {product_id}")
reviews = self.scrapers[platform](product_id)
all_reviews.extend(reviews)
print(f"Collected {len(reviews)} reviews from {platform}")
time.sleep(random.uniform(5, 10))
return all_reviews
def to_dataframe(self, reviews: list[Review]):
"""Convert reviews to a pandas DataFrame for analysis."""
import pandas as pd
return pd.DataFrame([vars(r) for r in reviews])
# Usage
collector = ReviewCollector()
reviews = collector.collect_all({
"amazon": "B0CHX3QBCH",
})
print(f"\nTotal reviews collected: {len(reviews)}")Node.js 実装
Node.jsレビュースクレーパーを使用して ProxyHat ノード SDK. .
const axios = require("axios");
const cheerio = require("cheerio");
const { HttpsProxyAgent } = require("https-proxy-agent");
function getProxy(sessionId = null) {
if (sessionId) {
return `http://USERNAME-session-${sessionId}:PASSWORD@gate.proxyhat.com:8080`;
}
return "http://USERNAME:PASSWORD@gate.proxyhat.com:8080";
}
async function scrapeAmazonReviews(asin, maxPages = 10) {
const reviews = [];
const sessionId = `rev-${asin}-${Math.floor(Math.random() * 9000 + 1000)}`;
const agent = new HttpsProxyAgent(getProxy(sessionId));
for (let page = 1; page <= maxPages; page++) {
const url = `https://www.amazon.com/product-reviews/${asin}?pageNumber=${page}&sortBy=recent`;
try {
const { data } = await axios.get(url, {
httpsAgent: agent,
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
},
timeout: 30000,
});
if (data.toLowerCase().includes("captcha")) {
console.log(`CAPTCHA on page ${page}`);
break;
}
const $ = cheerio.load(data);
const reviewDivs = $('[data-hook="review"]');
if (reviewDivs.length === 0) break;
reviewDivs.each((_, el) => {
const $el = $(el);
const ratingText = $el.find('[data-hook="review-star-rating"]').text();
const rating = parseFloat(ratingText.split(" ")[0]) || null;
reviews.push({
platform: "amazon",
productId: asin,
rating,
title: $el.find('[data-hook="review-title"]').text().trim(),
text: $el.find('[data-hook="review-body"]').text().trim(),
date: $el.find('[data-hook="review-date"]').text().trim(),
author: $el.find(".a-profile-name").text().trim(),
verified: $el.find('[data-hook="avp-badge"]').length > 0,
});
});
console.log(`Page ${page}: ${reviewDivs.length} reviews (total: ${reviews.length})`);
await new Promise((r) => setTimeout(r, 2000 + Math.random() * 3000));
} catch (err) {
console.error(`Error page ${page}: ${err.message}`);
break;
}
}
return reviews;
}
// Usage
scrapeAmazonReviews("B0CHX3QBCH", 5).then((reviews) => {
console.log(`Collected ${reviews.length} reviews`);
console.log(JSON.stringify(reviews.slice(0, 2), null, 2));
});スケールでのパジネーション処理
大規模なレビュースクレイピングで最大の課題の1つです。
Amazonパジネーション戦略
Amazonでは、レビューページを10レビューに制限し、通常最大500ページ(5,000レビュー)が表示されます。 より多くのレビューを持つ製品には、フィルタパラメータを使用してセグメントに使用します。
# Filter by star rating to get more reviews
star_filters = [
"one_star", "two_star", "three_star",
"four_star", "five_star"
]
for star in star_filters:
url = (f"https://www.amazon.com/product-reviews/{asin}"
f"?filterByStar={star}&pageNumber={page}")
# This lets you access more reviews per productパジネーションセッション管理
各製品のレビューペジネーションは、独自のスティッキーセッションを使用する必要があります。 1つの製品を終了し、次に移動すると、異なるIPで新しいセッションを作成します。
| フェーズ | プロキシ戦略 | レイソン |
|---|---|---|
| 製品を探す | パーリクエスト回転 | 独立したルックアップ、セッション不要 |
| レビューの開始 | 製品の粘着セッション | ページ全体で同じIPが自然に見える |
| 製品について | 新規セッション/IP | 各製品の新鮮な身元 |
センチメント分析のためのデータの準備
生の見直しのテキストは、感情解析の前に、前処理が必要です。
import re
from collections import Counter
def clean_review_text(text):
"""Clean review text for analysis."""
# Remove HTML entities
text = re.sub(r'&\w+;', ' ', text)
# Remove excessive whitespace
text = re.sub(r'\s+', ' ', text).strip()
# Remove very short reviews (likely not useful)
if len(text) < 20:
return None
return text
def extract_key_phrases(reviews, min_frequency=3):
"""Extract frequently mentioned phrases from reviews."""
from collections import Counter
import re
words = []
for review in reviews:
if review.text:
# Simple bigram extraction
tokens = re.findall(r'\b\w+\b', review.text.lower())
for i in range(len(tokens) - 1):
bigram = f"{tokens[i]} {tokens[i+1]}"
words.append(bigram)
return Counter(words).most_common(50)
def aggregate_sentiment(reviews):
"""Calculate aggregate sentiment metrics."""
if not reviews:
return {}
ratings = [r.rating for r in reviews if r.rating]
return {
"total_reviews": len(reviews),
"avg_rating": sum(ratings) / len(ratings) if ratings else 0,
"rating_distribution": {
str(i): len([r for r in reviews if r.rating == i])
for i in range(1, 6)
},
"verified_pct": (
len([r for r in reviews if r.verified]) / len(reviews) * 100
if reviews else 0
),
}何百万人もの口コミをスケーリング
ターゲットリストが複数のプラットフォーム間で何千もの製品に成長する場合、アーキテクチャの問題。
キューベースアーキテクチャ
- メッセージキュー (Redis, RabbitMQ) を使用して、製品リストを管理し、労働者間で作業を配布します。
- 各ワーカーは、1つの製品を一度に処理します。すべてのレビュー、ストア結果を通して、次の製品に移動します。
- 異なるレートの制限を尊重するプラットフォームごとのキューを分離します。
ストレージ戦略
- パーサが変更したときに、オブジェクトストレージ(S3)で未加工HTMLを保存します。
- PostgreSQL で解析のための全文検索で解析されたレビューを保存します。
- レビューIDまたはハッシュに基づいて重複排除を使用して、再処理に重複を保存しないようにします。
イントレメンタルスクレイピング
継続的な監視では、毎回すべてのレビューを再処理する必要はありません。 あなたがすでに収集したレビューに当たると、最新かつ停止でソートします。 これにより、プロキシの使用量を劇的に削減し、コレクションをスピードアップ。
キーテイクアウト: 以前に収集したコンテンツをヒットしたときに、最新の最初からレビューをソートし、スクレイピングを停止します。 これは、増分の更新に完全に再処理されます。
ベストプラクティス
- pagination 用のスティッキーセッションを使用する: アンチボット検出のトリガーを避けるために、レビューページ全体で同じIPを維持します。
- レートの限界を点検して下さい: 2-5 ページ間の秒間遅延、製品間の長い遅延。 異なるプラットフォームは異なる許容値を持っています。
- 空のページを処理する: 空のレビューページは、最後に到達したことを意味します。 より多くのページを試し続けないでください。
- 有効なデータ品質: CAPTCHAページ、空のコンテンツ、およびパイプラインでのレビューを複製するためのチェック。
- 使用条件 住宅のプロキシ: : : Amazonなどの保護されたプラットフォームに不可欠です。
- 増分保存: 処理し、最後に1つのバッチではなく、それらを掻くようにレビューを保存します。
キーテイクアウト
- 他のデータソースが提供していない独自の競争力のあるインテリジェンスを提供します。
- 異なるプラットフォームは、プラットフォームごとにモジュラースクレーパーを構築し、異なるスクレイピング戦略を必要とします。
- 製品間のペジネーションとパーリクエストの回転をレビューするために、スティッキーセッションを使用します。
- 新しく最初にソートし、以前に収集したレビューでストップし、効率的なインクリメンタルスクレイピングを実現します。
- 送信分析のための事前プロセスレビューテキスト:きれいで、重複し、キーフレーズを抽出します。
- 使用条件 ProxyHatの住宅用プロキシ ジオターゲティングで、すべてのプラットフォーム間でページをレビューする信頼性の高いアクセスを実現します。
レビューデータを収集する準備はできましたか? お問い合わせ Amazonスクレイピングガイド プラットフォーム固有の詳細と当社の eコマースデータスクレイピングガイド 完全な戦略のため。 チェックイン Pythonでプロキシを使う そして、 Node.js でプロキシを使用する 実装パターンのため。






