Отслеживание цены и наличия товара
Ждёте, пока подешевеет видеокарта или билет, ловите момент, когда товар снова завезут? Вместо того чтобы обновлять вкладку по десять раз в день, повесим проверку на cron: скрипт сам скачает страницу, вытащит цену и пришлёт push, когда что-то изменится.
Отслеживание цены
Заголовок раздела «Отслеживание цены»Скрипт скачивает страницу, вытаскивает цену (тут — через grep, но чаще
удобнее CSS-селектором в pup или JSON через jq), сравнивает с сохранённой:
#!/usr/bin/env bash# ~/bin/notifly-price — `notifly-price <url> <желаемая_цена>`set -euset -a; source ~/.notifly.env; set +a
URL="${1:?url}"TARGET="${2:-0}" # порог: push, если цена не выше этого значенияUA="Mozilla/5.0 (compatible; notifly-price/1.0)"STATE="/tmp/notifly-price-$(echo -n "$URL" | md5sum | cut -c1-8).state"PREV=$(cat "$STATE" 2>/dev/null || echo 0)
HTML=$(curl -fsSL --compressed -A "$UA" "$URL")
# Вытаскиваем первое число из тега с ценой. Селектор подгоните под сайт:# у многих магазинов цена лежит в JSON-LD ("price":"12990") — тогда через jq.PRICE=$(echo "$HTML" | grep -oE '"price":"[0-9]+' | head -1 | grep -oE '[0-9]+')[[ -z "$PRICE" ]] && { echo "Цена не найдена — поправьте селектор" >&2; exit 1; }
# Цена упала относительно прошлой проверки?if (( PREV > 0 )) && (( PRICE < PREV )); then notifly-send "📉 Цена упала: $PRICE ₽" "Было $PREV ₽\n$URL" 6fi
# Достигли желаемого порога?if (( TARGET > 0 )) && (( PRICE <= TARGET )) && (( PREV > TARGET || PREV == 0 )); then notifly-send "🎯 Цена ≤ $TARGET ₽" "Сейчас $PRICE ₽\n$URL" 8fi
echo "$PRICE" > "$STATE"Ставим в cron раз в два часа (уважаем сайт — не чаще):
# ~/bin/notifly-price <url> <порог>17 */2 * * * ~/bin/notifly-price 'https://shop.example.com/gpu-4080' 89990Наличие товара («снова в продаже»)
Заголовок раздела «Наличие товара («снова в продаже»)»Иногда важна не цена, а сам факт появления. Ловим по признаку в разметке («В корзину» вместо «Нет в наличии»):
#!/usr/bin/env bash# ~/bin/notifly-stock — `notifly-stock <url>`set -euset -a; source ~/.notifly.env; set +a
URL="${1:?url}"STATE="/tmp/notifly-stock-$(echo -n "$URL" | md5sum | cut -c1-8).state"PREV=$(cat "$STATE" 2>/dev/null || echo OUT)
HTML=$(curl -fsSL --compressed -A "Mozilla/5.0 (notifly-stock)" "$URL")
# Признак наличия. Подгоните строку под конкретный магазин.if echo "$HTML" | grep -qi 'в корзину\|add to cart'; then NOW=INelse NOW=OUTfi
if [[ "$NOW" == "IN" && "$PREV" == "OUT" ]]; then notifly-send "🛒 Товар в наличии!" "$URL" 8fiecho "$NOW" > "$STATE"Совет: цену из JSON надёжнее брать через jq
Заголовок раздела «Совет: цену из JSON надёжнее брать через jq»Многие сайты кладут цену в JSON-LD <script type="application/ld+json">.
Это стабильнее, чем парсить HTML:
PRICE=$(echo "$HTML" \ | grep -oP '(?<=<script type="application/ld\+json">).*?(?=</script>)' \ | jq -r '..|.price? // empty' | head -1)Windows: PowerShell + Task Scheduler
Заголовок раздела «Windows: PowerShell + Task Scheduler»Аналог на PowerShell. Предполагается настроенная
функция Send-Notifly.
param([Parameter(Mandatory)][string]$Url, [int]$Target = 0). $env:USERPROFILE\Documents\WindowsPowerShell\Notifly.ps1
$hash = ([System.BitConverter]::ToString( (New-Object Security.Cryptography.MD5CryptoServiceProvider).ComputeHash( [Text.Encoding]::UTF8.GetBytes($Url))) -replace '-').Substring(0, 8)$State = "$env:LOCALAPPDATA\notifly-price-$hash.txt"$prev = if (Test-Path $State) { [int](Get-Content $State) } else { 0 }
$html = Invoke-WebRequest -Uri $Url -UserAgent "Mozilla/5.0 (notifly-price)" -UseBasicParsing# Первое число из "price":"12990". Селектор подгоните под сайт.$price = [int]([regex]::Match($html.Content, '"price":"(\d+)').Groups[1].Value)if ($price -eq 0) { throw "Цена не найдена — поправьте селектор" }
if ($prev -gt 0 -and $price -lt $prev) { Send-Notifly -Title "📉 Цена упала: $price ₽" -Message "Было $prev ₽`n$Url" -Priority 6}if ($Target -gt 0 -and $price -le $Target -and ($prev -gt $Target -or $prev -eq 0)) { Send-Notifly -Title "🎯 Цена ≤ $Target ₽" -Message "Сейчас $price ₽`n$Url" -Priority 8}$price | Set-Content $StateРасписание раз в два часа:
$Action = New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File C:\scripts\Notifly-Price.ps1 -Url 'https://shop.example.com/gpu' -Target 89990"$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) ` -RepetitionInterval (New-TimeSpan -Hours 2)Register-ScheduledTask -TaskName "Notifly Price Watch" -Action $Action -Trigger $Trigger- Ловите скидку, не залипая в браузере. Cron проверяет за вас круглосуточно.
- Порог + падение. Отдельно «стало дешевле» (приоритет 6) и «достигло цели» (приоритет 8, чтобы точно не проспать).
- Не только цены. Тот же приём — любое изменение на странице: статус заявки, расписание, наличие записи к врачу.
Что улучшить дальше
Заголовок раздела «Что улучшить дальше»- Для сайтов, где всё рисует JavaScript, снимать цену
встроенным браузером-монитором вместо
curl. - Хранить историю цен и слать график минимума за неделю.
- Прикладывать в push ссылку-картинку товара через дополнительные поля.