<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>Vatican</title><subtitle>Frontend architect</subtitle><author><name>Vatican</name></author><id>https://teletype.in/atom/vatican_joy</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/vatican_joy?offset=0"></link><link rel="alternate" type="text/html" href="https://vatican-blog.cvv.life/?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=vatican_joy"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/vatican_joy?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-09-18T13:24:24.979Z</updated><entry><id>vatican_joy:Reality_Reversal</id><link rel="alternate" type="text/html" href="https://vatican-blog.cvv.life/Reality_Reversal?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=vatican_joy"></link><title>Reality Reversal</title><published>2026-09-18T10:20:25.295Z</published><updated>2026-09-18T10:20:25.295Z</updated><category term="dev" label="Dev"></category><summary type="html">&lt;img src=&quot;https://img3.teletype.in/files/66/e8/66e89196-e734-482b-88a1-d1734b4208da.jpeg&quot;&gt;В 1986 году The Mentor написал &quot;Сознание хакера&quot;. Прошло почти 40 лет. Мир изменился. Мы — нет.</summary><content type="html">
  &lt;p id=&quot;GvkA&quot;&gt;&lt;em&gt;В 1986 году The Mentor написал &amp;quot;Сознание хакера&amp;quot;. Прошло почти 40 лет. Мир изменился. Мы — нет.&lt;/em&gt;&lt;/p&gt;
  &lt;blockquote id=&quot;LTti&quot;&gt;&lt;strong&gt;Мы верим, что:&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;Знание — это власть. Не доступ к власти. Не инструмент власти. Сама власть.&lt;/strong&gt;&lt;/blockquote&gt;
  &lt;blockquote id=&quot;TV69&quot;&gt;&lt;strong&gt;&lt;br /&gt;Тот, кто понимает механику системы — контролирует её. Тот, кто контролирует — не спрашивает разрешения.&lt;/strong&gt;&lt;/blockquote&gt;
  &lt;h3 id=&quot;G11s&quot;&gt;Наши принципы:&lt;br /&gt;1. Разбирай всё&lt;/h3&gt;
  &lt;blockquote id=&quot;74ct&quot;&gt;&lt;em&gt;Всё, что построено — можно разобрать. Всё, что работает — можно понять. Всё, что можно понять — можно воспроизвести. Нет &amp;quot;закрытых&amp;quot; систем. Есть системы, которые ты ещё не вскрыл.&lt;/em&gt;&lt;/blockquote&gt;
  &lt;h3 id=&quot;3e73&quot;&gt;2. Поверхность — это ложь&lt;/h3&gt;
  &lt;blockquote id=&quot;TDR4&quot;&gt;&lt;em&gt;То, что показывают — витрина. То, что под ней — сущность. API скрыт? Значит есть. Данные &amp;quot;недоступны&amp;quot;? Значит просто не нашли endpoint. Реальность имеет слои, и большинство живёт на первом.&lt;/em&gt;&lt;/blockquote&gt;
  &lt;h3 id=&quot;nlkw&quot;&gt;3. Слабость системы — не твоя проблема&lt;/h3&gt;
  &lt;blockquote id=&quot;1bWH&quot;&gt;&lt;em&gt;Если их пароль — 123456. Если их API отдаёт всё без токена. Если их &amp;quot;защита&amp;quot; — проверка на фронте. Это не ты эксплуатируешь. Это они построили картонный дом.&lt;/em&gt;&lt;/blockquote&gt;
  &lt;h3 id=&quot;kp1a&quot;&gt;4. Инструмент не выбирает цель&lt;/h3&gt;
  &lt;blockquote id=&quot;mN0d&quot;&gt;&lt;em&gt;Молоток не решает, что строить. Скрипт не решает, что парсить. Тот, кто держит инструмент в руках — определяет применение.&lt;/em&gt;&lt;/blockquote&gt;
  &lt;h3 id=&quot;oSfT&quot;&gt;5. Эффективность — единственная метрика&lt;/h3&gt;
  &lt;blockquote id=&quot;QlQ1&quot;&gt;&lt;em&gt;Работает / не работает. Быстро / медленно. Обнаружили / не обнаружили. Остальное — шум.&lt;/em&gt;&lt;/blockquote&gt;
  &lt;h2 id=&quot;6tmV&quot;&gt;Три типа тех, кто в этой игре:&lt;/h2&gt;
  &lt;ol id=&quot;aCky&quot;&gt;
    &lt;li id=&quot;HovK&quot;&gt;Играет по правилам, которые написал кто-то другой. Ждёт bug bounty. Отчитывается. Просит.&lt;/li&gt;
    &lt;li id=&quot;c75z&quot;&gt;Берёт что находит. Не спрашивает. Не оглядывается. Иногда проигрывает.&lt;/li&gt;
    &lt;li id=&quot;dHkt&quot;&gt;Понимает обе стороны. Использует ту, которая даёт результат. Границу двигает под задачу.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;blockquote id=&quot;ZoKA&quot;&gt;&lt;em&gt;Этот канал — не про этику. Не про закон. Не про &amp;quot;как правильно&amp;quot;.&lt;br /&gt;Этот канал — про то, &lt;strong&gt;как всё устроено на самом деле&lt;/strong&gt;.&lt;br /&gt;Система видит то, что показывает. Мы видим то, что она скрывает.&lt;/em&gt;&lt;/blockquote&gt;
  &lt;blockquote id=&quot;CO9o&quot;&gt;&lt;em&gt;By Vatican&lt;/em&gt;&lt;/blockquote&gt;

</content></entry><entry><id>vatican_joy:dark_side_API</id><link rel="alternate" type="text/html" href="https://vatican-blog.cvv.life/dark_side_API?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=vatican_joy"></link><title>Тёмная сторона API</title><published>2026-09-18T10:05:36.013Z</published><updated>2026-09-18T10:05:36.013Z</updated><category term="dev" label="Dev"></category><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/01/6e/016ea2aa-72cf-471c-a2cf-4d19958edb59.jpeg&quot;&gt;Всё, что ты видишь в браузере — это данные. Вопрос только в том, как их достать.</summary><content type="html">
  &lt;h2 id=&quot;2dNy&quot;&gt;Часть 1: Анатомия данных&lt;/h2&gt;
  &lt;p id=&quot;iAAF&quot;&gt;&lt;em&gt;Всё, что ты видишь в браузере — это данные. Вопрос только в том, как их достать.&lt;/em&gt;&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;xhNT&quot;&gt;Сайт в браузере&lt;/p&gt;
    &lt;ul id=&quot;ClcM&quot;&gt;
      &lt;li id=&quot;20dt&quot;&gt;Открытый API (мб будет документация)&lt;/li&gt;
      &lt;li id=&quot;Ni6w&quot;&gt;Скрытый API (документации нету но стучать можно)&lt;/li&gt;
      &lt;li id=&quot;MasQ&quot;&gt;&lt;em&gt;GraphQL endpoint&lt;/em&gt;&lt;/li&gt;
      &lt;li id=&quot;sulH&quot;&gt;&lt;em&gt;WebSocket поток&lt;/em&gt;&lt;/li&gt;
      &lt;li id=&quot;qB0c&quot;&gt;&lt;em&gt;Серверный рендеринг (HTML с данными)&lt;/em&gt;&lt;/li&gt;
      &lt;li id=&quot;yeVV&quot;&gt;&lt;em&gt;Клиентский рендеринг (JS собирает данные)&lt;/em&gt;&lt;/li&gt;
      &lt;li id=&quot;5KC9&quot;&gt;&lt;em&gt;Скрытые поля, комментарии, метаданные&lt;/em&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/section&gt;
  &lt;blockquote id=&quot;TAjH&quot;&gt;&lt;strong&gt;&lt;em&gt;&lt;u&gt;Если данные видны в браузере — их можно достать программно.&lt;/u&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/blockquote&gt;
  &lt;h2 id=&quot;Ut8t&quot;&gt;Часть 2: Уровни доступа к данным&lt;/h2&gt;
  &lt;h3 id=&quot;6e9S&quot;&gt;&lt;em&gt;Уровень 0 — Официальное API&lt;/em&gt;&lt;/h3&gt;
  &lt;blockquote id=&quot;dV37&quot;&gt;Самое простое. Документация открыта, ключ бесплатный.&lt;/blockquote&gt;
  &lt;pre id=&quot;lZlU&quot; data-lang=&quot;python&quot;&gt;import requests
# Пример: публичное API

response = requests.get(&amp;quot;https://api.example.com/v1/data&amp;quot;)
data = response.json()&lt;/pre&gt;
  &lt;p id=&quot;t15K&quot;&gt;&lt;strong&gt;Плюсы:&lt;/strong&gt; Стабильно, легально, документация&lt;br /&gt;&lt;strong&gt;Минусы:&lt;/strong&gt; Rate limits, неполные данные, ключ могут отозвать&lt;/p&gt;
  &lt;h3 id=&quot;wTVY&quot;&gt;Уровень 1 — Скрытый API (Reverse Engineering)&lt;/h3&gt;
  &lt;p id=&quot;vl3c&quot;&gt;&lt;em&gt;Сайты часто имеют внутренние API, которые фронтенд использует для получения данных. Документации нет, но endpoint работает.&lt;/em&gt;&lt;/p&gt;
  &lt;p id=&quot;fLPD&quot;&gt;&lt;strong&gt;Как найти:&lt;/strong&gt;&lt;/p&gt;
  &lt;ol id=&quot;460M&quot;&gt;
    &lt;li id=&quot;DQ6B&quot;&gt;Открой DevTools (F12)&lt;/li&gt;
    &lt;li id=&quot;btoM&quot;&gt;Вкладка Network → XHR/Fetch&lt;/li&gt;
    &lt;li id=&quot;bf3G&quot;&gt;Обнови страницу&lt;/li&gt;
    &lt;li id=&quot;r5yX&quot;&gt;Смотри какие запросы летят&lt;/li&gt;
    &lt;li id=&quot;teu4&quot;&gt;Найди endpoint с нужными данными&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;xaPM&quot;&gt;&lt;/p&gt;
  &lt;blockquote id=&quot;VLOE&quot;&gt;&lt;strong&gt;Пример — что видишь в Network:&lt;br /&gt;GET https://site.com/api/v2/products?page=1&amp;amp;limit=50&lt;/strong&gt;&lt;/blockquote&gt;
  &lt;p id=&quot;cr5e&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;y1Pp&quot;&gt;&lt;strong&gt;Воспроизводим&lt;/strong&gt;&lt;/p&gt;
  &lt;pre id=&quot;2O5i&quot; data-lang=&quot;python&quot;&gt;import requests

headers = {
    &amp;quot;Authorization&amp;quot;: &amp;quot;Bearer eyJhbGciOiJIUzI1...&amp;quot;,
    &amp;quot;User-Agent&amp;quot;: &amp;quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64)&amp;quot;,
    &amp;quot;Accept&amp;quot;: &amp;quot;application/json&amp;quot;,
    &amp;quot;Referer&amp;quot;: &amp;quot;https://site.com/catalog&amp;quot;
}

response = requests.get(
    &amp;quot;https://site.com/api/v2/products&amp;quot;,
    params={&amp;quot;page&amp;quot;: 1, &amp;quot;limit&amp;quot;: 50},
    headers=headers
)
print(response.json())&lt;/pre&gt;
  &lt;p id=&quot;rX4d&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;x0vv&quot;&gt;&lt;strong&gt;Инструменты для реверса:&lt;/strong&gt;&lt;/p&gt;
  &lt;ul id=&quot;UJ66&quot;&gt;
    &lt;li id=&quot;kjfo&quot;&gt;&lt;strong&gt;DevTools (Network)&lt;/strong&gt; — базовый&lt;/li&gt;
    &lt;li id=&quot;SGLH&quot;&gt;&lt;strong&gt;mitmproxy&lt;/strong&gt; — перехват трафика мобильных приложений&lt;/li&gt;
    &lt;li id=&quot;mGkA&quot;&gt;&lt;strong&gt;Charles Proxy&lt;/strong&gt; — то же, но с GUI&lt;/li&gt;
    &lt;li id=&quot;uzck&quot;&gt;&lt;strong&gt;Postman&lt;/strong&gt; — тестирование найденных endpoints&lt;/li&gt;
    &lt;li id=&quot;Rw3d&quot;&gt;&lt;strong&gt;Frida&lt;/strong&gt; — хук методов в мобильных приложениях&lt;/li&gt;
    &lt;li id=&quot;IbZ1&quot;&gt;&lt;strong&gt;Burp Suite — &lt;/strong&gt;Позволяет перехватывать, анализировать и модифицировать HTTP- и HTTPS-трафик между браузером&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h3 id=&quot;TAw5&quot;&gt;Уровень 2 — GraphQL Endpoints&lt;/h3&gt;
  &lt;blockquote id=&quot;LKJH&quot;&gt;&lt;em&gt;Многие современные сайты используют GraphQL. Он отдаёт ровно те данные, которые запросишь.&lt;/em&gt;&lt;/blockquote&gt;
  &lt;p id=&quot;nrEd&quot;&gt;&lt;strong&gt;Как обнаружить:&lt;/strong&gt;&lt;/p&gt;
  &lt;ol id=&quot;s1he&quot;&gt;
    &lt;li id=&quot;r7Rz&quot;&gt;В DevTools ищи POST-запросы на /graphql&lt;/li&gt;
    &lt;li id=&quot;5s3u&quot;&gt;Или /api/graphql&lt;/li&gt;
    &lt;li id=&quot;48N5&quot;&gt;Или любые POST с query в body&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;iCtC&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;OfEu&quot;&gt;&lt;strong&gt;Интроспекция — всей схемы:&lt;/strong&gt;&lt;/p&gt;
  &lt;pre id=&quot;4XUC&quot; data-lang=&quot;graphql&quot;&gt;# Запрос всей схемы API
{
  __schema {
    types {
      name
      fields {
        name
        type {
          name
        }
      }
    }
  }
}&lt;/pre&gt;
  &lt;p id=&quot;7R2v&quot;&gt;&lt;strong&gt;Пример запроса данных:&lt;/strong&gt;&lt;/p&gt;
  &lt;pre id=&quot;ozaX&quot; data-lang=&quot;python&quot;&gt;import requests

query = &amp;quot;&amp;quot;&amp;quot;
{
  users(first: 100) {
    id
    username
    email
    posts {
      title
      createdAt
    }
  }
}
&amp;quot;&amp;quot;&amp;quot;

response = requests.post(
    &amp;quot;https://site.com/graphql&amp;quot;,
    json={&amp;quot;query&amp;quot;: query},
    headers={&amp;quot;Content-Type&amp;quot;: &amp;quot;application/json&amp;quot;}
)
print(response.json())&lt;/pre&gt;
  &lt;p id=&quot;H7bc&quot;&gt;&lt;strong&gt;Инструменты:&lt;/strong&gt;&lt;/p&gt;
  &lt;ul id=&quot;ZWR8&quot;&gt;
    &lt;li id=&quot;Cd5c&quot;&gt;&lt;strong&gt;GraphiQL / Altair&lt;/strong&gt; — IDE для GraphQL&lt;/li&gt;
    &lt;li id=&quot;Ey4b&quot;&gt;&lt;strong&gt;graphql-path-enum&lt;/strong&gt; — перебор скрытых полей&lt;/li&gt;
    &lt;li id=&quot;hCP0&quot;&gt;&lt;strong&gt;InQL (Burp extension)&lt;/strong&gt; — автоматическая интроспекция&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;DmHI&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;5uYi&quot;&gt;Уровень 3 — Парсинг HTML&lt;/h3&gt;
  &lt;blockquote id=&quot;uJ2c&quot;&gt;&lt;em&gt;Когда API нет или оно закрыто — парсим то, что видит браузер.&lt;/em&gt;&lt;/blockquote&gt;
  &lt;h4 id=&quot;d9Em&quot;&gt;Базовый — requests + BeautifulSoup&lt;/h4&gt;
  &lt;pre id=&quot;RXvG&quot; data-lang=&quot;python&quot;&gt;import requests
from bs4 import BeautifulSoup

response = requests.get(&amp;quot;https://example.com/products&amp;quot;)
soup = BeautifulSoup(response.text, &amp;quot;html.parser&amp;quot;)

products = soup.find_all(&amp;quot;div&amp;quot;, class_=&amp;quot;product-card&amp;quot;)

for product in products:
    title = product.find(&amp;quot;h3&amp;quot;).text.strip()
    price = product.find(&amp;quot;span&amp;quot;, class_=&amp;quot;price&amp;quot;).text
    link = product.find(&amp;quot;a&amp;quot;)[&amp;quot;href&amp;quot;]
    
    print(f&amp;quot;{title} | {price} | {link}&amp;quot;)&lt;/pre&gt;
  &lt;p id=&quot;rp9o&quot;&gt;&lt;strong&gt;или когда например html динамический&lt;/strong&gt;&lt;/p&gt;
  &lt;pre id=&quot;hCHV&quot; data-lang=&quot;python&quot;&gt;from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    
    # Перехватываем API-запросы страницы
    responses = []
    page.on(&amp;quot;response&amp;quot;, lambda res: responses.append(res) 
            if &amp;quot;api&amp;quot; in res.url else None)
    
    page.goto(&amp;quot;https://example.com/catalog&amp;quot;)
    page.wait_for_load_state(&amp;quot;networkidle&amp;quot;)
    
    # Данные из перехваченных API-запросов
    for res in responses:
        print(res.url, res.json())
    
    # Или контент из DOM
    content = page.content()
    browser.close()
&lt;/pre&gt;
  &lt;h4 id=&quot;aYBE&quot;&gt;Селекторы — что использовать&lt;/h4&gt;
  &lt;pre id=&quot;VGxd&quot; data-lang=&quot;python&quot;&gt;# CSS селекторы (быстрее, чище)
soup.select(&amp;quot;div.product-card &amp;gt; h3.title&amp;quot;)
soup.select(&amp;quot;a[href*=&amp;#x27;/product/&amp;#x27;]&amp;quot;)
soup.select(&amp;quot;span[data-price]&amp;quot;)

# XPath (мощнее для сложной логики)
soup.xpath(&amp;quot;//div[@class=&amp;#x27;item&amp;#x27;][position() &amp;lt; 10]&amp;quot;)
soup.xpath(&amp;quot;//a[contains(@href, &amp;#x27;product&amp;#x27;)]/@href&amp;quot;)&lt;/pre&gt;
  &lt;p id=&quot;9Gpm&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;om4n&quot;&gt;Уровень 4 — Headless Browser (эмуляция)&lt;/h3&gt;
  &lt;blockquote id=&quot;vDSs&quot;&gt;&lt;em&gt;Когда сайт проверяет JS, куки, поведение — эмулируем реального пользователя.&lt;/em&gt;&lt;/blockquote&gt;
  &lt;h4 id=&quot;xOAR&quot;&gt;Playwright — стандарт&lt;/h4&gt;
  &lt;pre id=&quot;wGfa&quot; data-lang=&quot;python&quot;&gt;from playwright.sync_api import sync_playwright
import time
import random

with sync_playwright() as p:
    browser = p.chromium.launch(
        headless=False,
        args=[
            &amp;quot;--disable-blink-features=AutomationControlled&amp;quot;,
            &amp;quot;--no-sandbox&amp;quot;
        ]
    )
    
    context = browser.new_context(
        viewport={&amp;quot;width&amp;quot;: 1920, &amp;quot;height&amp;quot;: 1080},
        user_agent=&amp;quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36&amp;quot;,
        locale=&amp;quot;ru-RU&amp;quot;,
        timezone_id=&amp;quot;Europe/Moscow&amp;quot;
    )
    
    page = context.new_page()
    
    # Убираем флаг webdriver
    page.add_init_script(&amp;quot;&amp;quot;&amp;quot;
        Object.defineProperty(navigator, &amp;#x27;webdriver&amp;#x27;, {
            get: () =&amp;gt; undefined
        });
    &amp;quot;&amp;quot;&amp;quot;)
    
    page.goto(&amp;quot;https://example.com&amp;quot;)
    
    # Имитируем человека
    time.sleep(random.uniform(1, 3))
    page.mouse.move(random.randint(100, 800), random.randint(100, 600))
    
    # Скроллим как человек
    page.evaluate(&amp;quot;&amp;quot;&amp;quot;
        async () =&amp;gt; {
            for (let i = 0; i &amp;lt; document.body.scrollHeight; i += 100) {
                window.scrollTo(0, i);
                await new Promise(r =&amp;gt; setTimeout(r, 100));
            }
        }
    &amp;quot;&amp;quot;&amp;quot;)
    
    # Забираем контент
    html = page.content()
    
    browser.close()&lt;/pre&gt;
  &lt;h4 id=&quot;KV3h&quot;&gt;Анти-детект: что проверяют сайты (зависит от фантазии создателя)&lt;/h4&gt;
  &lt;p id=&quot;jCd5&quot;&gt;Основное&lt;/p&gt;
  &lt;figure id=&quot;erw2&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/4e/73/4e73ec50-6381-4ab8-b555-e71c7e38d889.png&quot; width=&quot;568&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;wF0M&quot;&gt;Уровень 5 — Мобильные API&lt;/h3&gt;
  &lt;blockquote id=&quot;t3ii&quot;&gt;&lt;em&gt;Приложения часто используют API, недоступные через веб. Данные там чище и полнее.&lt;/em&gt;&lt;/blockquote&gt;
  &lt;p id=&quot;rOhj&quot;&gt;&lt;strong&gt;Инструменты:&lt;/strong&gt;&lt;/p&gt;
  &lt;ul id=&quot;UzEO&quot;&gt;
    &lt;li id=&quot;56Bc&quot;&gt;mitmproxy — перехват HTTPS трафика&lt;/li&gt;
    &lt;li id=&quot;vB4u&quot;&gt;Frida — инъекция кода в приложение&lt;/li&gt;
    &lt;li id=&quot;Ghsm&quot;&gt;Charles Proxy — GUI-альтернатива&lt;/li&gt;
    &lt;li id=&quot;NHWg&quot;&gt;jadx — декомпиляция APK&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;fb7P&quot;&gt;&lt;strong&gt;Процесс:&lt;/strong&gt;&lt;/p&gt;
  &lt;ol id=&quot;eIui&quot;&gt;
    &lt;li id=&quot;tdC5&quot;&gt;Скачай APK (apkpure, apkmirror)&lt;/li&gt;
    &lt;li id=&quot;1WY7&quot;&gt;Декомпилируй (jadx)&lt;/li&gt;
    &lt;li id=&quot;YK5I&quot;&gt;Ищи строки с &amp;quot;api&amp;quot;, &amp;quot;http&amp;quot;, &amp;quot;endpoint&amp;quot;&lt;/li&gt;
    &lt;li id=&quot;qKnM&quot;&gt;Найди базовый URL и параметры&lt;/li&gt;
    &lt;li id=&quot;BXQf&quot;&gt;Перехвати трафик через mitmproxy&lt;/li&gt;
    &lt;li id=&quot;wfFf&quot;&gt;Воспроизведи запрос в Python&lt;/li&gt;
  &lt;/ol&gt;
  &lt;h2 id=&quot;31ho&quot;&gt;&lt;strong&gt;Часть 3: Защита и обход&lt;/strong&gt;&lt;/h2&gt;
  &lt;h3 id=&quot;lkcQ&quot;&gt;Rate Limiting&lt;/h3&gt;
  &lt;p id=&quot;E9wD&quot;&gt;Сайт ограничивает частоту запросов. Виды:&lt;/p&gt;
  &lt;ul id=&quot;Yo3o&quot;&gt;
    &lt;li id=&quot;NSGv&quot;&gt;IP-based → ротация прокси&lt;/li&gt;
    &lt;li id=&quot;19sT&quot;&gt;API Key-based → ротация ключей&lt;/li&gt;
    &lt;li id=&quot;PjYU&quot;&gt;Session-based → ротация сессий&lt;/li&gt;
    &lt;li id=&quot;GOgj&quot;&gt;Cookie-based → сброс куки&lt;/li&gt;
    &lt;li id=&quot;XyCR&quot;&gt;Device fingerprint → подмена отпечатка&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;0YE5&quot;&gt;&lt;strong&gt;Подход:&lt;/strong&gt;&lt;/p&gt;
  &lt;pre id=&quot;nLU3&quot; data-lang=&quot;python&quot;&gt;import time
import random
from itertools import cycle

class RateLimiter:
    def __init__(self, min_delay=1.0, max_delay=3.0):
        self.min_delay = min_delay
        self.max_delay = max_delay
    
    def wait(self):
        delay = random.uniform(self.min_delay, self.max_delay)
        time.sleep(delay)

# Использование прокси
proxies = cycle([
    &amp;quot;http://proxy1:port&amp;quot;,
    &amp;quot;http://proxy2:port&amp;quot;,
    &amp;quot;http://proxy3:port&amp;quot;,
])

limiter = RateLimiter(1, 3)

for url in urls:
    proxy = next(proxies)
    limiter.wait()
    
    response = requests.get(url, proxiДл&amp;quot;: proxy})
    # Дальше ваша логика...&lt;/pre&gt;
  &lt;h3 id=&quot;3IjE&quot;&gt;Теперь главный враг автоматизации CAPTCHA&lt;/h3&gt;
  &lt;blockquote id=&quot;Gl6m&quot;&gt;&lt;em&gt;CAPTCHA можно проходить самыми разными способами от солвером до обучения локальных ИИ-моделей&lt;/em&gt;&lt;/blockquote&gt;
  &lt;figure id=&quot;iVzH&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/a1/8f/a18f63fb-34f4-4190-8108-80aa66a9d61f.png&quot; width=&quot;560&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Eu6N&quot;&gt;&lt;strong&gt;Интеграция с сервисом решения:&lt;/strong&gt;&lt;/p&gt;
  &lt;pre id=&quot;F9mf&quot; data-lang=&quot;python&quot;&gt;import requests
import time

API_KEY = &amp;quot;your_2captcha_key&amp;quot;

def solve_recaptcha(site_key, page_url):
    # Отправляем задачу
    response = requests.post(&amp;quot;https://api.2captcha.com/in.php&amp;quot;, data={
        &amp;quot;key&amp;quot;: API_KEY,
        &amp;quot;method&amp;quot;: &amp;quot;userrecaptcha&amp;quot;,
        &amp;quot;googlekey&amp;quot;: site_key,
        &amp;quot;pageurl&amp;quot;: page_url,
        &amp;quot;json&amp;quot;: 1
    })
    task_id = response.json()[&amp;quot;request&amp;quot;]
    
    # Ждём решение
    for _ in range(30):
        time.sleep(5)
        result = requests.get(&amp;quot;https://api.2captcha.com/res.php&amp;quot;, params={
            &amp;quot;key&amp;quot;: API_KEY,
            &amp;quot;action&amp;quot;: &amp;quot;get&amp;quot;,
            &amp;quot;id&amp;quot;: task_id,
            &amp;quot;json&amp;quot;: 1
        })
        if result.json()[&amp;quot;status&amp;quot;] == 1:
            return result.json()[&amp;quot;request&amp;quot;]
    
    return None&lt;/pre&gt;
  &lt;h2 id=&quot;7qzo&quot;&gt;Часть 4: Инфраструктура&lt;/h2&gt;
  &lt;h3 id=&quot;7ul7&quot;&gt;Прокси — виды и стратегии&lt;/h3&gt;
  &lt;p id=&quot;jMAn&quot;&gt;1. HTTP прокси → обычные, для http запросов&lt;br /&gt;2. SOCKS5 прокси → любой трафик&lt;br /&gt;3. Резидентные → IP реальных людей (дорого, надёжно)&lt;br /&gt;4. Датацентровые → IP серверов (дёшево, палевно)&lt;br /&gt;5. Мобильные → IP мобильных операторов (самые надёжные)&lt;br /&gt;6. Ротация → новый IP на каждый запрос&lt;br /&gt;7. Стик → IP привязан к сессии&lt;/p&gt;
  &lt;p id=&quot;4H8t&quot;&gt;&lt;strong&gt;Правильная ротация:&lt;/strong&gt;&lt;/p&gt;
  &lt;pre id=&quot;ylJz&quot; data-lang=&quot;python&quot;&gt;import aiohttp
import asyncio
from itertools import cycle

class ProxyPool:
    def __init__(self, proxy_list):
        self.proxies = cycle(proxy_list)
        self.failed = set()
    
    def get_proxy(self):
        for _ in range(len(self.proxies)):
            proxy = next(self.proxies)
            if proxy not in self.failed:
                return proxy
        # Все прокси умерли — сбрасываем
        self.failed.clear()
        return next(self.proxies)
    
    def mark_failed(self, proxy):
        self.failed.add(proxy)

async def fetch(session, url, proxy):
    try:
        async with session.get(url, proxy=proxy, timeout=10) as resp:
            return await resp.json()
    except:
        pool.mark_failed(proxy)
        return None

async def main(urls):
    pool = ProxyPool([
        &amp;quot;http://proxy1:8080&amp;quot;,
        &amp;quot;http://proxy2:8080&amp;quot;,
    ])
    
    connector = aiohttp.TCPConnector(limit=20)
    async with aiohttp.ClientSession(connector=connector) as session:
        tasks = []
        for url in urls:
            proxy = pool.get_proxy()
            tasks.append(fetch(session, url, proxy))
        
        results = await asyncio.gather(*tasks)
    return [r for r in results if r is not None]&lt;/pre&gt;
  &lt;h2 id=&quot;Pt2p&quot;&gt;Часть 5: Практические схемы&lt;/h2&gt;
  &lt;h3 id=&quot;wCHs&quot;&gt;Схема 1: Сбор данных с каталога&lt;/h3&gt;
  &lt;pre id=&quot;vBbj&quot; data-lang=&quot;python&quot;&gt;from playwright.sync_api import sync_playwright
from bs4 import BeautifulSoup
import json
import time
import random

class CatalogParser:
    def __init__(self, base_url):
        self.base_url = base_url
        self.results = []
    
    def parse_page(self, page, url):
        page.goto(url)
        page.wait_for_load_state(&amp;quot;networkidle&amp;quot;)
        
        # Ждём появления товаров
        page.wait_for_selector(&amp;quot;.product-card&amp;quot;, timeout=10000)
        
        html = page.content()
        soup = BeautifulSoup(html, &amp;quot;html.parser&amp;quot;)
        
        products = soup.select(&amp;quot;.product-card&amp;quot;)
        
        for product in products:
            self.results.append({
                &amp;quot;title&amp;quot;: product.select_one(&amp;quot;.title&amp;quot;)?.text.strip(),
                &amp;quot;price&amp;quot;: product.select_one(&amp;quot;.price&amp;quot;)?.text.strip(),
                &amp;quot;url&amp;quot;: product.select_one(&amp;quot;a&amp;quot;)?.get(&amp;quot;href&amp;quot;),
                &amp;quot;parsed_at&amp;quot;: time.strftime(&amp;quot;%Y-%m-%d %H:%M&amp;quot;)
            })
        
        # Случайная задержка
        time.sleep(random.uniform(2, 5))
    
    def run(self, pages=10):
        with sync_playwright() as p:
            browser = p.chromium.launch(headless=True)
            page = browser.new_page()
            
            for i in range(1, pages + 1):
                url = f&amp;quot;{self.base_url}?page={i}&amp;quot;
                print(f&amp;quot;Страница {i}/{pages}&amp;quot;)
                self.parse_page(page, url)
            
            browser.close()
        
        return self.results
    
    def save(self, filename=&amp;quot;data.json&amp;quot;):
        with open(filename, &amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
            json.dump(self.results, f, ensure_ascii=False, indent=2)

# Использование
parser = CatalogParser(&amp;quot;https://example.com/catalog&amp;quot;)
data = parser.run(pages=50)
parser.save()&lt;/pre&gt;
  &lt;h3 id=&quot;KwuT&quot;&gt;&lt;br /&gt;Схема 2: Мониторинг изменений&lt;/h3&gt;
  &lt;pre id=&quot;JUvg&quot; data-lang=&quot;python&quot;&gt;import hashlib
import json
from datetime import datetime
from pathlib import Path

class ChangeMonitor:
    def __init__(self, state_file=&amp;quot;state.json&amp;quot;):
        self.state_file = Path(state_file)
        self.state = self.load_state()
    
    def load_state(self):
        if self.state_file.exists():
            return json.loads(self.state_file.read_text())
        return {}
    
    def save_state(self):
        self.state_file.write_text(
            json.dumps(self.state, ensure_ascii=False, indent=2)
        )
    
    def get_hash(self, data):
        return hashlib.md5(json.dumps(data, sort_keys=True).encode()).hexdigest()
    
    def check(self, key, new_data):
        new_hash = self.get_hash(new_data)
        old_hash = self.state.get(key, {}).get(&amp;quot;hash&amp;quot;)
        
        if old_hash is None:
            self.state[key] = {&amp;quot;hash&amp;quot;: new_hash, &amp;quot;data&amp;quot;: new_data, &amp;quot;updated&amp;quot;: datetime.now().isoformat()}
            return {&amp;quot;status&amp;quot;: &amp;quot;new&amp;quot;, &amp;quot;data&amp;quot;: new_data}
        
        elif old_hash != new_hash:
            old = self.state[key][&amp;quot;data&amp;quot;]
            self.state[key] = {&amp;quot;hash&amp;quot;: new_hash, &amp;quot;data&amp;quot;: new_data, &amp;quot;updated&amp;quot;: datetime.now().isoformat()}
            return {&amp;quot;status&amp;quot;: &amp;quot;changed&amp;quot;, &amp;quot;old&amp;quot;: old, &amp;quot;new&amp;quot;: new_data}
        
        return {&amp;quot;status&amp;quot;: &amp;quot;unchanged&amp;quot;}&lt;/pre&gt;
  &lt;p id=&quot;kKWH&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;Ox4c&quot;&gt;Схема 3: Распределённый парсер&lt;/h3&gt;
  &lt;pre id=&quot;7RC2&quot; data-lang=&quot;python&quot;&gt;import asyncio
import aiohttp
from dataclasses import dataclass, field
from typing import Optional
import json

@dataclass
class Task:
    url: str
    retries: int = 0
    max_retries: int = 3
    proxy: Optional[str] = None

@dataclass
class Worker:
    id: int
    session: aiohttp.ClientSession
    queue: asyncio.Queue
    results: list = field(default_factory=list)

class DistributedParser:
    def __init__(self, urls, num_workers=5, proxy_list=None):
        self.tasks = [Task(url=url) for url in urls]
        self.num_workers = num_workers
        self.proxy_list = proxy_list or []
        self.results = []
    
    async def worker(self, wid, queue, session):
        while True:
            try:
                task = await asyncio.wait_for(queue.get(), timeout=5)
            except asyncio.TimeoutError:
                break
            
            try:
                proxy = None
                if self.proxy_list:
                    proxy = self.proxy_list[task.retries % len(self.proxy_list)]
                
                async with session.get(
                    task.url, 
                    proxy=proxy,
                    timeout=aiohttp.ClientTimeout(total=15)
                ) as resp:
                    if resp.status == 200:
                        data = await resp.text()
                        self.results.append({
                            &amp;quot;url&amp;quot;: task.url,
                            &amp;quot;data&amp;quot;: data,
                            &amp;quot;worker&amp;quot;: wid
                        })
                        print(f&amp;quot;[Worker {wid}] OK: {task.url}&amp;quot;)
                    else:
                        raise Exception(f&amp;quot;Status {resp.status}&amp;quot;)
                        
            except Exception as e:
                task.retries += 1
                if task.retries &amp;lt; task.max_retries:
                    await queue.put(task)
                else:
                    print(f&amp;quot;[Worker {wid}] FAILED: {task.url} - {e}&amp;quot;)
            
            finally:
                queue.task_done()
    
    async def run(self):
        queue = asyncio.Queue()
        for task in self.tasks:
            queue.put(task)
        
        connector = aiohttp.TCPConnector(limit=self.num_workers * 2)
        
        async with aiohttp.ClientSession(connector=connector) as session:
            workers = [
                asyncio.create_task(self.worker(i, queue, session))
                for i in range(self.num_workers)
            ]
            await queue.join()
            for w in workers:
                w.cancel()
        
        return self.results

# Использование
urls = [f&amp;quot;https://example.com/item/{i}&amp;quot; for i in range(1000)]
parser = DistributedParser(
    urls=urls, 
    num_workers=10,
    proxy_list=[&amp;quot;http://p1:8080&amp;quot;, &amp;quot;http://p2:8080&amp;quot;, &amp;quot;http://p3:8080&amp;quot;]
)
results = asyncio.run(parser.run())&lt;/pre&gt;
  &lt;p id=&quot;ocw4&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;TLlR&quot;&gt;Часть 6: Стек инструментов&lt;/h2&gt;
  &lt;h3 id=&quot;GVK4&quot;&gt;Обязательный минимум&lt;/h3&gt;
  &lt;ul id=&quot;h0lH&quot;&gt;
    &lt;li id=&quot;lRso&quot;&gt;requests — базовые HTTP запросы&lt;/li&gt;
    &lt;li id=&quot;TFKA&quot;&gt;httpx — async HTTP, HTTP/2&lt;/li&gt;
    &lt;li id=&quot;bU0M&quot;&gt;beautifulsoup4 — парсинг HTML&lt;/li&gt;
    &lt;li id=&quot;0Ioc&quot;&gt;lxml — быстрый парсер&lt;/li&gt;
    &lt;li id=&quot;6gg4&quot;&gt;playwright — headless браузер&lt;/li&gt;
    &lt;li id=&quot;Nx7q&quot;&gt;scrapy — фреймворк для масштабных проектов&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h3 id=&quot;sjxN&quot;&gt;Стань лучше:&lt;/h3&gt;
  &lt;ul id=&quot;a3A4&quot;&gt;
    &lt;li id=&quot;sXnk&quot;&gt;curl_cffi — имитация TLS fingerprint браузера&lt;/li&gt;
    &lt;li id=&quot;YMnl&quot;&gt;tls_client — обход Cloudflare через TLS&lt;/li&gt;
    &lt;li id=&quot;uGab&quot;&gt;camoufox — анти-детект Firefox&lt;/li&gt;
    &lt;li id=&quot;noed&quot;&gt;undetected-chromedriver — анти-детект Chrome&lt;/li&gt;
    &lt;li id=&quot;d8dJ&quot;&gt;aiohttp — async для скорости&lt;/li&gt;
    &lt;li id=&quot;Ou9y&quot;&gt;celery — очереди задач&lt;/li&gt;
    &lt;li id=&quot;I0JW&quot;&gt;redis — кэш и состояние&lt;/li&gt;
    &lt;li id=&quot;gL8X&quot;&gt;postgresql — хранилище данных&lt;/li&gt;
    &lt;li id=&quot;49kW&quot;&gt;docker — изоляция окружения (на уровне базовый минимум)&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h3 id=&quot;ObOv&quot;&gt;Инфраструктура (Может не подойти)&lt;/h3&gt;
  &lt;p id=&quot;YdrV&quot;&gt;Прокси-провайдеры:&lt;/p&gt;
  &lt;ul id=&quot;dXNG&quot;&gt;
    &lt;li id=&quot;0moO&quot;&gt;Bright Data (резидентные)&lt;/li&gt;
    &lt;li id=&quot;6xwK&quot;&gt;SmartProxy (резидентные)&lt;/li&gt;
    &lt;li id=&quot;ghsl&quot;&gt;Proxy-Seller (датацентровые)&lt;/li&gt;
    &lt;li id=&quot;tC7c&quot;&gt;IPRoyal (мобильные)&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;2c5N&quot;&gt;Решение капчи:&lt;/p&gt;
  &lt;ul id=&quot;CHP5&quot;&gt;
    &lt;li id=&quot;cVeA&quot;&gt;2Captcha&lt;/li&gt;
    &lt;li id=&quot;laYk&quot;&gt;Anti-Captcha&lt;/li&gt;
    &lt;li id=&quot;aEB2&quot;&gt;CapSolver&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;plNt&quot;&gt;Мониторинг:&lt;/p&gt;
  &lt;ul id=&quot;To3q&quot;&gt;
    &lt;li id=&quot;9NZM&quot;&gt;Grafana + Prometheus&lt;/li&gt;
    &lt;li id=&quot;y1ji&quot;&gt;Telegram-бот алерты&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2 id=&quot;l4Hw&quot;&gt;Часть 7: Как НЕ надо&lt;/h2&gt;
  &lt;ul id=&quot;Qhwq&quot;&gt;
    &lt;li id=&quot;wPeJ&quot;&gt;ДДОСить сайт парсером (1000 req/sec)&lt;/li&gt;
    &lt;li id=&quot;H4ih&quot;&gt;Игнорировать ошибки и повторять бесконечно&lt;/li&gt;
    &lt;li id=&quot;rtx2&quot;&gt;Хранить данные без структуры&lt;/li&gt;
    &lt;li id=&quot;8lxb&quot;&gt;Не логировать — не понимаешь что происходит&lt;/li&gt;
    &lt;li id=&quot;ZLtW&quot;&gt;Хардкодить селекторы без фоллбеков&lt;/li&gt;
    &lt;li id=&quot;M66j&quot;&gt;Не обрабатывать изменение вёрстк&lt;/li&gt;
    &lt;li id=&quot;F0HI&quot;&gt;Собирать всё подряд без цели&lt;/li&gt;
    &lt;li id=&quot;rZ9Y&quot;&gt;Не кэшировать — один и тот же запрос 50 раз&lt;/li&gt;
    &lt;li id=&quot;NCWG&quot;&gt;Писать всё в один файл без модульности&lt;/li&gt;
    &lt;li id=&quot;X0Ml&quot;&gt;Игнорировать robots.txt и уважение к серверу&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;ZfAs&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;kx2b&quot;&gt;&lt;/p&gt;
  &lt;blockquote id=&quot;QOtz&quot;&gt;&lt;em&gt;На этом все с вами был Vatican.&lt;/em&gt;&lt;/blockquote&gt;

</content></entry></feed>