
Python (по-русски говорят «питон» или «пайтон») — язык программирования общего назначения с простым синтаксисом и большой стандартной библиотекой. На нём пишут веб-сервисы, анализ данных и нейросети, а владельцу сайта он полезен короткими скриптами: 15–40 строк проверяют коды ответа страниц, срок SSL-сертификата, DNS-записи и битые ссылки.
Что такое Python простыми словами
Python — это язык, на котором программу читают почти как текст: блоки выделяются отступами, а не фигурными скобками, переменные не нужно объявлять заранее, типы определяются во время работы. Программа на Python не компилируется в отдельный exe-файл: её исполняет интерпретатор — та самая программа python, которую вы устанавливаете с python.org. Написали файл check.py, запустили командой python check.py — получили результат.
Для автоматизации проверок сайта у Python три сильные стороны.
- Стандартная библиотека. Модули
ssl,socket,urllib,json,csv,html.parserидут вместе с интерпретатором. Срок сертификата или разбор HTML-страницы не требуют ничего устанавливать. - Каталог пакетов PyPI. Если стандартной библиотеки мало, команда
pip installставит готовое:requestsдля HTTP,dnspythonдля DNS-запросов любого типа. - Кроссплатформенность. Один и тот же скрипт работает на Windows, macOS и Linux-сервере, где его удобно запускать по расписанию.
Где используют Python
Бэкенд сайтов (Django, Flask, FastAPI), анализ данных и отчёты (pandas), машинное обучение и нейросети (PyTorch, TensorFlow), системное администрирование и DevOps-скрипты, парсинг сайтов, тестирование. Для владельца сайта самое практичное — последний пункт из этого ряда: быстрые проверки, которые иначе делают руками в браузере по одной странице.
Как установить Python и запустить первый скрипт
Windows
Скачайте установщик со страницы python.org/downloads. На первом экране классического установщика поставьте галочку Add python.exe to PATH — без неё команда python в консоли не найдётся. На странице загрузок для Windows может предлагаться и новый менеджер установки Python install manager; с ним команды py и python настраиваются при установке.
Проверка в cmd или PowerShell:
py --version
python --version
Если вместо версии открывается Microsoft Store, сработал системный псевдоним-заглушка. Его отключают в «Параметры → Приложения → Дополнительные параметры приложений → Псевдонимы выполнения приложений» (выключить python.exe и python3.exe) либо просто пользуются командой py.
macOS
Есть два пути: установщик с python.org или Homebrew (brew install python). После установщика с python.org обязательно запустите файл Install Certificates.command из папки «Программы → Python 3.x». Без этого шага модули ssl и urllib падают на любом HTTPS-сайте с ошибкой CERTIFICATE_VERIFY_FAILED ... unable to get local issuer certificate — мы поймали её, пока проверяли скрипты для этой статьи. Библиотека requests при этом работает, потому что носит свой набор корневых сертификатов (пакет certifi).
Linux
В большинстве дистрибутивов Python 3 уже стоит. Не хватает обычно модулей venv и pip:
# Debian, Ubuntu
sudo apt install python3 python3-venv python3-pip
# Fedora, RHEL, AlmaLinux
sudo dnf install python3 python3-pip
Первый скрипт
Создайте файл hello.py в любом редакторе (удобно в VS Code — о нём отдельный разбор):
import sys
print("Python", sys.version)
Запуск: python3 hello.py на macOS и Linux, py hello.py на Windows. Если увидели номер версии — всё готово.
pip и venv: как ставить библиотеки
pip — установщик пакетов из PyPI. venv — виртуальное окружение: папка проекта со своей копией интерпретатора и своими библиотеками. Окружение нужно, чтобы пакеты одного проекта не ломали другие и системный Python. Для скриптов из этой статьи хватит одного окружения:
# macOS и Linux
python3 -m venv .venv
source .venv/bin/activate
pip install requests dnspython
# Windows, cmd
py -m venv .venv
.venv\Scripts\activate.bat
pip install requests dnspython
# Windows, PowerShell
.venv\Scripts\Activate.ps1
Если PowerShell отказывается запускать Activate.ps1 из-за политики выполнения, разрешите локальные скрипты для своей учётной записи: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.
В свежих Debian и Ubuntu команда pip install вне окружения завершается ошибкой error: externally-managed-environment. Это не поломка, а защита системного Python по PEP 668: ставьте пакеты в venv, как выше. Подробности — в документации модуля venv.
Синтаксис Python: что значит в коде ниже
Чтобы читать скрипты из следующего раздела, достаточно знать десяток конструкций. Частые вопросы «что значит в питоне» обычно как раз про них.
| Запись | Что значит | Пример |
|---|---|---|
# | Комментарий до конца строки, интерпретатор его пропускает | # проверяем SSL |
= и == | Присваивание и сравнение на равенство | days = 30, if code == 200: |
!=, >=, < | Не равно, больше или равно, меньше | if status >= 400: |
// и % | Целочисленное деление и остаток | seconds // 86400 — целые сутки |
f"..." | f-строка: подставляет значения выражений в фигурных скобках | f"{host}: {days} дн." |
: и отступ | Начало блока; всё, что сдвинуто на 4 пробела, входит в блок | for url in URLS: |
with | Открывает ресурс и гарантированно закрывает его, даже при ошибке | with socket.create_connection(...) as sock: |
try / except | Перехват ошибок: скрипт не падает, а сообщает, что случилось | except requests.RequestException: |
[ ], { } | Список и словарь (или множество) | {"q": host} |
sys.exit(1) | Завершить скрипт с кодом ошибки — его видят cron и планировщик | sys.exit(1 if failed else 0) |
Python для владельца сайта: 7 скриптов проверки
Все примеры ниже запускались на Python 3.13 с requests 2.34 и dnspython 2.8; нужен Python 3.10 или новее. В каждом есть таймаут и обработка ошибок: без timeout библиотека requests может ждать ответа сервера бесконечно, и зависший скрипт в расписании хуже, чем упавший. Скрипты возвращают код выхода 1, если нашли проблему, — это пригодится для запуска по расписанию. Какие ещё пункты стоит проверять на сайте, собрано в техническом чек-листе проверки сайта на ошибки.
1. Проверить код ответа списка URL
Самая частая задача: у вас список важных страниц — главная, каталог, корзина, форма заявки — и нужно убедиться, что все отвечают 200, а не 404 или 500.
import sys
import requests
URLS = [
"https://example.com/",
"https://example.com/no-such-page",
"https://www.python.org/",
]
HEADERS = {"User-Agent": "site-check/1.0 (+admin@example.com)"}
failed = 0
for url in URLS:
try:
r = requests.get(url, headers=HEADERS, timeout=10, allow_redirects=False)
note = r.headers.get("Location", "")
print(f"{r.status_code} {url} {note}")
if r.status_code >= 400:
failed += 1
except requests.RequestException as e:
print(f"ERR {url} {type(e).__name__}: {e}")
failed += 1
sys.exit(1 if failed else 0)
Параметр allow_redirects=False здесь намеренный: скрипт показывает код самой страницы, а для 301 и 302 — адрес из заголовка Location. Иначе requests молча пройдёт редирект, и вы увидите 200 конечной страницы. Заголовок User-Agent с контактом нужен потому, что часть сайтов режет запросы со стандартной подписью python-requests. Что означает каждый код, есть в справочнике HTTP-кодов ответа.
2. Узнать срок действия SSL-сертификата
Модули ssl и socket из стандартной библиотеки, ничего ставить не нужно:
import socket
import ssl
import sys
import time
HOSTS = ["example.com", "python.org", "expired.badssl.com"]
WARN_DAYS = 14
def days_left(host, port=443):
ctx = ssl.create_default_context()
with socket.create_connection((host, port), timeout=10) as sock:
with ctx.wrap_socket(sock, server_hostname=host) as tls:
cert = tls.getpeercert()
expires = ssl.cert_time_to_seconds(cert["notAfter"])
return int((expires - time.time()) // 86400), cert["notAfter"]
problems = 0
for host in HOSTS:
try:
days, not_after = days_left(host)
mark = "OK " if days > WARN_DAYS else "WARN"
print(f"{mark} {host}: {days} дн. (до {not_after})")
if days <= WARN_DAYS:
problems += 1
except ssl.SSLCertVerificationError as e:
print(f"FAIL {host}: сертификат не прошёл проверку: {e.verify_message}")
problems += 1
except (OSError, ssl.SSLError) as e:
print(f"ERR {host}: {type(e).__name__}: {e}")
problems += 1
sys.exit(1 if problems else 0)
Результат на момент проверки:
OK example.com: 31 дн. (до Oct 27 22:17:21 2026 GMT)
OK python.org: 140 дн. (до Feb 14 13:03:45 2027 GMT)
FAIL expired.badssl.com: сертификат не прошёл проверку: certificate has expired
Обратите внимание на третью строку. Контекст ssl.create_default_context() проверяет сертификат при рукопожатии, поэтому просроченный, самоподписанный или выданный на другое имя сертификат даёт не число дней, а исключение SSLCertVerificationError. Для мониторинга это правильное поведение: сайт с таким сертификатом браузер тоже не откроет без предупреждения. Отключать проверку (CERT_NONE) ради числа не нужно — тогда getpeercert() вернёт пустой словарь. Ручные способы проверки сертификата описаны в пошаговом руководстве по SSL.
3. Получить DNS-записи домена
Адреса A и AAAA отдаёт стандартная функция socket.getaddrinfo. Для MX, TXT и NS нужна библиотека dnspython (pip install dnspython, импортируется как dns):
import socket
import sys
import dns.exception
import dns.resolver
DOMAIN = sys.argv[1] if len(sys.argv) > 1 else "example.com"
# A и AAAA — стандартной библиотекой, через системный резолвер
try:
addrs = {info[4][0] for info in socket.getaddrinfo(DOMAIN, 443, proto=socket.IPPROTO_TCP)}
print("A/AAAA:", ", ".join(sorted(addrs)))
except socket.gaierror as e:
print("A/AAAA: не резолвится:", e)
# MX, TXT, NS — через dnspython (pip install dnspython)
resolver = dns.resolver.Resolver() # берёт DNS-серверы из настроек системы
# resolver.nameservers = ["8.8.8.8"] # или спросить конкретный публичный резолвер
for rtype in ("MX", "TXT", "NS"):
try:
answer = resolver.resolve(DOMAIN, rtype, lifetime=5)
for rdata in answer:
print(f"{rtype}: {rdata.to_text()}")
except dns.resolver.NXDOMAIN:
print(f"{rtype}: домен не существует")
break
except dns.resolver.NoAnswer:
print(f"{rtype}: записей такого типа нет")
except dns.exception.DNSException as e:
print(f"{rtype}: ошибка запроса: {type(e).__name__}")
Запуск: python3 dns_check.py example.com. Разница исключений важна: NXDOMAIN значит, что домена нет вовсе, NoAnswer — домен есть, но записей такого типа у него нет, таймаут — не ответил DNS-сервер. Функция getaddrinfo спрашивает системный резолвер, а значит, учитывает файл hosts и локальный кэш. Если нужен «чистый» ответ из интернета, раскомментируйте строку с nameservers и укажите публичный резолвер. API dnspython описан в официальной документации.
4. Найти битые ссылки на странице
Разбор HTML — встроенным html.parser, проверка ссылок — через requests:
import sys
from html.parser import HTMLParser
from urllib.parse import urljoin, urldefrag, urlparse
import requests
PAGE = sys.argv[1] if len(sys.argv) > 1 else "https://www.python.org/"
HEADERS = {"User-Agent": "site-check/1.0 (+admin@example.com)"}
LIMIT = 100
class LinkParser(HTMLParser):
def __init__(self):
super().__init__()
self.links = []
def handle_starttag(self, tag, attrs):
if tag == "a":
href = dict(attrs).get("href")
if href:
self.links.append(href)
def check(session, url):
try:
r = session.head(url, timeout=10, allow_redirects=True)
if r.status_code in (403, 405, 501):
# часть серверов не любит HEAD — переспрашиваем GET без скачивания тела
r = session.get(url, timeout=10, allow_redirects=True, stream=True)
r.close()
return r.status_code
except requests.RequestException as e:
return type(e).__name__
with requests.Session() as s:
s.headers.update(HEADERS)
page = s.get(PAGE, timeout=15)
page.raise_for_status()
parser = LinkParser()
parser.feed(page.text)
urls = []
for href in parser.links:
absolute = urldefrag(urljoin(page.url, href)).url
if urlparse(absolute).scheme in ("http", "https") and absolute not in urls:
urls.append(absolute)
print(f"Ссылок на странице: {len(urls)}, проверяю первые {min(len(urls), LIMIT)}")
for url in urls[:LIMIT]:
status = check(s, url)
if not isinstance(status, int) or status >= 400:
print(f"BROKEN {status} {url}")
Скрипт собирает все <a href>, превращает относительные адреса в абсолютные через urljoin, отбрасывает якоря, mailto: и tel:. Ссылки проверяются запросом HEAD: он не скачивает тело страницы. Учтите, что requests.head() по умолчанию не ходит по редиректам, поэтому allow_redirects=True указан явно. Часть серверов на HEAD отвечает 403 или 405, для них скрипт повторяет запрос через GET.
На странице python.org скрипт нашёл одну «битую» ссылку — BROKEN 999 на LinkedIn. Это не поломка: LinkedIn отвечает нестандартным кодом 999 на запросы, похожие на автоматические. Такие домены удобно держать в списке исключений. Лимит в 100 ссылок и один поток — чтобы не нагружать чужие серверы.
5. Выгрузить title и description страниц в CSV
Для SEO-проверки удобно получить таблицу: адрес, код ответа, title, description и их длину.
import csv
import sys
from html.parser import HTMLParser
import requests
URLS = ["https://example.com/", "https://www.python.org/"]
HEADERS = {"User-Agent": "site-check/1.0 (+admin@example.com)"}
class MetaParser(HTMLParser):
def __init__(self):
super().__init__()
self.title = ""
self.description = ""
self._in_title = False
def handle_starttag(self, tag, attrs):
a = dict(attrs)
if tag == "title":
self._in_title = True
elif tag == "meta" and (a.get("name") or "").lower() == "description":
self.description = (a.get("content") or "").strip()
def handle_endtag(self, tag):
if tag == "title":
self._in_title = False
def handle_data(self, data):
if self._in_title:
self.title += data
writer = csv.writer(sys.stdout)
writer.writerow(["url", "status", "title", "title_len", "description", "desc_len"])
for url in URLS:
try:
r = requests.get(url, headers=HEADERS, timeout=15)
if "charset" not in r.headers.get("Content-Type", "").lower():
r.encoding = r.apparent_encoding # иначе cp1251 превратится в кракозябры
p = MetaParser()
p.feed(r.text)
title = " ".join(p.title.split())
writer.writerow([url, r.status_code, title, len(title), p.description, len(p.description)])
except requests.RequestException as e:
writer.writerow([url, type(e).__name__, "", 0, "", 0])
Запуск с сохранением в файл: python3 meta_export.py > meta.csv. Файл открывается в Excel или Google Таблицах. Строка с apparent_encoding нужна для старых сайтов в кодировке windows-1251: если сервер не указал charset в заголовке Content-Type, requests считает страницу ISO-8859-1, и кириллица превращается в набор символов вроде «Ð¡Ñ‚». Пустой description в отчёте (как у example.com) — повод его написать.
6. Проверить цепочку редиректов
После переезда на HTTPS или смены структуры URL нужно убедиться, что старые адреса ведут на новые одним шагом и без петель:
import sys
import requests
URL = sys.argv[1] if len(sys.argv) > 1 else "http://python.org"
try:
r = requests.get(URL, timeout=10, allow_redirects=True)
except requests.TooManyRedirects:
sys.exit(f"{URL}: слишком много редиректов — похоже на петлю")
except requests.RequestException as e:
sys.exit(f"{URL}: {type(e).__name__}: {e}")
for step, hop in enumerate(r.history, 1):
print(f"{step}. {hop.status_code} {hop.url} -> {hop.headers.get('Location')}")
print(f"Итог: {r.status_code} {r.url} (переходов: {len(r.history)})")
$ python3 redirects.py http://python.org
1. 301 http://python.org/ -> https://www.python.org/
Итог: 200 https://www.python.org/ (переходов: 1)
Атрибут r.history хранит все промежуточные ответы по порядку. Петлю редиректов requests обрывает исключением TooManyRedirects — по умолчанию после 30 переходов. Какие цепочки считаются нормальными и чем плох 302 вместо 301, разобрано в статье о проверке редиректов сайта.
7. Как работать с API в Python: открытый API enterno
Когда проверять надо со стороннего сервера, а не со своего компьютера, можно обратиться к открытому API enterno.io. Для него не нужен ключ: запрос GET https://enterno.io/api/open/ssl?q=example.com возвращает данные о сертификате. Параметр q — проверяемый хост, format — формат ответа: text, json или csv. Для скрипта удобнее JSON, поэтому указываем его явно:
import sys
import requests
API = "https://enterno.io/api/open/ssl"
def ssl_days(host):
r = requests.get(API, params={"q": host, "format": "json"}, timeout=30)
data = r.json()
if "error" in data:
raise RuntimeError(data["error"])
print("Осталось запросов:", r.headers.get("X-RateLimit-Remaining"))
return data["certificate"]["days_left"]
host = sys.argv[1] if len(sys.argv) > 1 else "example.com"
try:
print(host, ssl_days(host), "дн.")
except (requests.RequestException, ValueError, KeyError, RuntimeError) as e:
sys.exit(f"{host}: {type(e).__name__}: {e}")
$ python3 enterno_ssl.py example.com
Осталось запросов: 29
example.com 31 дн.
Лимит запросов считается на IP-адрес, остаток сервер возвращает в заголовке X-RateLimit-Remaining. При ошибке API отвечает JSON с полем error — например, «Не удалось определить IP хоста» для несуществующего имени, и скрипт это показывает. Текстовый формат удобен прямо в консоли: curl "https://enterno.io/api/open/ssl?q=example.com" | grep days_left. Для регулярных и объёмных проверок есть API с ключом — /api/v4; оба варианта описаны в документации API.
Какой способ проверки выбрать: сравнение
| Задача | Модули | Нужен pip | Типичная ловушка |
|---|---|---|---|
| Коды ответа списка URL | requests | да | Без timeout скрипт может зависнуть; без allow_redirects=False не видно 301 |
| То же без установки пакетов | urllib.request | нет | Коды 4xx и 5xx приходят исключением HTTPError, а не ответом |
| Срок SSL-сертификата | ssl, socket | нет | На macOS после установщика python.org — запустить Install Certificates.command |
| A и AAAA | socket.getaddrinfo | нет | Учитывает hosts и локальный кэш |
| MX, TXT, NS | dnspython | да | Путать NXDOMAIN (нет домена) и NoAnswer (нет записи) |
| Битые ссылки | requests, html.parser | да | HEAD запрещён на части серверов; нестандартные коды вроде 999 |
| Title и description | requests, html.parser, csv | да | Кодировка windows-1251 без charset в заголовке |
| Цепочка редиректов | requests | да | Петля обрывается исключением, его надо ловить |
| Проверка со стороннего сервера | requests + открытый API enterno | да | Лимит на IP; по умолчанию ответ текстом, для JSON — format=json |
Как запускать проверки по расписанию
Проверка полезна, когда выполняется сама. На Linux-сервере добавьте строку в crontab (crontab -e). Указывайте Python из виртуального окружения напрямую — активировать его в cron не нужно:
0 9 * * * /home/user/checks/.venv/bin/python /home/user/checks/ssl_expiry.py >> /home/user/checks/ssl.log 2>&1
На Windows то же делает «Планировщик заданий»: действие «Запуск программы», программа — C:\checks\.venv\Scripts\python.exe, аргумент — путь к скрипту. Коды выхода 0 и 1 из примеров позволяют настроить реакцию: например, в cron цепочкой || команда-оповещения отправлять сообщение только при проблеме. Помните, что скрипт на вашем компьютере проверяет сайт из вашей сети: если упал провайдер или компьютер выключен, проверка молчит.
Как проверить без кода
Если скрипт выдал странный результат, сверьтесь с независимой проверкой со стороны:
- Проверка SSL-сертификата — срок, цепочка, соответствие имени.
- Проверка редиректов — вся цепочка с кодами каждого шага.
- Поиск битых ссылок — без установки Python.
Частые вопросы
Python и питон — это одно и то же?
Да. Python — официальное название, «питон» и «пайтон» — разговорные варианты произношения. Назван язык в честь британского комик-шоу «Летающий цирк Монти Пайтона», а не змеи.
Нужно ли учить Python, чтобы пользоваться этими скриптами?
Нет. Достаточно установить интерпретатор, создать окружение, поставить пакеты и заменить список адресов в начале скрипта на свои. Таблица синтаксиса выше помогает понять, что делает каждая строка.
Чем requests отличается от urllib?
urllib входит в стандартную библиотеку и работает без установки, но код с ним длиннее, а ответы 4xx и 5xx приходят исключением. requests нужно ставить через pip, зато он проще: сессии, история редиректов, удобная работа с JSON.
Почему скрипт на macOS пишет CERTIFICATE_VERIFY_FAILED?
Python с python.org на macOS не подключает системное хранилище сертификатов. Запустите Install Certificates.command из папки «Программы → Python 3.x» или используйте requests, у которого свой набор сертификатов.
Можно ли так проверять чужие сайты?
Одиночные запросы к публичным страницам — да. Не запускайте сотни параллельных запросов к одному серверу: это похоже на атаку и может закончиться блокировкой вашего IP. Уважайте robots.txt и условия использования сайта при сборе данных.