424 lines
13 KiB
Markdown
424 lines
13 KiB
Markdown
# Ошибка Forbidden в API Магнит
|
||
_Exported on 12/14/2025 at 03:06:00 GMT+5 from Cursor (2.2.20)_
|
||
|
||
---
|
||
|
||
**User**
|
||
|
||
@.cursor/plans/supermarket_scraper_system_1af4ed29.plan.md
|
||
|
||
Вопрос сейчас только про api магнит
|
||
|
||
https://magnit.ru/webgate/v2/goods/1000257489/stores/992301?storetype=6&catalogtype=1
|
||
ответ Forbidden
|
||
Transaction ID: 2b7f613f-b7fe-4f95-9dd4-b30c1f5cb4a5
|
||
|
||
|
||
Хотя в браузере работает у них на сайте
|
||
https://magnit.ru/product/1000257489-chay_chernyy_greenfield_english_edition_100pak?shopCode=992301&shopType=6
|
||
|
||
вот заголовки в браузере
|
||
URL запроса:
|
||
https://magnit.ru/webgate/v2/goods/1000257489/stores/992301?storetype=6&catalogtype=1
|
||
Метод запроса:
|
||
GET
|
||
Код статуса:
|
||
200 OK
|
||
Удаленный адрес:
|
||
178.248.238.72:443
|
||
Правило для URL перехода:
|
||
origin
|
||
content-encoding:
|
||
gzip
|
||
content-type:
|
||
application/json
|
||
date:
|
||
Sat, 13 Dec 2025 21:41:24 GMT
|
||
document-policy:
|
||
js-profiling
|
||
referrer-policy:
|
||
origin
|
||
server:
|
||
QRATOR
|
||
strict-transport-security:
|
||
max-age=31536000; includeSubDomains
|
||
vary:
|
||
Accept-Encoding,Accept-Encoding
|
||
x-content-type-options:
|
||
nosniff
|
||
x-frame-options:
|
||
SAMEORIGIN
|
||
x-trace-id:
|
||
544850473b9c35d2d1c00538cc1d16c6
|
||
x-xss-protection:
|
||
1
|
||
:authority:
|
||
magnit.ru
|
||
:method:
|
||
GET
|
||
:path:
|
||
/webgate/v2/goods/1000257489/stores/992301?storetype=6&catalogtype=1
|
||
:scheme:
|
||
https
|
||
accept:
|
||
*/*
|
||
accept-encoding:
|
||
gzip, deflate, br, zstd
|
||
accept-language:
|
||
ru,en-US;q=0.9,en;q=0.8
|
||
baggage:
|
||
sentry-environment=production,sentry-public_key=7992e659d14bf5d8da08d2e6a0706d0a,sentry-trace_id=596895b1cc7b4c05935d288b1cc91e8a,sentry-sampled=false,sentry-sample_rand=0.6716192411675364,sentry-sample_rate=0.05
|
||
content-type:
|
||
application/json
|
||
cookie:
|
||
nmg_dt=; shopCode=992301; x_shop_type=ME; mg_udi=9D4909B8-9F79-5B1D-8457-EA519EC9AA3F; oxxfgh=ef3934b0-03b7-44fc-b6d8-9b0dd7412ea3%230%237884000000%235000%231800000%2312840; uwyii=b172df84-99bb-4b86-51b5-909db1474363; nmg_sp=Y; mg_uac=1; mg_adlt=Y; uwyiert=65182acc-a917-c963-d7fe-a14c0cab4e8d
|
||
dnt:
|
||
1
|
||
priority:
|
||
u=1, i
|
||
referer:
|
||
https://magnit.ru/
|
||
sec-ch-ua:
|
||
"Not?A_Brand";v="99", "Chromium";v="130"
|
||
sec-ch-ua-mobile:
|
||
?0
|
||
sec-ch-ua-platform:
|
||
"Windows"
|
||
sec-fetch-dest:
|
||
empty
|
||
sec-fetch-mode:
|
||
cors
|
||
sec-fetch-site:
|
||
same-origin
|
||
sec-gpc:
|
||
1
|
||
sentry-trace:
|
||
596895b1cc7b4c05935d288b1cc91e8a-b581de5bc3a814d5-0
|
||
user-agent:
|
||
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36
|
||
x-app-version:
|
||
2025.12.12-16.56
|
||
x-client-name:
|
||
magnit
|
||
x-device-id:
|
||
9D4909B8-9F79-5B1D-8457-EA519EC9AA3F
|
||
x-device-platform:
|
||
Web
|
||
x-device-tag:
|
||
disabled
|
||
x-new-magnit:
|
||
true
|
||
x-platform-version:
|
||
Windows Chrome 130
|
||
|
||
---
|
||
|
||
**Cursor**
|
||
|
||
|
||
API Магнита возвращает 403, потому что требуется авторизация и корректные заголовки. Анализирую требования и способы их соблюдения:
|
||
|
||
|
||
|
||
API Магнита возвращает 403, потому что требуется авторизация и корректные заголовки. По заголовкам из браузера видно, что нужны:
|
||
|
||
## Проблема
|
||
|
||
API проверяет:
|
||
1. Cookies (сессия)
|
||
2. Заголовки, имитирующие браузер
|
||
3. Referer и origin
|
||
|
||
## Решение
|
||
|
||
### Вариант 1: Использовать Playwright для получения cookies и заголовков
|
||
|
||
Сначала зайти на сайт через Playwright, получить cookies, затем использовать их в HTTP-запросах:
|
||
|
||
```typescript
|
||
// src/scrapers/api/magnit/MagnitApiScraper.ts
|
||
import { chromium, Browser, Page } from 'playwright';
|
||
import axios, { AxiosInstance } from 'axios';
|
||
|
||
export class MagnitApiScraper {
|
||
private browser: Browser | null = null;
|
||
private page: Page | null = null;
|
||
private httpClient: AxiosInstance;
|
||
private cookies: string = '';
|
||
private deviceId: string = '';
|
||
|
||
constructor() {
|
||
this.httpClient = axios.create({
|
||
baseURL: 'https://magnit.ru',
|
||
headers: {
|
||
'accept': '*/*',
|
||
'accept-language': 'ru,en-US;q=0.9,en;q=0.8',
|
||
'content-type': 'application/json',
|
||
'referer': 'https://magnit.ru/',
|
||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
|
||
'x-app-version': '2025.12.12-16.56',
|
||
'x-client-name': 'magnit',
|
||
'x-device-platform': 'Web',
|
||
'x-device-tag': 'disabled',
|
||
'x-new-magnit': 'true',
|
||
'x-platform-version': 'Windows Chrome 130',
|
||
}
|
||
});
|
||
}
|
||
|
||
async initialize() {
|
||
// Запускаем браузер и получаем cookies
|
||
this.browser = await chromium.launch({ headless: true });
|
||
this.page = await this.browser.newPage();
|
||
|
||
// Переходим на главную страницу
|
||
await this.page.goto('https://magnit.ru/', { waitUntil: 'networkidle' });
|
||
|
||
// Получаем cookies
|
||
const cookies = await this.page.context().cookies();
|
||
this.cookies = cookies.map(c => `${c.name}=${c.value}`).join('; ');
|
||
|
||
// Извлекаем device-id из cookies
|
||
const mgUdiCookie = cookies.find(c => c.name === 'mg_udi');
|
||
if (mgUdiCookie) {
|
||
this.deviceId = mgUdiCookie.value;
|
||
} else {
|
||
// Генерируем новый device-id если нет
|
||
this.deviceId = this.generateDeviceId();
|
||
}
|
||
|
||
// Обновляем заголовки с device-id
|
||
this.httpClient.defaults.headers['x-device-id'] = this.deviceId;
|
||
this.httpClient.defaults.headers['cookie'] = this.cookies;
|
||
}
|
||
|
||
async getProductStores(productId: string, storeCode: string = '992301') {
|
||
if (!this.page) {
|
||
await this.initialize();
|
||
}
|
||
|
||
try {
|
||
const response = await this.httpClient.get(
|
||
`/webgate/v2/goods/${productId}/stores/${storeCode}`,
|
||
{
|
||
params: {
|
||
storetype: '6',
|
||
catalogtype: '1'
|
||
}
|
||
}
|
||
);
|
||
|
||
return response.data;
|
||
} catch (error) {
|
||
if (axios.isAxiosError(error) && error.response?.status === 403) {
|
||
// Если 403, обновляем сессию
|
||
await this.initialize();
|
||
return this.getProductStores(productId, storeCode);
|
||
}
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
private generateDeviceId(): string {
|
||
// Генерация UUID формата: 9D4909B8-9F79-5B1D-8457-EA519EC9AA3F
|
||
return 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/[x]/g, () => {
|
||
return (Math.random() * 16 | 0).toString(16).toUpperCase();
|
||
});
|
||
}
|
||
|
||
async close() {
|
||
if (this.browser) {
|
||
await this.browser.close();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Вариант 2: Использовать Playwright напрямую для API-запросов
|
||
|
||
Делать запросы через контекст браузера Playwright:
|
||
|
||
```typescript
|
||
async getProductStoresViaBrowser(productId: string, storeCode: string = '992301') {
|
||
if (!this.page) {
|
||
await this.initialize();
|
||
}
|
||
|
||
// Используем request API Playwright, который автоматически использует cookies браузера
|
||
const response = await this.page!.request.get(
|
||
`https://magnit.ru/webgate/v2/goods/${productId}/stores/${storeCode}?storetype=6&catalogtype=1`,
|
||
{
|
||
headers: {
|
||
'accept': '*/*',
|
||
'referer': 'https://magnit.ru/',
|
||
'x-app-version': '2025.12.12-16.56',
|
||
'x-client-name': 'magnit',
|
||
'x-device-platform': 'Web',
|
||
'x-new-magnit': 'true',
|
||
}
|
||
}
|
||
);
|
||
|
||
if (!response.ok()) {
|
||
throw new Error(`API request failed: ${response.status()}`);
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
```
|
||
|
||
## Важные моменты
|
||
|
||
1. Обязательные заголовки:
|
||
- `x-device-id` — из cookie `mg_udi`
|
||
- `x-client-name: magnit`
|
||
- `x-device-platform: Web`
|
||
- `referer: https://magnit.ru/`
|
||
- `cookie` — все cookies из сессии
|
||
|
||
2. Cookies, которые нужно передавать:
|
||
- `shopCode=992301`
|
||
- `mg_udi` (device-id)
|
||
- `oxxfgh`, `uwyii`, `uwyiert` (сессионные)
|
||
|
||
3. Рекомендация: использовать Playwright для инициализации сессии и получения cookies, затем делать запросы через `page.request` или передавать cookies в axios.
|
||
|
||
Нужна полная реализация `MagnitApiScraper` с этой логикой?
|
||
|
||
```typescript
|
||
// src/scrapers/api/magnit/MagnitApiScraper.ts
|
||
import { chromium, Browser, Page } from 'playwright';
|
||
import axios, { AxiosInstance } from 'axios';
|
||
|
||
export class MagnitApiScraper {
|
||
private browser: Browser | null = null;
|
||
private page: Page | null = null;
|
||
private httpClient: AxiosInstance;
|
||
private cookies: string = '';
|
||
private deviceId: string = '';
|
||
|
||
constructor() {
|
||
this.httpClient = axios.create({
|
||
baseURL: 'https://magnit.ru',
|
||
headers: {
|
||
'accept': '*/*',
|
||
'accept-language': 'ru,en-US;q=0.9,en;q=0.8',
|
||
'content-type': 'application/json',
|
||
'referer': 'https://magnit.ru/',
|
||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
|
||
'x-app-version': '2025.12.12-16.56',
|
||
'x-client-name': 'magnit',
|
||
'x-device-platform': 'Web',
|
||
'x-device-tag': 'disabled',
|
||
'x-new-magnit': 'true',
|
||
'x-platform-version': 'Windows Chrome 130',
|
||
}
|
||
});
|
||
}
|
||
|
||
async initialize() {
|
||
// Запускаем браузер и получаем cookies
|
||
this.browser = await chromium.launch({ headless: true });
|
||
this.page = await this.browser.newPage();
|
||
|
||
// Переходим на главную страницу
|
||
await this.page.goto('https://magnit.ru/', { waitUntil: 'networkidle' });
|
||
|
||
// Получаем cookies
|
||
const cookies = await this.page.context().cookies();
|
||
this.cookies = cookies.map(c => `${c.name}=${c.value}`).join('; ');
|
||
|
||
// Извлекаем device-id из cookies
|
||
const mgUdiCookie = cookies.find(c => c.name === 'mg_udi');
|
||
if (mgUdiCookie) {
|
||
this.deviceId = mgUdiCookie.value;
|
||
} else {
|
||
// Генерируем новый device-id если нет
|
||
this.deviceId = this.generateDeviceId();
|
||
}
|
||
|
||
// Обновляем заголовки с device-id
|
||
this.httpClient.defaults.headers['x-device-id'] = this.deviceId;
|
||
this.httpClient.defaults.headers['cookie'] = this.cookies;
|
||
}
|
||
|
||
async getProductStores(productId: string, storeCode: string = '992301') {
|
||
if (!this.page) {
|
||
await this.initialize();
|
||
}
|
||
|
||
try {
|
||
const response = await this.httpClient.get(
|
||
`/webgate/v2/goods/${productId}/stores/${storeCode}`,
|
||
{
|
||
params: {
|
||
storetype: '6',
|
||
catalogtype: '1'
|
||
}
|
||
}
|
||
);
|
||
|
||
return response.data;
|
||
} catch (error) {
|
||
if (axios.isAxiosError(error) && error.response?.status === 403) {
|
||
// Если 403, обновляем сессию
|
||
await this.initialize();
|
||
return this.getProductStores(productId, storeCode);
|
||
}
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
private generateDeviceId(): string {
|
||
// Генерация UUID формата: 9D4909B8-9F79-5B1D-8457-EA519EC9AA3F
|
||
return 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/[x]/g, () => {
|
||
return (Math.random() * 16 | 0).toString(16).toUpperCase();
|
||
});
|
||
}
|
||
|
||
async close() {
|
||
if (this.browser) {
|
||
await this.browser.close();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
```typescript
|
||
async getProductStoresViaBrowser(productId: string, storeCode: string = '992301') {
|
||
if (!this.page) {
|
||
await this.initialize();
|
||
}
|
||
|
||
// Используем request API Playwright, который автоматически использует cookies браузера
|
||
const response = await this.page!.request.get(
|
||
`https://magnit.ru/webgate/v2/goods/${productId}/stores/${storeCode}?storetype=6&catalogtype=1`,
|
||
{
|
||
headers: {
|
||
'accept': '*/*',
|
||
'referer': 'https://magnit.ru/',
|
||
'x-app-version': '2025.12.12-16.56',
|
||
'x-client-name': 'magnit',
|
||
'x-device-platform': 'Web',
|
||
'x-new-magnit': 'true',
|
||
}
|
||
}
|
||
);
|
||
|
||
if (!response.ok()) {
|
||
throw new Error(`API request failed: ${response.status()}`);
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
```
|
||
|
||
|
||
Проверяю структуру проекта и план, чтобы понять текущее состояние и что нужно реализовать:
|
||
|
||
|
||
|
||
Проверяю, есть ли уже код для работы с API Магнита. Если нет, предложу решение для обхода 403 Forbidden.
|
||
|
||
|