utils/parse/collect_dealers_autoru.js
2026-03-16 11:30:50 +03:00

186 lines
9.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(async function() {
const csrfToken = "79684b90849bec4c80846f355dd1671b48338cc739007d81"; // ← ОБНОВИ!
const url = "https://auto.ru/krasnodar/dilery/cars/new/?place_lat_from=46.65304&place_lon_from=38.80775&place_lat_to=48.07858&place_lon_to=40.5024&geo_radius=300"; // Вставьте URL здесь
const params = new URLSearchParams(url.split('?')[1]);
const latMin = parseFloat(params.get('place_lat_from'));
const latMax = parseFloat(params.get('place_lat_to'));
const lonMin = parseFloat(params.get('place_lon_from'));
const lonMax = parseFloat(params.get('place_lon_to'));
const numLatSplits = 2;
const numLonSplits = 2;
const maxDepth = 10; // Максимальная глубина рекурсии (чтобы не бесконечно разбивать)
let allDealers = [];
const seenCodes = new Set();
// Функция для задержки (чтобы не спамить API)
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Рекурсивная функция для обработки области
async function processArea(latFrom, latTo, lonFrom, lonTo, depth = 0) {
if (depth > maxDepth) {
console.warn(`🚨 Максимальная глубина (${maxDepth}) достигнута для lat ${latFrom}-${latTo}, lon ${lonFrom}-${lonTo}. Пропускаем.`);
return;
}
const body = {
"section": "new",
"category": "cars",
"place_lat_from": latFrom.toFixed(6),
"place_lat_to": latTo.toFixed(6),
"place_lon_from": lonFrom.toFixed(6),
"place_lon_to": lonTo.toFixed(6),
"__dont_change_map": 1,
"geo_radius": 300,
"geo_id": [35],
"page": 1,
"page_size": 100
};
const areaDesc = `lat ${body.place_lat_from}-${body.place_lat_to}, lon ${body.place_lon_from}-${body.place_lon_to}`;
console.log(`\n${depth > 0 ? 'Рекурсия lvl ' + depth + ': ' : ''}Запрос для ${areaDesc}`);
try {
await delay(50); // Пауза 0.5 сек между запросами
const res = await fetch("https://auto.ru/-/ajax/desktop-search/dealersListing/", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"X-Csrf-Token": csrfToken,
"Accept": "application/json",
"Referer": "https://auto.ru/"
},
body: JSON.stringify(body)
});
if (!res.ok) {
console.warn(`Ошибка ${res.status} для ${areaDesc}`);
return;
}
const data = await res.json();
if (data.totalResultsCount && data.totalResultsCount > 100) {
console.log(`⚠️ Total = ${data.totalResultsCount} > 100. Разбиваем на 4 под-области (глубина ${depth + 1}).`);
// Разбиваем на 2x2
const latMid = (latFrom + latTo) / 2;
const lonMid = (lonFrom + lonTo) / 2;
await processArea(latFrom, latMid, lonFrom, lonMid, depth + 1);
await processArea(latFrom, latMid, lonMid, lonTo, depth + 1);
await processArea(latMid, latTo, lonFrom, lonMid, depth + 1);
await processArea(latMid, latTo, lonMid, lonTo, depth + 1);
return; // Не обрабатываем текущую область дальше, т.к. разбили
}
if (!data.dealers) return;
let newCount = 0;
for (const d of data.dealers) {
const code = d.dealerCode || d.code || d.id || d.dealerName;
if (code && !seenCodes.has(code)) {
seenCodes.add(code);
allDealers.push(d);
newCount++;
}
}
console.log(` +${data.dealers.length} дилеров (новых: ${newCount}, всего: ${allDealers.length})`);
console.log(` Total в области: ${data.totalResultsCount || 'неизвестно'}`);
} catch (e) {
console.error(`Ошибка для ${areaDesc}:`, e);
}
}
// Генерация начальных областей
const latStep = (latMax - latMin) / numLatSplits;
const lonStep = (lonMax - lonMin) / numLonSplits;
const subAreas = [];
for (let i = 0; i < numLatSplits; i++) {
const latFrom = latMin + i * latStep;
const latTo = (i === numLatSplits - 1 ? latMax : latMin + (i + 1) * latStep);
for (let j = 0; j < numLonSplits; j++) {
const lonFrom = lonMin + j * lonStep;
const lonTo = (j === numLonSplits - 1 ? lonMax : lonMin + (j + 1) * lonStep);
subAreas.push({ latFrom, latTo, lonFrom, lonTo });
}
}
console.log(`Сгенерировано начальных областей: ${subAreas.length} (${numLatSplits}×${numLonSplits})`);
console.log(`Размер одного квадрата ≈ ${(latStep * 111).toFixed(1)} × ${((lonStep * 111) * Math.cos((latMin + latMax) / 2 * Math.PI / 180)).toFixed(1)} км`);
// Обработка всех начальных областей последовательно
for (let idx = 0; idx < subAreas.length; idx++) {
const { latFrom, latTo, lonFrom, lonTo } = subAreas[idx];
await processArea(latFrom, latTo, lonFrom, lonTo);
}
console.log(`\n✅ Готово! Собрано уникальных дилеров: ${allDealers.length}`);
// Вывод всех дилеров в консоль в табличном виде
console.table(allDealers);
const codes = allDealers.map(d => d.dealerCode || d.code || d.id || d.dealerName || 'unknown');
navigator.clipboard.writeText(codes.join('\n')).then(() => {
console.log("Коды успешно скопированы в буфер!");
}).catch(err => {
console.error("Ошибка копирования:", err);
});
// НОВАЯ ЧАСТЬ: Запрос телефонов для каждого dealerCode
console.log("\n📞 Запрашиваем телефоны для всех дилеров...");
let allPhones = {}; // { code: [phones] }
let counter = 0;
for (const d of allDealers) {
counter++;
console.log(counter)
// if (counter > 10) continue;
const code = d.dealerCode; // Используем dealerCode, если есть
if (!code) {
console.warn(`Пропуск: Нет dealerCode для ${d.dealerName || d.code || d.id}`);
continue;
}
console.log(`Запрос телефонов для ${code}...`);
try {
await delay(100); // Пауза между запросами
const r = await fetch("https://auto.ru/-/ajax/desktop-search/getSalonPhones/", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"X-Csrf-Token": csrfToken,
"Accept": "application/json",
"Referer": "https://auto.ru/"
},
body: JSON.stringify({
code: code,
category: "cars",
section: "all",
geo_radius: 300,
geo_id: [35]
})
});
console.log(`Статус для ${code}: ${r.status}`);
if (!r.ok) {
console.warn(`Ошибка ${r.status} для ${code}`);
continue;
}
const data = await r.json();
console.log(`Полный ответ для ${code}:`, data);
const phones = data || [];
phones.map((v) => {
// phones: [{phone: '', ...}]
if (v.phone) {
v.phone = v.phone.replace(/\D/g, ''); // Оставляем только цифры
}
// add to allPhones
if (!allPhones[code]) {
allPhones[code] = [];
}
if (v.phone && !allPhones[code].includes(v.phone)) {
allPhones[code].push(v.phone);
}
})
console.log(`Телефоны для ${code}:`, phones.length ? phones : "нет телефонов");
} catch (e) {
console.error(`Ошибка для ${code}:`, e);
}
}
// Вывод всех телефонов в таблицу
console.log("\n📋 Все телефоны:");
console.table(Object.entries(allPhones).map(([code, phones]) => ({
code,
phones: phones.join(', ') || 'нет'
})));
// Скопировать телефоны в буфер (code: phones)
const phonesText = Object.entries(allPhones).map(([code, phones]) => `${code}: ${phones.join(', ') || 'нет'}`).join('\n');
navigator.clipboard.writeText(phonesText).then(() => {
console.log("Телефоны (code: phones) скопированы в буфер!");
}).catch(err => {
console.error("Ошибка копирования телефонов:", err);
});
})()