mapsscrapper / index.html
Bencolliss's picture
how do i increase the rate limit - Initial Deployment
0494b46 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ethical Google Maps Data Extractor</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.progress-bar {
transition: width 0.3s ease;
}
.fade-in {
animation: fadeIn 0.5s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.scrollable-container {
scrollbar-width: thin;
scrollbar-color: #3b82f6 #e5e7eb;
}
.scrollable-container::-webkit-scrollbar {
width: 6px;
}
.scrollable-container::-webkit-scrollbar-track {
background: #e5e7eb;
}
.scrollable-container::-webkit-scrollbar-thumb {
background-color: #3b82f6;
border-radius: 3px;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<header class="mb-8">
<div class="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
<div>
<h1 class="text-3xl font-bold text-gray-800">Ethical Maps Extractor</h1>
<p class="text-gray-600">Responsible data extraction from Google Maps</p>
</div>
<div class="flex items-center gap-2 bg-blue-50 px-4 py-2 rounded-lg">
<i class="fas fa-shield-alt text-blue-500"></i>
<span class="text-sm font-medium text-blue-700">Ethical Mode: Active</span>
</div>
</div>
</header>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- Left Panel - Configuration -->
<div class="lg:col-span-1 bg-white rounded-xl shadow-md p-6 h-fit">
<h2 class="text-xl font-semibold text-gray-800 mb-4">Extraction Settings</h2>
<div class="space-y-5">
<!-- Search Term -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Search Term</label>
<div class="relative">
<input type="text" id="searchTerm" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="e.g. Restaurants in New York">
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<i class="fas fa-search text-gray-400"></i>
</div>
</div>
</div>
<!-- Location -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Location</label>
<div class="relative">
<input type="text" id="location" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="City, State or Country">
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<i class="fas fa-map-marker-alt text-gray-400"></i>
</div>
</div>
</div>
<!-- Google Maps API Key -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Google Maps API Key</label>
<div class="relative">
<input type="text" id="apiKey" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="Your Google Maps API Key">
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<i class="fas fa-key text-gray-400"></i>
</div>
</div>
<p class="text-xs text-gray-500 mt-1">Required for Google Maps API access</p>
</div>
<!-- Result Limit -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Result Limit</label>
<div class="flex items-center gap-2">
<input type="range" id="resultLimit" min="1" max="1000" value="10" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
<span id="limitValue" class="text-sm font-medium text-gray-700 w-10 text-center">10</span>
</div>
<p class="text-xs text-gray-500 mt-1">Ethical limit: 1000 results per day</p>
</div>
<!-- Extraction Depth -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Data Depth</label>
<div class="grid grid-cols-2 gap-2">
<div>
<input type="checkbox" id="basicInfo" class="hidden peer" checked>
<label for="basicInfo" class="flex items-center justify-between p-2 border border-gray-300 rounded-lg cursor-pointer peer-checked:border-blue-500 peer-checked:bg-blue-50">
<span class="text-sm text-gray-700">Basic Info</span>
<i class="fas fa-check-circle text-blue-500"></i>
</label>
</div>
<div>
<input type="checkbox" id="contactInfo" class="hidden peer" checked>
<label for="contactInfo" class="flex items-center justify-between p-2 border border-gray-300 rounded-lg cursor-pointer peer-checked:border-blue-500 peer-checked:bg-blue-50">
<span class="text-sm text-gray-700">Contact Info</span>
<i class="fas fa-check-circle text-blue-500"></i>
</label>
</div>
<div>
<input type="checkbox" id="reviews" class="hidden peer">
<label for="reviews" class="flex items-center justify-between p-2 border border-gray-300 rounded-lg cursor-pointer peer-checked:border-blue-500 peer-checked:bg-blue-50">
<span class="text-sm text-gray-700">Reviews</span>
<i class="fas fa-check-circle text-blue-500"></i>
</label>
</div>
<div>
<input type="checkbox" id="hours" class="hidden peer">
<label for="hours" class="flex items-center justify-between p-2 border border-gray-300 rounded-lg cursor-pointer peer-checked:border-blue-500 peer-checked:bg-blue-50">
<span class="text-sm text-gray-700">Hours</span>
<i class="fas fa-check-circle text-blue-500"></i>
</label>
</div>
</div>
</div>
<!-- Rate Limiting -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Request Delay</label>
<select id="requestDelay" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="5">5 seconds (recommended)</option>
<option value="7">7 seconds (safer)</option>
<option value="10">10 seconds (most reliable)</option>
</select>
</div>
<!-- Start Button -->
<button id="startExtraction" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition duration-200 flex items-center justify-center gap-2">
<i class="fas fa-play"></i>
Start Extraction
</button>
<!-- Ethical Notice -->
<div class="p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
<div class="flex items-start gap-2">
<i class="fas fa-exclamation-triangle text-yellow-500 mt-0.5"></i>
<div>
<p class="text-sm text-yellow-800 font-medium">Ethical Notice</p>
<p class="text-xs text-yellow-700">This tool follows strict ethical guidelines. Usage is limited to 50 results per day to respect Google's services.</p>
</div>
</div>
</div>
</div>
</div>
<!-- Right Panel - Results and Logs -->
<div class="lg:col-span-2 space-y-6">
<!-- Progress Card -->
<div id="progressCard" class="bg-white rounded-xl shadow-md p-6 hidden">
<div class="flex justify-between items-center mb-3">
<h2 class="text-lg font-semibold text-gray-800">Extraction Progress</h2>
<div class="flex items-center gap-2">
<span id="progressText" class="text-sm font-medium text-gray-700">0%</span>
<button id="pauseExtraction" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-pause"></i>
</button>
</div>
</div>
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div id="progressBar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div>
</div>
<div class="mt-4 grid grid-cols-3 gap-4 text-center">
<div class="bg-blue-50 p-2 rounded-lg">
<p class="text-xs text-blue-700 font-medium">Extracted</p>
<p id="extractedCount" class="text-lg font-bold text-blue-800">0</p>
</div>
<div class="bg-gray-50 p-2 rounded-lg">
<p class="text-xs text-gray-700 font-medium">Remaining</p>
<p id="remainingCount" class="text-lg font-bold text-gray-800">0</p>
</div>
<div class="bg-green-50 p-2 rounded-lg">
<p class="text-xs text-green-700 font-medium">Success Rate</p>
<p id="successRate" class="text-lg font-bold text-green-800">100%</p>
</div>
</div>
<div class="mt-4">
<div class="flex items-center justify-between mb-2">
<span class="text-sm font-medium text-gray-700">Estimated Time Remaining</span>
<span id="timeRemaining" class="text-sm font-medium text-gray-700">Calculating...</span>
</div>
</div>
</div>
<!-- Results Card -->
<div class="bg-white rounded-xl shadow-md overflow-hidden">
<div class="border-b border-gray-200 px-6 py-4">
<div class="flex justify-between items-center">
<h2 class="text-lg font-semibold text-gray-800">Extracted Data</h2>
<div class="flex gap-2">
<button id="exportCSV" class="text-sm bg-green-50 hover:bg-green-100 text-green-700 font-medium py-1 px-3 rounded-lg flex items-center gap-1">
<i class="fas fa-file-csv"></i>
Export CSV
</button>
<button id="clearResults" class="text-sm bg-gray-50 hover:bg-gray-100 text-gray-700 font-medium py-1 px-3 rounded-lg flex items-center gap-1">
<i class="fas fa-trash-alt"></i>
Clear
</button>
</div>
</div>
</div>
<div class="scrollable-container max-h-96 overflow-y-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Address</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Phone</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Rating</th>
</tr>
</thead>
<tbody id="resultsTable" class="bg-white divide-y divide-gray-200">
<!-- Results will be inserted here -->
<tr class="text-center py-4">
<td colspan="4" class="px-6 py-12 text-gray-500">
<i class="fas fa-database text-4xl text-gray-300 mb-3"></i>
<p>No data extracted yet</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Logs Card -->
<div class="bg-white rounded-xl shadow-md overflow-hidden">
<div class="border-b border-gray-200 px-6 py-4">
<div class="flex justify-between items-center">
<h2 class="text-lg font-semibold text-gray-800">Extraction Logs</h2>
<div class="flex gap-2">
<button id="toggleLogs" class="text-sm bg-gray-50 hover:bg-gray-100 text-gray-700 font-medium py-1 px-3 rounded-lg flex items-center gap-1">
<i class="fas fa-chevron-up"></i>
Collapse
</button>
</div>
</div>
</div>
<div id="logsContainer" class="scrollable-container max-h-60 overflow-y-auto px-6 py-4 space-y-2">
<div class="text-sm text-gray-500 italic">Waiting for extraction to begin...</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal for Ethical Agreement -->
<div id="ethicalModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 hidden">
<div class="bg-white rounded-xl shadow-xl max-w-md w-full">
<div class="p-6">
<div class="flex items-center gap-3 mb-4">
<i class="fas fa-shield-alt text-2xl text-blue-500"></i>
<h3 class="text-xl font-bold text-gray-800">Ethical Usage Agreement</h3>
</div>
<div class="mb-6 text-sm text-gray-600 space-y-3">
<p>This tool is designed for ethical data extraction from Google Maps with strict limitations:</p>
<ul class="list-disc pl-5 space-y-1">
<li>Maximum 50 results per day</li>
<li>Minimum 5-second delay between requests</li>
<li>No bypassing of CAPTCHAs or other security measures</li>
<li>Data must not be used for spamming or harassment</li>
</ul>
<p class="mt-3 font-medium">By using this tool, you agree to these terms and Google's Terms of Service.</p>
</div>
<div class="flex justify-end gap-3">
<button id="declineAgreement" class="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 font-medium hover:bg-gray-50">Decline</button>
<button id="acceptAgreement" class="px-4 py-2 bg-blue-600 rounded-lg text-white font-medium hover:bg-blue-700">I Accept</button>
</div>
</div>
</div>
</div>
<script>
// DOM Elements
const searchTermInput = document.getElementById('searchTerm');
const locationInput = document.getElementById('location');
const resultLimitInput = document.getElementById('resultLimit');
const limitValue = document.getElementById('limitValue');
const startExtractionBtn = document.getElementById('startExtraction');
const progressCard = document.getElementById('progressCard');
const progressBar = document.getElementById('progressBar');
const progressText = document.getElementById('progressText');
const extractedCount = document.getElementById('extractedCount');
const remainingCount = document.getElementById('remainingCount');
const successRate = document.getElementById('successRate');
const timeRemaining = document.getElementById('timeRemaining');
const resultsTable = document.getElementById('resultsTable');
const logsContainer = document.getElementById('logsContainer');
const exportCSVBtn = document.getElementById('exportCSV');
const clearResultsBtn = document.getElementById('clearResults');
const toggleLogsBtn = document.getElementById('toggleLogs');
const logsContainerDiv = document.getElementById('logsContainer');
const ethicalModal = document.getElementById('ethicalModal');
const acceptAgreementBtn = document.getElementById('acceptAgreement');
const declineAgreementBtn = document.getElementById('declineAgreement');
const pauseExtractionBtn = document.getElementById('pauseExtraction');
// Variables
let isExtracting = false;
let isPaused = false;
let extractedItems = 0;
let totalItems = 0;
let successfulExtractions = 0;
let extractionStartTime = null;
let extractionInterval = null;
// Event Listeners
resultLimitInput.addEventListener('input', updateLimitValue);
startExtractionBtn.addEventListener('click', startExtraction);
exportCSVBtn.addEventListener('click', exportToCSV);
clearResultsBtn.addEventListener('click', clearResults);
toggleLogsBtn.addEventListener('click', toggleLogs);
acceptAgreementBtn.addEventListener('click', acceptAgreement);
declineAgreementBtn.addEventListener('click', declineAgreement);
pauseExtractionBtn.addEventListener('click', togglePause);
// Functions
function updateLimitValue() {
limitValue.textContent = resultLimitInput.value;
}
function startExtraction() {
const apiKey = document.getElementById('apiKey').value.trim();
const searchTerm = searchTermInput.value.trim();
const location = locationInput.value.trim();
const limit = parseInt(resultLimitInput.value);
if (!apiKey) {
addLog("AIzaSyDQAh_kANmTKL5pm9q4MDFfe4cKvEGffrM", "error");
document.getElementById('AIzaSyDQAh_kANmTKL5pm9q4MDFfe4cKvEGffrM').focus();
return;
}
if (!searchTerm) {
addLog("Please enter a search term", "error");
searchTermInput.focus();
return;
}
if (limit > 1000) {
addLog("Ethical limit is 1000 results per day", "error");
return;
}
// Show ethical agreement modal
ethicalModal.classList.remove('hidden');
}
function acceptAgreement() {
ethicalModal.classList.add('hidden');
beginExtraction();
}
function declineAgreement() {
ethicalModal.classList.add('hidden');
addLog("Extraction canceled - you must accept the ethical agreement", "error");
}
function beginExtraction() {
const searchTerm = searchTermInput.value.trim();
const location = locationInput.value.trim();
const limit = parseInt(resultLimitInput.value);
const delay = parseInt(document.getElementById('requestDelay').value);
// Reset variables
extractedItems = 0;
totalItems = limit;
successfulExtractions = 0;
extractionStartTime = new Date();
isExtracting = true;
isPaused = false;
// Update UI
startExtractionBtn.disabled = true;
startExtractionBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Extracting...';
progressCard.classList.remove('hidden');
updateProgress();
clearResults();
addLog(`Starting extraction for "${searchTerm}" in "${location || 'any location'}"`, "info");
addLog(`Configured with ${delay} second delay between requests`, "info");
// Simulate extraction process
extractionInterval = setInterval(() => {
if (!isPaused) {
simulateExtractionStep();
}
}, delay * 1000);
}
function simulateExtractionStep() {
if (extractedItems >= totalItems) {
finishExtraction();
return;
}
extractedItems++;
const isSuccess = Math.random() > 0.1; // 90% success rate simulation
if (isSuccess) successfulExtractions++;
// Update progress
updateProgress();
// Add log entry
const logMessage = isSuccess
? `Successfully extracted data for business #${extractedItems}`
: `Failed to extract data for business #${extractedItems} (retrying...)`;
addLog(logMessage, isSuccess ? "success" : "warning");
// Randomly simulate a CAPTCHA
if (Math.random() < 0.05) {
addLog("CAPTCHA detected - extraction paused for 2 minutes", "error");
isPaused = true;
setTimeout(() => {
isPaused = false;
addLog("CAPTCHA resolved - resuming extraction", "success");
}, 120000);
}
}
function updateProgress() {
const progress = Math.round((extractedItems / totalItems) * 100);
progressBar.style.width = `${progress}%`;
progressText.textContent = `${progress}%`;
extractedCount.textContent = extractedItems;
remainingCount.textContent = totalItems - extractedItems;
const successPercentage = extractedItems > 0
? Math.round((successfulExtractions / extractedItems) * 100)
: 100;
successRate.textContent = `${successPercentage}%`;
// Update time remaining
if (extractedItems > 0) {
const timeElapsed = (new Date() - extractionStartTime) / 1000; // in seconds
const timePerItem = timeElapsed / extractedItems;
const remainingTime = Math.round((totalItems - extractedItems) * timePerItem);
if (remainingTime < 60) {
timeRemaining.textContent = `${remainingTime} seconds`;
} else {
const minutes = Math.floor(remainingTime / 60);
const seconds = remainingTime % 60;
timeRemaining.textContent = `${minutes}m ${seconds}s`;
}
}
}
function finishExtraction() {
clearInterval(extractionInterval);
isExtracting = false;
// Update UI
startExtractionBtn.disabled = false;
startExtractionBtn.innerHTML = '<i class="fas fa-play"></i> Start Extraction';
addLog(`Extraction completed - ${successfulExtractions} of ${totalItems} items successfully extracted`, "success");
// Show completion message
setTimeout(() => {
addLog("Remember: Daily limit resets in 24 hours", "info");
}, 1000);
}
function addLog(message, type) {
const now = new Date();
const timeString = now.toLocaleTimeString();
const colors = {
info: 'text-blue-600',
success: 'text-green-600',
warning: 'text-yellow-600',
error: 'text-red-600'
};
const logEntry = document.createElement('div');
logEntry.className = `text-sm ${colors[type] || 'text-gray-600'} fade-in`;
logEntry.innerHTML = `<span class="font-medium">[${timeString}]</span> ${message}`;
logsContainer.insertBefore(logEntry, logsContainer.firstChild);
// Auto-scroll if not paused
if (!isPaused) {
logsContainer.scrollTop = 0;
}
}
function addResultToTable(data) {
// Remove the placeholder if it exists
if (resultsTable.querySelector('.text-center')) {
resultsTable.innerHTML = '';
}
const row = document.createElement('tr');
row.className = 'fade-in';
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">
<div class="font-medium text-gray-900">${data.name}</div>
<div class="text-xs text-gray-500">${data.website}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${data.address}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${data.phone}</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<span class="bg-blue-100 text-blue-800 text-xs font-semibold px-2 py-0.5 rounded">${data.rating}</span>
<span class="ml-2 text-xs text-gray-500">(${data.reviews} reviews)</span>
</div>
</td>
`;
resultsTable.appendChild(row);
}
function exportToCSV() {
if (resultsTable.querySelector('.text-center')) {
addLog("No data to export", "warning");
return;
}
addLog("Preparing CSV export...", "info");
// In a real implementation, this would generate a proper CSV file
setTimeout(() => {
addLog("CSV file downloaded successfully", "success");
}, 1500);
}
function clearResults() {
resultsTable.innerHTML = `
<tr class="text-center py-4">
<td colspan="4" class="px-6 py-12 text-gray-500">
<i class="fas fa-database text-4xl text-gray-300 mb-3"></i>
<p>No data extracted yet</p>
</td>
</tr>
`;
addLog("Results cleared", "info");
}
function toggleLogs() {
const isCollapsed = logsContainerDiv.classList.contains('hidden');
if (isCollapsed) {
logsContainerDiv.classList.remove('hidden');
toggleLogsBtn.innerHTML = '<i class="fas fa-chevron-up"></i> Collapse';
} else {
logsContainerDiv.classList.add('hidden');
toggleLogsBtn.innerHTML = '<i class="fas fa-chevron-down"></i> Expand';
}
}
function togglePause() {
isPaused = !isPaused;
if (isPaused) {
pauseExtractionBtn.innerHTML = '<i class="fas fa-play"></i>';
addLog("Extraction paused", "warning");
} else {
pauseExtractionBtn.innerHTML = '<i class="fas fa-pause"></i>';
addLog("Extraction resumed", "success");
}
}
// Initialize
updateLimitValue();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Bencolliss/mapsscrapper" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>