为什么在规模上进行产品审查?
产品审查是电子商务中最有价值的数据来源之一。 它们揭示了客户情绪、产品质量问题、特征要求和竞争性定位——其他数据来源无法提供的信息。 在规模上,审查数据使:
- 感知分析: 追踪顾客对您产品和竞争对手产品 的感受
- 产品开发: 查明成千上万次审查中反复提出的投诉和专题请求。
- 竞争性情报: 从顾客的言语中了解竞争对手的优缺点.
- 市场研究: 通过分析各类审查模式,发现未得到满足的需求和新出现的趋势。
- 质量监测: 通过监测审查情绪趋势,及早发现产品质量问题。
挑战在于审查数据分布在多个平台(亚马逊,沃尔玛,Best Buy,Trustpilot,Google),每个平台具有不同的结构和反机器人保护. 大规模审查需要针对具体平台的战略和强有力的代理基础设施。 关于基本的电子商务刮刮模式,见我们 电子商务数据报废指南。 。 。
审查跨平台的数据结构
| 平台 | 审查领域 | 倾斜 | 反毒级 |
|---|---|---|---|
| 亚马逊 | 评分、标题、文本、日期、核实、有帮助的表决 | 基于页面(10页) | 高级 |
| 沃尔玛 | 评级、标题、文本、日期、提交来源 | 基于偏移的 API | 中型 |
| 最佳买家 | 评级、标题、文本、日期、有用/无用 | 基于页面的 API | 中型 |
| 信任驾驶员 | 评分、标题、文本、日期、答复 | 基于页面 | 低 |
| 谷歌购物 | 评分、文本、日期、来源 | 基于滚动 | 高级 |
审查抓取的代理配置
审查刮刮涉及斜拉式导航,这意味着在多个请求之间保持会话。 代理Hat的粘性会话是这种模式的理想.
代理服务器设置
# 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在审查刮刮时,在对单一产品进行所有审查时使用粘性会议,在不同产品之间移动时使用按要求轮换。 这模仿了自然浏览行为,一个用户在移动到下一个产品之前会读取一个产品的多个评论页.
Python 执行
这里有一个多平台的审查刮刮机 代理哈特的 Python SDK。 。 。
亚马逊评论
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)}")节点.js 执行
节点J审查刮刮机 代理哈特节点 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));
});缩放处理
检讨降级是大规模检讨刮片中最大的挑战之一.
亚马逊定居战略
亚马逊将评论页数限制在每篇10篇评论,一般显示多达500页(5000篇评论). 对于有更多评论的产品,使用过滤参数进行片段:
# 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插位会话管理
每种产品的审查标注应使用自己的粘度段. 当完成一个产品并移动到下一个产品时,用不同的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)来管理产品列表,并在工人之间分配工作.
- 每个工人一次处理一种产品:通过所有审查粘贴,存储结果,转移到下一个产品.
- 每个平台分别排队,以遵守不同的速率限制.
储存战略
- 当解析器改变时,将原始 HTML 存储在对象存储(S3)中进行后处理.
- 在 PostgreSQL 中存储解析评论,并进行全文搜索以进行分析.
- 使用基于审查ID或散列的调值来避免重复存储在重排上.
递增划线
对于持续的监控,你不需要每次重复所有的评论. 按最近的情况排序, 并在您已经收集到的评论中停止 。 这极大地减少了代理用户,加速了收藏.
密钥外卖:先按最新评论排序, 并当您击中以前收集的内容时停止刮刮 。 这使得一个完全的重击变成了递增更新.
最佳做法
- 使用粘贴会话进行页标 : 为单一产品保持相同的IP跨审查页面,以避免触发反机器人检测.
- 尊重率限制 : 2-5秒的两页间延迟,产品间延迟的时间更长. 不同的平台具有不同的容忍度.
- 处理空页 : 空审查页表示您已到达尾端 。 不要继续尝试更多的页面。
- 验证数据质量: 检查 CAPTCHA 的页面, 空的内容, 并重复您的管道中的审查 。
- 使用 住宅代办编号 : 对亚马逊和其他高度保护的平台至关重要。
- 渐进存储 : 处理和存储评论 当您刮它们, 而不是一个批次在结尾。
关键外卖
- 审查数据提供了其他数据来源无法提供的独特竞争情报。
- 不同的平台需要不同的刮刮策略——每个平台建造模块式刮刮器.
- 使用粘贴会话进行审查,在产品之间进行按要求旋转。
- 先按最新数据排序,然后在以前收集的审查中止步,以便有效地逐步报废。
- 情绪分析的预处理审查文本:干净,去重复,并提取关键短语.
- 使用 代理哈特的住宅代理 并设定地理目标,以便可靠地访问所有平台的审查网页。
准备好开始收集审查数据了吗? 看我们的 亚马逊刮纸指南 具体平台和我们 电子商务数据报废指南 为整个战略。 检查 使用 Python 中的代理 和 使用节点中的代理 执行模式。






