Skip to content
Advanced / 4 min read

Как маршрутизировать прокси Dataimpulse через mitmproxy на macOS для веб-скрейпинга

Один локальный слушатель mitmproxy перенаправляет все скраперы, браузеры и эмуляторы на macOS к резидентным или мобильным прокси Dataimpulse через HTTP или SOCKS5, с ротацией, ограничением скорости и опциональной TLS-инспекцией под вашим контролем.

macOS SOCKS5 Веб-скрейпинг Приватность/Анонимность

Обзор

mitmproxy — это программируемый прокси-перехватчик с открытым исходным кодом, работающий как обычный процесс macOS. В upstream-режиме он принимает подключения от локальных клиентов на одном порту (127.0.0.1:8080) и перенаправляет этот трафик на второй прокси — в данном случае на ваш резидентный или мобильный эндпоинт Dataimpulse.

Преимущество — централизация: вы настраиваете прокси один раз, после чего любой браузер, CLI-инструмент, Python-скрипт или мобильный эмулятор на машине можно маршрутизировать через Dataimpulse без редактирования собственных настроек прокси этого инструмента. Всё проходит через единую точку, где можно ограничить параллелизм, отслеживать сбои и подтвердить, какой исходящий IP вы фактически используете.

Dataimpulse хорошо подходит для такого шаблона, потому что предлагает резидентные и мобильные эндпоинты как через HTTP, так и через SOCKS5, с оплатой по факту использования и большим пулом IP — это полезно для импульсивного сбора данных с большим объёмом, где ежемесячные обязательства не нужны. Вы можете сравнить его с другими провайдерами прокси, прежде чем выбирать.

Когда этот подход имеет смысл

Подход Что вы настраиваете Лучше всего подходит для
Upstream-режим mitmproxy (это руководство) Один локальный порт для всех клиентов Несколько инструментов одновременно, централизованное ограничение и логирование
Флаги прокси для каждого инструмента (curl -x, requests proxies=) Каждый инструмент отдельно Один скрипт с минимальным количеством движущихся частей
Десктопные менеджеры прокси (например, Proxifier) Правила для каждого приложения GUI-приложения, игнорирующие системные настройки прокси

Что вы построите

  • Локальный слушатель mitmproxy на 127.0.0.1:8080
  • Upstream-подключение к Dataimpulse через HTTP или SOCKS5
  • Опциональный перехват TLS для конкретных хостов, которые вы хотите инспектировать
  • Добавление для ограничения параллелизма, которое делает нагрузку запросов предсказуемой
  • Рабочие примеры для curl, requests, Playwright и эмулятора Android

Предварительные требования

  • macOS 13 или новее с установленным Homebrew
  • Аккаунт Dataimpulse, а также имя пользователя, пароль, хост шлюза и порты HTTP/SOCKS5, показанные в вашей панели управления
  • Python 3.9 или новее для примеров клиентов (python3 --version)
  • Уверенное владение Терминалом и базовое понимание HTTP-прокси и TLS

Как работает настройка

  1. mitmproxy слушает на 127.0.0.1:8080.
  2. Ваш клиент отправляет запрос в mitmproxy, а не напрямую на целевой сайт.
  3. mitmproxy открывает подключение к шлюзу Dataimpulse.
  4. Dataimpulse назначает исходящий IP из своего пула.
  5. Ответ возвращается через mitmproxy к вашему клиенту.

Важны два режима, и ранний выбор между ними экономит время:

  • Прозрачный проход (туннелирование): mitmproxy пересылает TLS-байты, не расшифровывая их. Никаких изменений сертификатов нигде не требуется. Включается с --ignore-hosts '.*'.
  • Перехват: mitmproxy завершает TLS, чтобы читать или переписывать запросы и ответы. Требует доверия к CA-сертификату mitmproxy в macOS и в каждой клиентской среде выполнения.

Для скрейпинга, которому нужен только другой исходящий IP, прозрачный проход проще и быстрее, и он полностью избегает работы с сертификатами. Включайте перехват только для хостов, которые вам действительно нужно инспектировать.

Шаги

  1. Установите mitmproxy.

    brew install mitmproxy
    mitmdump --version
    
  2. Экспортируйте учётные данные Dataimpulse как переменные окружения, чтобы они никогда не появлялись в истории командной оболочки или в закоммиченных файлах.

    export DI_USER='your-dataimpulse-username'
    export DI_PASS='your-dataimpulse-password'
    export DI_HOST='gateway-host-from-your-dashboard'
    export DI_HTTP_PORT='http-port-from-your-dashboard'
    export DI_SOCKS_PORT='socks5-port-from-your-dashboard'
    export DI_SESSION='scrape-macos-01'
    

    Добавьте те же строки в ~/.zshrc, если хотите, чтобы они были доступны в каждой новой оболочке, и подтвердите хост и порт, не выводя пароль:

    echo "${DI_HOST}:${DI_HTTP_PORT}"
    
  3. Кодируйте учётные данные, содержащие зарезервированные символы, в процентную кодировку. Символы вроде @, :, / или # внутри пароля нарушат upstream-URL. Кодируйте только то значение, которое вы помещаете в URL.

    python3 -c "import os, urllib.parse; print(urllib.parse.quote(os.environ['DI_PASS'], safe=''))"
    
  4. Запустите mitmproxy в upstream-режиме прозрачного прохода через HTTP. В режиме прозрачного прохода mitmproxy туннелирует трафик без расшифровки, поэтому изменения клиентских сертификатов не требуются.

    mitmdump \
      --mode "upstream:http://${DI_USER}:${DI_PASS}@${DI_HOST}:${DI_HTTP_PORT}" \
      --listen-port 8080 \
      --ignore-hosts '.*' \
      --set flow_detail=1
    
  5. Проверьте исходящий IP. Оставьте mitmproxy работать в отдельном окне терминала и проверьте из второго окна.

    curl -s -x http://127.0.0.1:8080 https://api.ipify.org
    echo
    curl -s https://api.ipify.org
    echo
    

    Эти два адреса должны различаться. Если они совпадают, запрос не достигает Dataimpulse — см. раздел устранения неполадок.

  6. Переключите upstream на SOCKS5, когда хотите, чтобы прокси разрешал DNS удалённо, что часто предпочтительнее для геозависимых целей.

    mitmdump \
      --mode "upstream:socks5://${DI_USER}:${DI_PASS}@${DI_HOST}:${DI_SOCKS_PORT}" \
      --listen-port 8080 \
      --ignore-hosts '.*'
    
  7. Включайте перехват только для нужных хостов. --allow-hosts ограничивает расшифровку совпадающими хостами; всё остальное остаётся туннелированным и нетронутым.

    mitmdump \
      --mode "upstream:http://${DI_USER}:${DI_PASS}@${DI_HOST}:${DI_HTTP_PORT}" \
      --listen-port 8080 \
      --allow-hosts 'example\.com' \
      -s ./gate.py
    
  8. Доверьтесь CA-сертификату mitmproxy перед скрейпингом HTTPS-хостов через перехват. Файлы сертификатов создаются при первом запуске mitmproxy.

    # First run generates ~/.mitmproxy/mitmproxy-ca-cert.pem
    mitmdump --listen-port 8081 &
    sleep 2
    kill %1
    
    sudo security add-trusted-cert -d -r trustRoot \
      -k /Library/Keychains/System.keychain \
      ~/.mitmproxy/mitmproxy-ca-cert.pem
    

    Среды выполнения командной строки часто игнорируют связку ключей macOS, поэтому укажите им явный bundle:

    cat "$(python3 -c 'import certifi; print(certifi.where())')" \
      ~/.mitmproxy/mitmproxy-ca-cert.pem > ~/.mitmproxy/ca-bundle.pem
    
    export REQUESTS_CA_BUNDLE=~/.mitmproxy/ca-bundle.pem
    export NODE_EXTRA_CA_CERTS=~/.mitmproxy/mitmproxy-ca-cert.pem
    
  9. Добавьте addon для ограничения параллелизма, чтобы всплеск от быстрого скрапера не перегружал шлюз. Сохраните это как gate.py рядом с каталогом, из которого вы запускаете mitmproxy.

    # gate.py — cap in-flight requests passing through mitmproxy
    import asyncio
    
    from mitmproxy import http
    
    
    class Gate:
        def __init__(self, limit: int = 8) -> None:
            self._limit = limit
            self._sem: asyncio.Semaphore | None = None
    
        def running(self) -> None:
            self._sem = asyncio.Semaphore(self._limit)
    
        async def request(self, flow: http.HTTPFlow) -> None:
            await self._sem.acquire()
            flow.metadata["gate_held"] = True
    
        def _release(self, flow: http.HTTPFlow) -> None:
            if flow.metadata.pop("gate_held", False):
                self._sem.release()
    
        def response(self, flow: http.HTTPFlow) -> None:
            self._release(flow)
    
        def error(self, flow: http.HTTPFlow) -> None:
            self._release(flow)
    
    
    addons = [Gate(limit=8)]
    

    Загрузите его с -s ./gate.py. Обратите внимание, что addons видят только перехваченные потоки: если вы запускаете с --ignore-hosts '.*', подключения туннелируются, и эти хуки никогда не срабатывают. В этом случае ограничивайте параллелизм внутри вашего скрапера.

  10. Направьте свои инструменты на локальный слушатель. Для shell-инструментов экспортируйте стандартные переменные в терминале, из которого вы скрейпите — но не в терминале, где запущен mitmproxy, чтобы избежать прокси-петли.

    export HTTP_PROXY=http://127.0.0.1:8080
    export HTTPS_PROXY=http://127.0.0.1:8080
    export NO_PROXY=localhost,127.0.0.1
    curl -s https://httpbin.org/ip
    

    Для Python-скрипта конфигурация CA не нужна, пока вы остаётесь в режиме прозрачного прохода:

    import requests
    
    LOCAL_PROXY = "http://127.0.0.1:8080"
    proxies = {"http": LOCAL_PROXY, "https": LOCAL_PROXY}
    
    response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=30)
    response.raise_for_status()
    print(response.json())
    

    Для headless-браузера передайте тот же локальный прокси в контекст браузера:

    from playwright.sync_api import sync_playwright
    
    LOCAL_PROXY = "http://127.0.0.1:8080"
    
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True, proxy={"server": LOCAL_PROXY})
        page = browser.new_page()
        page.goto("https://httpbin.org/ip", wait_until="domcontentloaded")
        print(page.inner_text("body"))
        browser.close()
    

    Если вам также нужно скрейпить сервис, работающий на вашей собственной машине, Chromium по умолчанию обходит loopback-адреса. Добавление args=["--proxy-bypass-list=<-loopback>"] в вызов запуска убирает это исключение.

  11. Захват трафика эмулятора (опционально). Мобильные эндпоинты Dataimpulse предоставляют исходящие IP уровня оператора; они сами не захватывают трафик устройства. Чтобы маршрутизировать эмулятор через этот пул, направьте эмулятор на слушатель хоста.

    # Android emulator: 10.0.2.2 is the host machine
    adb shell settings put global http_proxy 10.0.2.2:8080
    adb shell settings get global http_proxy
    
    # Reset when finished
    adb shell settings put global http_proxy :0
    

    Для iOS Simulator откройте Settings, выберите Wi-Fi, затем Configure Proxy, Manual и укажите сервер 127.0.0.1 на порту 8080. Трафик симулятора использует сетевой стек Mac, поэтому для перехвата CA также нужно установить в симуляторе.

  12. Очистите после завершения. Удалите сертификат перехвата и сбросьте все настройки клиентов, чтобы посторонний трафик перестал идти через mitmproxy.

    sudo security delete-certificate -c mitmproxy /Library/Keychains/System.keychain
    unset HTTP_PROXY HTTPS_PROXY
    

Устранение неполадок

407 Proxy Authentication Required

В upstream-URL отсутствуют учётные данные, или зарезервированный символ в пароле нарушил URL. Повторно запустите команду процентного кодирования из шага 3 и пересоберите строку --mode с закодированным значением.

SSLCertVerificationError, CERT_AUTHORITY_INVALID или unable to get local issuer certificate

Вы включили перехват, но клиент не доверяет CA mitmproxy. Установите сертификат, как показано в шаге 8, затем задайте переменные bundle для конкретной среды выполнения (REQUESTS_CA_BUNDLE, NODE_EXTRA_CA_CERTS). Либо вернитесь в режим прозрачного прохода с --ignore-hosts '.*', если вам не нужно читать трафик.

Address already in use

Порт уже занят другим процессом. Найдите его, затем измените --listen-port, а не убивайте посторонние сервисы.

lsof -nP -iTCP:8080 -sTCP:LISTEN

Все запросы зависают или истекают по тайм-ауту

Скорее всего, вы в прокси-петле: HTTP_PROXY экспортирован в той же оболочке, которая запустила mitmproxy, поэтому собственное upstream-подключение mitmproxy отправляется обратно к нему самому. Запустите mitmproxy в чистом терминале или выполните unset HTTP_PROXY HTTPS_PROXY перед его запуском.

Запросы успешны, но исходящий IP никогда не меняется

Режим прозрачного прохода сам по себе ничего не ротирует. Поведение ротации определяется типом сессии, который вы настроили в панели управления, и тем, как ваш клиент открывает подключения. Убедитесь, что вы не закрепляете одну липкую сессию на весь запуск, и проверьте исходящий IP командой из шага 5.

Эмулятор игнорирует прокси

В эмуляторах Android 127.0.0.1 относится к самому эмулятору, а не к Mac. Используйте 10.0.2.2 и подтвердите значение с помощью adb shell settings get global http_proxy.

Цели возвращают 403 или 429

Это ограничение скорости на стороне назначения, а не сбой прокси. Уменьшите лимит gate в gate.py, добавьте задержки между запросами и убедитесь, что ваш скрейпинг соответствует условиям сайта и robots.txt.

Итоги

Теперь у вас есть единый локальный слушатель mitmproxy на macOS, который перенаправляет любой клиент к резидентным или мобильным прокси Dataimpulse через HTTP или SOCKS5, с режимом прозрачного прохода для простой ротации IP и выборочным перехватом для более глубокой инспекции. Учётные данные хранятся в переменных окружения, параллелизм ограничен addon'ом, а доверие к сертификату можно чисто добавить или удалить, когда вам больше не нужно читать зашифрованный трафик.