Status: Active personal tool. Issues are kept simple; pull requests are welcome only when they match the current scope.
Проект автоматизирует сбор вакансий с XING, ранжирование и обработку откликов через единый слой XingClient с тестируемыми моками.
Важное ограничение: любой реальный переход по сайту выполняется аккуратно и в ручном режиме там, где это нужно по безопасности.
- Что внутри
- Установка
- Конфигурация
- Запуск тестов
- Запуск через PyCharm с выбором сценариев
- Как работает pipeline XING
- Логирование
- E2E на реальном сайте
- Диагностика и частые вопросы
- Безопасность и секреты
Ключевые модули:
src/xingbot/settings.py— загрузка env и runtime-конфигурации.src/xingbot/xing/config.py— typed policies для HTTP, retry и safety.src/xingbot/xing/http.py— тестопригодный async HTTP-клиент с retry/backoff и rate-limit.src/xingbot/xing/client.py—XingClient, метод-слой для list/collect/apply.src/xingbot/scraping/xing_cards.py— парсинг карточек и деталей вакансий.src/xingbot/scraping/xing_scraper.py— обёртки сценариевscrape_xing_jobs(page, settings)иapply_to_relevant_jobs(page, settings, ...).src/xingbot/csv_store.pyиsrc/xingbot/enums.py— схема и хранениеjob_listings.csv.tests/unit— unit-тесты логики клиента и парсинга.tests/integration— мок-интеграционные тесты черезrespx.tests/e2e/test_xing_e2e.py— заглушка, e2e по умолчанию выключен.scripts/run_xing_tests.py— удобный runner сценариев тестов.scripts/xing_e2e.py— безопасный ручной сценарий с--confirm-send.
- Установи зависимости:
poetry install- Активируй окружение:
.venv\Scripts\Activate.ps1- Подготовь переменные окружения в
.envпо шаблону.env.example.
Copy-Item .env.example .env
notepad .env- Проверь, что
pytestдоступен из интерпретатора проекта:
python -m pytest -qXING_EMAILXING_PASSWORDOPENAI_API_KEY(если используешь шаг оценки резюме/респонса)LOG_LEVEL
XING_URLSXING_HTTP_TIMEOUT_SXING_RETRIESXING_BACKOFF_BASE_SXING_BACKOFF_MAX_SXING_RETRY_STATUSXING_ACTION_INTERVAL_SXING_MAX_ACTIONS_PER_RUNXING_DRY_RUN_DEFAULTXING_RATE_LIMIT_ENABLEDXING_CONFIRM_SEND_DEFAULTXING_PROXYXING_STORAGE_STATE_FILEHEADLESS(если не задан, режим выбирается автоматически: headful при наличии display, иначе headless)XING_USER_AGENT(оставь пустым, чтобы использовать UA браузера по умолчанию)XING_ALLOWED_LANGSFILTER_BY_DESCRIPTION_LANGKEEP_UNKNOWN_LANGRELEVANCE_SCORE_THRESHOLDMAX_SCROLLSMAX_JOBS_COLLECTED
LOG_LEVEL=INFO
HEADLESS=
XING_EMAIL=
XING_PASSWORD=
OPENAI_API_KEY=
XING_URLS=
XING_ALLOWED_LANGS=en,de
FILTER_BY_DESCRIPTION_LANG=true
KEEP_UNKNOWN_LANG=true
RELEVANCE_SCORE_THRESHOLD=8.0
MAX_SCROLLS=40
MAX_JOBS_COLLECTED=500
XING_HTTP_TIMEOUT_S=8.0
XING_RETRIES=3
XING_BACKOFF_BASE_S=0.7
XING_BACKOFF_MAX_S=4.5
XING_RETRY_STATUS=429,500,502,503,504
XING_ACTION_INTERVAL_S=20.0
XING_MAX_ACTIONS_PER_RUN=1
XING_DRY_RUN_DEFAULT=true
XING_RATE_LIMIT_ENABLED=true
XING_CONFIRM_SEND_DEFAULT=false
XING_PROXY=
XING_STORAGE_STATE_FILE=xing_storage_state.json
XING_USER_AGENT=Сбор выполняет collect_jobs, который:
- открывает страницы из
XING_URLSили дефолтного списка, - грузит и нормализует существующий
job_listings.csv, - сохраняет новые карточки (с дедупликацией),
- при возможности добавляет описание и внешний URL,
- обновляет
stats.csvи статусные маркеры.
apply_to_relevant_jobs читает job_listings.csv и:
- фильтрует по статусу и порогу score,
- учитывает
dry-run,max actions,confirm send, - обновляет статус в CSV после каждой попытки,
- не шлёт заявки без подтверждения, если включена ручная валидация.
python main.py --help— список доступных команд.python main.py --log-level DEBUG ...— принудительно включает debug-логирование в консоли.python main.py apply --dry-run— безопасный режим по умолчанию, без реального клика отправки.python main.py apply --no-dry-run --confirm-send— разрешает реальный клик только с подтверждением.python -m xingbot.auto_run --once --dry-run— одиночный безопасный прогон pipeline.
import asyncio
from playwright.async_api import async_playwright
from xingbot.settings import Settings
from xingbot.scraping.xing_scraper import scrape_xing_jobs, apply_to_relevant_jobs
async def main() -> None:
settings = Settings.load()
async with async_playwright() as p:
browser = await p.chromium.launch(headless=settings.headless)
context = await browser.new_context()
page = await context.new_page()
await scrape_xing_jobs(page, settings)
await apply_to_relevant_jobs(
page,
settings,
min_score=8.0,
message="Hello from pipeline",
dry_run=True,
max_actions=1,
)
await browser.close()
asyncio.run(main())pytest -q--scenario unit— толькоtests/unit--scenario integration—tests/integrationс маркеромintegration--scenario e2e—tests/e2e(только если включен флаг, иначе skip)--scenario all— весь каталогtests
python scripts/run_xing_tests.py --scenario unit
python scripts/run_xing_tests.py --scenario integration
python scripts/run_xing_tests.py --scenario all
python scripts/run_xing_tests.py --scenario e2e --enable-e2e- unit- и integration-тесты запускаются быстро и полностью автоматизируемы;
- e2e не включается в стандартный прогон и требует явного разрешения;
- в
tests/e2eсохранён один skip-тест с сигналомXING_E2E_ENABLED=1.
- Открой Run/Debug Configurations
- Добавь конфиг
Python - Script:
scripts/run_xing_tests.py - Python interpreter:
...\\.venv\\Scripts\\python.exe - Working directory:
$PROJECT_DIR$ - Parameters:
--scenario unit - Сохрани как
XING Unit
- Скопируй конфиг
XING Unit - Переименуй в
XING Integration - Замени Parameters на
--scenario integration
- Переименуй ещё один конфиг в
XING All - Parameters:
--scenario all
- Переименуй ещё один конфиг в
XING E2E - Parameters:
--scenario e2e --enable-e2e - Только для ручных запусков
Логер настроен на loguru и читает LOG_LEVEL из окружения.
Доступные уровни:
TRACEDEBUGINFOWARNINGERRORCRITICAL
Формат лога в консоли показывает:
- время,
- уровень,
- модуль и функцию,
- строку,
- сообщение.
LOG_LEVEL=DEBUG
python scripts/run_xing_tests.py --scenario allВ репозитории есть безопасный сценарий в scripts/xing_e2e.py.
- браузер запускается
headful(headless=False); - вход в аккаунт делается вручную;
- без
--confirm-sendдействие не выполняется; - в конце сохраняются скриншоты/HTML в
tests/e2e/artifacts/.
python scripts/xing_e2e.py --job-url "https://www.xing.com/jobs/..."
python scripts/xing_e2e.py --job-url "https://www.xing.com/jobs/..." --confirm-sendЕсли скрипт не отправляет отклики, проверь XING_CONFIRM_SEND_DEFAULT и параметр confirm_send.
Если скрипт не отправляет отклики, проверь XING_MAX_ACTIONS_PER_RUN и XING_ACTION_INTERVAL_S.
Если скрипт не отправляет отклики, проверь, что cookies валидны и login не упал в captcha/2FA.
Если сбор слишком долгий, снизь MAX_SCROLLS.
Если сбор слишком долгий, снизь MAX_JOBS_COLLECTED.
Если сбор слишком долгий, уменьшай время ожиданий.
Если тесты медленные, запускай только нужный сценарий --scenario unit или --scenario integration.
- Файл
.envдолжен содержать только локальные значения и не коммитится. - Для репозитория используем
.env.exampleс пустыми/заглушечными значениями. .gitignoreнастроен на исключениеuser_data,log,debug_artifacts,xing_storage_state.json,*storage_state*.jsonи.env.- На живом сайте не автоматизируются captcha/2FA; такие участки завершаются с понятным сообщением и инструкцией для ручного шага.
- Файл состояния по умолчанию:
xing_storage_state.json(или путь изXING_STORAGE_STATE_FILE). - Формат: JSON Playwright
storage_state(ключиcookies,origins), безpickle. - При сохранении применяется best-effort ужесточение прав файла.
This project is licensed for non-commercial use under the PolyForm Noncommercial License 1.0.0. Commercial use, resale, paid distribution, marketplace publication, SaaS hosting, or bundling into a paid product requires separate written permission from the author. Project names, logos, package identifiers, store listings, screenshots, and other branding assets are not licensed for use in forks or redistributed builds.