new commit

This commit is contained in:
54CHA
2025-07-19 17:56:06 +03:00
commit 4153e2c00a
140 changed files with 66097 additions and 0 deletions

419
public/test-parser.html Normal file
View File

@ -0,0 +1,419 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Тест Парсера Wildberries - ОБНОВЛЕНО</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.header {
text-align: center;
margin-bottom: 30px;
}
.header h1 {
color: #2d3748;
margin-bottom: 10px;
}
.status-badge {
display: inline-block;
background: #48bb78;
color: white;
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
font-weight: 600;
}
.update-info {
background: #e6fffa;
border: 2px solid #48bb78;
border-radius: 8px;
padding: 20px;
margin-bottom: 30px;
}
.update-info h3 {
color: #2d3748;
margin-top: 0;
}
.update-info ul {
color: #4a5568;
line-height: 1.6;
}
.test-section {
background: #f7fafc;
border-radius: 8px;
padding: 25px;
margin-bottom: 25px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #2d3748;
}
input[type="text"] {
width: 100%;
padding: 12px;
border: 2px solid #e2e8f0;
border-radius: 6px;
font-size: 16px;
box-sizing: border-box;
}
.preset-buttons {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-top: 10px;
}
.preset-btn {
background: #667eea;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s;
}
.preset-btn:hover {
background: #5a67d8;
}
.test-btn {
background: #48bb78;
color: white;
border: none;
padding: 15px 30px;
border-radius: 8px;
font-size: 18px;
font-weight: 600;
cursor: pointer;
width: 100%;
transition: background 0.2s;
}
.test-btn:hover:not(:disabled) {
background: #38a169;
}
.test-btn:disabled {
background: #a0aec0;
cursor: not-allowed;
}
.loading {
display: none;
text-align: center;
margin: 20px 0;
color: #4a5568;
}
.loading.active {
display: block;
}
.spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid #e2e8f0;
border-radius: 50%;
border-top-color: #667eea;
animation: spin 1s ease-in-out infinite;
margin-right: 10px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.results {
display: none;
margin-top: 30px;
}
.results.active {
display: block;
}
.result-header {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.status-icon {
font-size: 24px;
margin-right: 10px;
}
.product-card {
background: white;
border: 2px solid #e2e8f0;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.product-image {
width: 100px;
height: 100px;
object-fit: cover;
border-radius: 8px;
margin-right: 15px;
}
.product-info {
display: flex;
align-items: flex-start;
}
.product-details h3 {
margin: 0 0 10px 0;
color: #2d3748;
}
.positions-table {
width: 100%;
border-collapse: collapse;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.positions-table th,
.positions-table td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #e2e8f0;
}
.positions-table th {
background: #4a5568;
color: white;
font-weight: 600;
}
.position-found {
color: #48bb78;
font-weight: 600;
}
.position-not-found {
color: #f56565;
font-weight: 600;
}
.error {
background: #fed7d7;
border: 2px solid #f56565;
color: #c53030;
padding: 15px;
border-radius: 8px;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🎯 Тест Парсера Wildberries</h1>
<span class="status-badge">✅ РАБОТАЕТ</span>
</div>
<div class="update-info">
<h3>🚀 Парсер обновлен и работает!</h3>
<ul>
<li><strong>Решена проблема блокировки:</strong> Добавлены продвинутые методы обхода защиты Wildberries</li>
<li><strong>Улучшенная маскировка:</strong> Парсер теперь имитирует реальное поведение пользователя</li>
<li><strong>API поиск:</strong> Добавлен резервный поиск через внутренние API</li>
<li><strong>Стабильная работа:</strong> Тестирование показало успешное нахождение товаров</li>
<li><strong>Корректная обработка:</strong> Правильно обрабатывает случаи "товар не найден"</li>
</ul>
</div>
<div class="test-section">
<h2>🧪 Протестировать парсер</h2>
<div class="form-group">
<label for="query">Поисковый запрос:</label>
<input type="text" id="query" value="лабубу" placeholder="Введите поисковый запрос">
<div class="preset-buttons">
<button class="preset-btn" onclick="setPreset('лабубу', '447020075')">Лабубу (работает)</button>
<button class="preset-btn" onclick="setPreset('плюшевая игрушка', '197761909')">Плюшевая игрушка (работает)</button>
<button class="preset-btn" onclick="setPreset('игрушка', '123456789')">Тест "не найдено"</button>
</div>
</div>
<div class="form-group">
<label for="article">Артикул товара:</label>
<input type="text" id="article" value="447020075" placeholder="Введите артикул">
</div>
<button class="test-btn" onclick="testParser()" id="testBtn">
🔍 Запустить тест парсера
</button>
<div class="loading" id="loading">
<div class="spinner"></div>
Парсинг в процессе... Это может занять до 1 минуты
</div>
</div>
<div class="results" id="results">
<div class="result-header">
<span class="status-icon" id="statusIcon"></span>
<h2 id="resultTitle"></h2>
</div>
<div id="resultContent"></div>
</div>
</div>
<script>
function setPreset(query, article) {
document.getElementById('query').value = query;
document.getElementById('article').value = article;
}
async function testParser() {
const query = document.getElementById('query').value.trim();
const article = document.getElementById('article').value.trim();
if (!query || !article) {
alert('Пожалуйста, заполните все поля');
return;
}
const testBtn = document.getElementById('testBtn');
const loading = document.getElementById('loading');
const results = document.getElementById('results');
// Показываем индикатор загрузки
testBtn.disabled = true;
loading.classList.add('active');
results.classList.remove('active');
try {
const response = await fetch('/api/test-parser', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: query,
myArticleId: article
})
});
const data = await response.json();
if (data.success) {
showResults(true, data.data, data.message);
} else {
showResults(false, null, data.error || 'Произошла ошибка');
}
} catch (error) {
showResults(false, null, 'Ошибка соединения: ' + error.message);
} finally {
testBtn.disabled = false;
loading.classList.remove('active');
}
}
function showResults(success, data, message) {
const results = document.getElementById('results');
const statusIcon = document.getElementById('statusIcon');
const resultTitle = document.getElementById('resultTitle');
const resultContent = document.getElementById('resultContent');
if (success && data) {
statusIcon.textContent = '✅';
resultTitle.textContent = 'Парсинг выполнен успешно!';
const product = data.product;
const positions = data.positions;
const foundPositions = positions.filter(p => p.position !== null);
const isFound = foundPositions.length > 0;
resultContent.innerHTML = `
<div class="product-card">
<div class="product-info">
<img src="${product.imageUrl}" alt="${product.name}" class="product-image" onerror="this.src='/images/no-image.svg'">
<div class="product-details">
<h3>${product.name}</h3>
<p><strong>Бренд:</strong> ${product.brand}</p>
<p><strong>Цена:</strong> ${product.price} ₽</p>
<p><strong>Артикул:</strong> ${product.article}</p>
<p><strong>Статус:</strong> ${isFound ? '<span class="position-found">Найден в поиске</span>' : '<span class="position-not-found">Не найден в поиске</span>'}</p>
</div>
</div>
</div>
<h3>Позиции по городам:</h3>
<table class="positions-table">
<thead>
<tr>
<th>Город</th>
<th>Код</th>
<th>Позиция</th>
<th>Статус</th>
</tr>
</thead>
<tbody>
${positions.map(pos => `
<tr>
<td>${pos.city}</td>
<td>${pos.cityCode}</td>
<td>${pos.position || '—'}</td>
<td>${pos.position ? '<span class="position-found">Найден</span>' : '<span class="position-not-found">Не найден</span>'}</td>
</tr>
`).join('')}
</tbody>
</table>
<p style="margin-top: 20px; color: #4a5568; font-style: italic;">${message}</p>
`;
} else {
statusIcon.textContent = '❌';
resultTitle.textContent = 'Ошибка парсинга';
resultContent.innerHTML = `<div class="error">${message}</div>`;
}
results.classList.add('active');
}
</script>
</body>
</html>