Need an Amazon Review Export Tool for Fast Scraping?
Looking for an easy Amazon review scraper? Export reviews to CSV, JSON, or Markdown with this lightweight web automation userscript.
Your browser can do more than just browsing. With the new Amazon Review Exporter Pro userscript, you can collect and export Amazon reviews in seconds using a clean floating interface directly inside the page.
This lightweight tool is built for fast data extraction, simple web scraping, and smooth web automation workflows. Instead of manually copying reviews one by one, the script exports review data into JSON, CSV, Markdown, or clipboard format with a single click.

The exporter captures useful details like reviewer name, rating, review text, verified purchase status, helpful votes, product variants, and review images. It works great for product research, competitor analysis, sentiment analysis, ecommerce datasets, and automation projects.
The interface is minimal, responsive, and designed to feel native on Amazon pages. Whether you are building scraping pipelines, analyzing customer feedback, or collecting datasets for Python automation, this userscript keeps the process fast and organized.
// ==UserScript==
// @name Amazon Review Exporter ✦ Pro
// @namespace https://github.com/review-exporter
// @version 2.0.0
// @description Export Amazon reviews as JSON, CSV, Markdown, or copy to clipboard with one click — sleek floating panel UI
// @author Review Exporter Pro
// @match https://www.amazon.com/*
// @match https://www.amazon.co.uk/*
// @match https://www.amazon.ca/*
// @match https://www.amazon.com.au/*
// @match https://www.amazon.de/*
// @match https://www.amazon.fr/*
// @match https://www.amazon.co.jp/*
// @match https://www.amazon.in/*
// @grant GM_download
// @grant GM_setClipboard
// @grant GM_addStyle
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
// ─── Inject Styles ──────────────────────────────────────────────────────────
GM_addStyle(`
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Syne:wght@600;700;800&display=swap');
#rex-launcher {
position: fixed;
bottom: 28px;
right: 28px;
z-index: 2147483647;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 12px;
font-family: 'Syne', sans-serif;
}
#rex-fab {
width: 56px;
height: 56px;
border-radius: 16px;
background: linear-gradient(135deg, #0f0f14 0%, #1a1a26 100%);
border: 1.5px solid rgba(255,255,255,0.12);
box-shadow: 0 8px 32px rgba(0,0,0,0.45), 0 0 0 0 rgba(124,90,255,0);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.25s cubic-bezier(0.34,1.56,0.64,1);
position: relative;
overflow: hidden;
}
#rex-fab::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, rgba(124,90,255,0.3), rgba(56,189,248,0.15));
opacity: 0;
transition: opacity 0.2s;
}
#rex-fab:hover {
transform: scale(1.1) translateY(-2px);
box-shadow: 0 12px 40px rgba(0,0,0,0.5), 0 0 0 4px rgba(124,90,255,0.2);
border-color: rgba(124,90,255,0.5);
}
#rex-fab:hover::before { opacity: 1; }
#rex-fab svg { position: relative; z-index: 1; }
#rex-badge {
position: absolute;
top: -5px;
right: -5px;
background: #7c5aff;
color: #fff;
font-family: 'DM Mono', monospace;
font-size: 10px;
font-weight: 500;
min-width: 18px;
height: 18px;
border-radius: 9px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 4px;
border: 2px solid #0f0f14;
transition: all 0.3s;
}
#rex-panel {
width: 340px;
background: #0f0f14;
border: 1.5px solid rgba(255,255,255,0.1);
border-radius: 20px;
box-shadow: 0 24px 80px rgba(0,0,0,0.6), 0 0 0 1px rgba(124,90,255,0.1);
overflow: hidden;
transform-origin: bottom right;
transform: scale(0.85) translateY(10px);
opacity: 0;
pointer-events: none;
transition: all 0.3s cubic-bezier(0.34,1.56,0.64,1);
}
#rex-panel.rex-open {
transform: scale(1) translateY(0);
opacity: 1;
pointer-events: all;
}
#rex-panel-header {
padding: 18px 20px 14px;
background: linear-gradient(135deg, rgba(124,90,255,0.12) 0%, rgba(56,189,248,0.06) 100%);
border-bottom: 1px solid rgba(255,255,255,0.07);
display: flex;
align-items: center;
justify-content: space-between;
}
#rex-panel-title {
font-family: 'Syne', sans-serif;
font-weight: 800;
font-size: 15px;
color: #fff;
letter-spacing: -0.3px;
display: flex;
align-items: center;
gap: 8px;
}
#rex-panel-title span.rex-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #7c5aff;
box-shadow: 0 0 8px rgba(124,90,255,0.8);
animation: rex-pulse 2s infinite;
}
@keyframes rex-pulse {
0%, 100% { box-shadow: 0 0 8px rgba(124,90,255,0.8); }
50% { box-shadow: 0 0 16px rgba(124,90,255,1); }
}
#rex-close-btn {
width: 28px;
height: 28px;
border-radius: 8px;
border: 1px solid rgba(255,255,255,0.1);
background: transparent;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255,255,255,0.5);
transition: all 0.2s;
}
#rex-close-btn:hover { background: rgba(255,255,255,0.08); color: #fff; }
#rex-stats {
padding: 14px 20px;
border-bottom: 1px solid rgba(255,255,255,0.06);
display: flex;
gap: 12px;
}
.rex-stat {
flex: 1;
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.07);
border-radius: 10px;
padding: 10px;
text-align: center;
}
.rex-stat-val {
font-family: 'Syne', sans-serif;
font-weight: 800;
font-size: 20px;
color: #7c5aff;
line-height: 1;
}
.rex-stat-lbl {
font-family: 'DM Mono', monospace;
font-size: 9px;
color: rgba(255,255,255,0.4);
text-transform: uppercase;
letter-spacing: 0.8px;
margin-top: 4px;
}
#rex-actions {
padding: 14px 20px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.rex-btn {
border-radius: 12px;
border: 1.5px solid rgba(255,255,255,0.09);
background: rgba(255,255,255,0.04);
color: #fff;
font-family: 'Syne', sans-serif;
font-weight: 700;
font-size: 12px;
letter-spacing: 0.2px;
padding: 12px 10px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 7px;
transition: all 0.2s;
position: relative;
overflow: hidden;
}
.rex-btn::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, var(--rex-accent), transparent);
opacity: 0;
transition: opacity 0.2s;
}
.rex-btn:hover {
border-color: var(--rex-accent);
transform: translateY(-1px);
box-shadow: 0 6px 20px rgba(0,0,0,0.3);
}
.rex-btn:hover::after { opacity: 0.12; }
.rex-btn:active { transform: translateY(0); }
.rex-btn.rex-full { grid-column: span 2; }
.rex-btn.rex-success {
border-color: #22c55e !important;
background: rgba(34,197,94,0.1) !important;
}
/* accent colors per button */
.rex-btn-json { --rex-accent: #f59e0b; }
.rex-btn-csv { --rex-accent: #22c55e; }
.rex-btn-md { --rex-accent: #38bdf8; }
.rex-btn-clip { --rex-accent: #7c5aff; }
#rex-footer {
padding: 10px 20px 14px;
border-top: 1px solid rgba(255,255,255,0.06);
font-family: 'DM Mono', monospace;
font-size: 9px;
color: rgba(255,255,255,0.2);
text-align: center;
letter-spacing: 0.5px;
}
#rex-toast {
position: fixed;
bottom: 100px;
right: 28px;
z-index: 2147483647;
background: #0f0f14;
border: 1.5px solid rgba(34,197,94,0.4);
border-radius: 12px;
padding: 10px 16px;
font-family: 'Syne', sans-serif;
font-size: 13px;
font-weight: 700;
color: #22c55e;
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
display: flex;
align-items: center;
gap: 8px;
transform: translateY(20px);
opacity: 0;
transition: all 0.4s cubic-bezier(0.34,1.56,0.64,1);
pointer-events: none;
}
#rex-toast.rex-show {
transform: translateY(0);
opacity: 1;
}
#rex-toast.rex-error {
border-color: rgba(239,68,68,0.4);
color: #ef4444;
}
`);
// ─── DOM Helpers ─────────────────────────────────────────────────────────────
function $(sel, ctx = document) { return ctx.querySelector(sel); }
function $$(sel, ctx = document) { return [...ctx.querySelectorAll(sel)]; }
// ─── Review Scraper ──────────────────────────────────────────────────────────
function scrapeReviews() {
const containers = $$('[data-hook="review"]');
return containers.map(el => {
const reviewId = el.closest('[data-reviewid]')?.dataset?.reviewid || el.id || '';
const asin = el.closest('[data-asin]')?.dataset?.asin || '';
const author = $('[class*="a-profile-name"]', el)?.textContent?.trim() || 'Anonymous';
const starEl = $('[data-hook="review-star-rating"] .a-icon-alt', el);
const rating = starEl ? parseFloat(starEl.textContent) : null;
const title = $('[data-hook="reviewTitle"]', el)?.textContent?.trim() || '';
const dateStr = $('[data-hook="review-date"]', el)?.textContent?.trim() || '';
const verified = !!$('[data-hook="avp-badge"]', el);
const bodyEl = $('[data-hook="reviewRichContentContainer"]', el) ||
$('[data-hook="review-collapsed"]', el) ||
$('[data-hook="review-body"]', el);
const body = bodyEl?.textContent?.trim().replace(/\s+/g, ' ') || '';
const variant = $('[data-hook="format-strip"]', el)?.textContent?.trim().replace(/\s+/g, ' ') || '';
const helpfulEl = $('[data-hook="helpful-vote-statement"]', el);
const helpful = helpfulEl?.textContent?.trim() || '0';
const images = $$('[data-hook="review-image-tile"] img, ._Y3Itd_media-popover-thumbnail-image-view_2J_oP', el)
.map(img => img.src).filter(Boolean);
return { reviewId, asin, author, rating, title, date: dateStr, verified, body, variant, helpful, images };
});
}
// ─── Export Formatters ────────────────────────────────────────────────────────
function toJSON(reviews) {
return JSON.stringify({ exportedAt: new Date().toISOString(), count: reviews.length, reviews }, null, 2);
}
function toCSV(reviews) {
const cols = ['reviewId','asin','author','rating','title','date','verified','body','variant','helpful'];
const esc = v => `"${String(v ?? '').replace(/"/g, '""')}"`;
const rows = reviews.map(r => cols.map(k => esc(r[k])).join(','));
return [cols.join(','), ...rows].join('\n');
}
function toMarkdown(reviews) {
const asin = reviews[0]?.asin || '';
let md = `# Amazon Reviews Export\n\n`;
md += `**Exported:** ${new Date().toLocaleString()} \n`;
md += `**Product ASIN:** \`${asin}\` \n`;
md += `**Total Reviews:** ${reviews.length}\n\n---\n\n`;
reviews.forEach((r, i) => {
const stars = '★'.repeat(r.rating || 0) + '☆'.repeat(5 - (r.rating || 0));
md += `## ${i + 1}. ${r.title || '(No Title)'}\n\n`;
md += `| Field | Value |\n|---|---|\n`;
md += `| **Author** | ${r.author} |\n`;
md += `| **Rating** | ${stars} (${r.rating}/5) |\n`;
md += `| **Date** | ${r.date} |\n`;
md += `| **Verified** | ${r.verified ? '✅ Yes' : '❌ No'} |\n`;
if (r.variant) md += `| **Variant** | ${r.variant} |\n`;
if (r.helpful && r.helpful !== '0') md += `| **Helpful** | ${r.helpful} |\n`;
md += `\n${r.body}\n\n`;
if (r.images?.length) {
md += `**Images:**\n`;
r.images.forEach((src, j) => md += `\n`);
md += '\n';
}
md += `---\n\n`;
});
return md;
}
function toClipText(reviews) {
return reviews.map((r, i) => {
const stars = '★'.repeat(r.rating || 0) + '☆'.repeat(5 - (r.rating || 0));
return [
`[${i+1}] ${r.title}`,
`${stars} ${r.author} · ${r.date}`,
r.verified ? '✅ Verified Purchase' : '',
r.variant ? `Variant: ${r.variant}` : '',
'',
r.body,
'─'.repeat(60)
].filter(l => l !== undefined).join('\n');
}).join('\n\n');
}
// ─── File Download (fallback for GM_download) ─────────────────────────────────
function downloadFile(content, filename, mime) {
try {
const blob = new Blob([content], { type: mime });
const url = URL.createObjectURL(blob);
GM_download({ url, name: filename, saveAs: true });
setTimeout(() => URL.revokeObjectURL(url), 5000);
} catch {
// fallback anchor
const a = document.createElement('a');
a.href = 'data:' + mime + ';charset=utf-8,' + encodeURIComponent(content);
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
}
}
// ─── Toast Notification ───────────────────────────────────────────────────────
let toastTimer;
function showToast(msg, isError = false) {
const t = document.getElementById('rex-toast');
if (!t) return;
t.innerHTML = isError
? `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg> ${msg}`
: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M20 6L9 17l-5-5"/></svg> ${msg}`;
t.classList.toggle('rex-error', isError);
t.classList.add('rex-show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => t.classList.remove('rex-show'), 2600);
}
// ─── Animate button success briefly ──────────────────────────────────────────
function flashBtn(btn, label) {
const orig = btn.innerHTML;
btn.innerHTML = `<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M20 6L9 17l-5-5"/></svg> Done!`;
btn.classList.add('rex-success');
setTimeout(() => { btn.innerHTML = orig; btn.classList.remove('rex-success'); }, 1800);
}
// ─── Timestamp for filenames ──────────────────────────────────────────────────
function ts() {
const d = new Date();
return `${d.getFullYear()}${String(d.getMonth()+1).padStart(2,'0')}${String(d.getDate()).padStart(2,'0')}_${String(d.getHours()).padStart(2,'0')}${String(d.getMinutes()).padStart(2,'0')}`;
}
// ─── Build UI ────────────────────────────────────────────────────────────────
function buildUI() {
const reviews = scrapeReviews();
const count = reviews.length;
const avgRating = count
? (reviews.reduce((s,r) => s + (r.rating||0), 0) / count).toFixed(1)
: '—';
const verifiedCount = reviews.filter(r => r.verified).length;
const asin = reviews[0]?.asin || 'unknown';
const launcher = document.createElement('div');
launcher.id = 'rex-launcher';
launcher.innerHTML = `
<!-- Panel -->
<div id="rex-panel">
<div id="rex-panel-header">
<div id="rex-panel-title">
<span class="rex-dot"></span>
Review Exporter Pro
</div>
<button id="rex-close-btn" title="Close">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
</div>
<div id="rex-stats">
<div class="rex-stat">
<div class="rex-stat-val">${count}</div>
<div class="rex-stat-lbl">Reviews</div>
</div>
<div class="rex-stat">
<div class="rex-stat-val">${avgRating}</div>
<div class="rex-stat-lbl">Avg Rating</div>
</div>
<div class="rex-stat">
<div class="rex-stat-val">${verifiedCount}</div>
<div class="rex-stat-lbl">Verified</div>
</div>
</div>
<div id="rex-actions">
<button class="rex-btn rex-btn-json" id="rex-export-json">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
Export JSON
</button>
<button class="rex-btn rex-btn-csv" id="rex-export-csv">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/></svg>
Export CSV
</button>
<button class="rex-btn rex-btn-md" id="rex-export-md">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M10 13l-2 2 2 2"/><path d="M14 13l2 2-2 2"/></svg>
Markdown
</button>
<button class="rex-btn rex-btn-clip" id="rex-export-clip">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><rect x="8" y="2" width="8" height="4" rx="1" ry="1"/></svg>
Copy All
</button>
</div>
<div id="rex-footer">ASIN: ${asin} · amazon-review-exporter v2.0</div>
</div>
<!-- FAB -->
<div id="rex-fab" title="Review Exporter Pro">
<span id="rex-badge">${count}</span>
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</div>
`;
// Toast element
const toast = document.createElement('div');
toast.id = 'rex-toast';
document.body.appendChild(toast);
document.body.appendChild(launcher);
// ── Event Listeners ──────────────────────────────────────────────────────
const panel = document.getElementById('rex-panel');
const fab = document.getElementById('rex-fab');
const close = document.getElementById('rex-close-btn');
fab.addEventListener('click', () => panel.classList.toggle('rex-open'));
close.addEventListener('click', () => panel.classList.remove('rex-open'));
document.getElementById('rex-export-json').addEventListener('click', function () {
if (!count) { showToast('No reviews found on this page.', true); return; }
downloadFile(toJSON(reviews), `amazon_reviews_${asin}_${ts()}.json`, 'application/json');
flashBtn(this);
showToast(`Exported ${count} reviews as JSON`);
});
document.getElementById('rex-export-csv').addEventListener('click', function () {
if (!count) { showToast('No reviews found on this page.', true); return; }
downloadFile(toCSV(reviews), `amazon_reviews_${asin}_${ts()}.csv`, 'text/csv');
flashBtn(this);
showToast(`Exported ${count} reviews as CSV`);
});
document.getElementById('rex-export-md').addEventListener('click', function () {
if (!count) { showToast('No reviews found on this page.', true); return; }
downloadFile(toMarkdown(reviews), `amazon_reviews_${asin}_${ts()}.md`, 'text/markdown');
flashBtn(this);
showToast(`Exported ${count} reviews as Markdown`);
});
document.getElementById('rex-export-clip').addEventListener('click', function () {
if (!count) { showToast('No reviews found on this page.', true); return; }
try {
GM_setClipboard(toClipText(reviews));
flashBtn(this);
showToast(`${count} reviews copied to clipboard!`);
} catch {
navigator.clipboard.writeText(toClipText(reviews)).then(() => {
flashBtn(this);
showToast(`${count} reviews copied to clipboard!`);
}).catch(() => showToast('Clipboard access denied.', true));
}
});
}
// ─── Init ─────────────────────────────────────────────────────────────────────
function init() {
// Only show on review pages or product pages with reviews
const hasReviews = document.querySelector('[data-hook="review"]') ||
document.querySelector('#cm-cr-dp-review-list') ||
document.querySelector('#customerReviews');
if (!hasReviews) return;
if (document.getElementById('rex-launcher')) return; // already injected
buildUI();
}
// Run after page settles (handles SPA navigation too)
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => setTimeout(init, 1200));
} else {
setTimeout(init, 1200);
}
// Re-init on Amazon's SPA navigation
const _pushState = history.pushState;
history.pushState = function (...args) {
_pushState.apply(history, args);
setTimeout(() => { document.getElementById('rex-launcher')?.remove(); document.getElementById('rex-toast')?.remove(); init(); }, 1500);
};
window.addEventListener('popstate', () => {
setTimeout(() => { document.getElementById('rex-launcher')?.remove(); document.getElementById('rex-toast')?.remove(); init(); }, 1500);
});
})();