/
Text
Кирилл Феткулин
Автоматизируй рутину в
разработке и
4 " *
администрировании
Python-
инструментарий:
Автоматизируй
рутину в разработке и
администрировании
Автор: Феткулин Кирилл Ренатович
Оглавление
Введение
Часть I: Фундамент автоматизации
Глава 1: Настройка окружения и первый автоматический скрипт
Глава 2: Мастер работы с файлами и директориями
Глава 3: Общение с системой: процессы и командная строка
Часть II: Автоматизация в разработке (Dev)
Глава 4: Магия регулярных выражений (regex)
Глава 5: Парсим данные: от CSV до JSON и Excel
Глава 6: Веб-скрейпиш: добыча данных из интернета
Часть III: Автоматизация в администрировании (Ops)
Глава 7: Мониторинг системы и отправка уведомлений
Глава 8: Автоматизация по сети: SSH и управление серверами
Глава 9: Взаимодействие с облачными провайдерами (на примере AWS)
Глава 10: Работа с Docker через Python
Часть IV: Профессиональные техники
Глава И: Создание профессиональных CLI-утилит
Глава 12: Дотирование и обработка ошибок
Глава 13: Параллельное и асинхронное выполнение
Глава 14: Упаковка скрипта в Docker-контейнер
Заключение
Приложения
Приложение А: Шпаргалка по основным модулям Python
Приложение Б: Best Practices
Python-инструментарий: Автоматизируй
рутину в разработке и администрировании
Введение
Python прочно вошел в число самых популярных языков
программирования благодаря своей простоте, универсальности и богатой
экосистеме библиотек. Его возможности выходят далеко за рамки веб-
разработки и data science — Python является идеальным инструментом
для автоматизации рутинных задач в разработке и системном
админис!рировании.
Для кого эта книга? Эта книга предназначена для разработчиков,
системных администраторов, DevOps-инженеров и всех специалистов,
которые хотят повысить свою эффективность за счет автоматизации
повторяющихся задач. Мы будем писать скрипты для мониторинга,
деплоя, пестирования и работы с данными.
Что внутри? Книга разделена на четыре логические части:
1 Фундамент автоматизации — настройка окружения, работа с файлами
и командной строкой
2. Автоматизация в разработке — регулярные выражения, парсинг
данных, веб-скрейпинг
3. Автоматизация в администрировании — мониторинг, управление
серверами,облачные провайдеры
4. Профессиональные техники — создание CLI-утилит, легирование,
параллельное выполнение
Как работать с книгой? Для максимальной пользы рекомендуем:
1. Установить Python 3.3 или новее
2. Создать виртуальное окружение для экспериментов
3. Запускать все примеры кода на своей системе
4. Модифицировать примеры под свои задачи
5. Изучать документацию к библиотекам, упомянутым в книге
Часть I: Фундамент автоматизации
Глава 1: Настройка окружения и первый автоматический
скрипт
Правильная настройка окружения — критически важный первый шаг для
эффективной работы с Python. Начнем с установки интерпретатора и
создания виртуального окружения, которое изолирует зависимости
нашего проекта.
Установка Python и создание виртуального окружения
python
# check_python_version.py
import sys
def chcck_python_version()'
..Проверяет версию Python....
required_version = (3, 8)
current_version = sys versionjnfo
if current_version < requircd_version
print(f"Tpe6ye гея Python {required..veision[P]} {required. version[l]} или выше")
рпп^Г'Текущая версия {current version.major} {current_version. minor)
{current version micro} )
sys exit(l)
else:
print(f"BepcHfl Python подходит {current_version major} {current_version minor}
{current version micro}")
if _ name__ == "_main__
check_python_version()
После проверки версии Python создаем виртуальное окружение:
python
# creaLe_venv.py
import os
import sys
import subprocess
import venv
def create_virtual_environment(venv_name=".venv"):
..Создает виртуальное окружение
try:
# Создаем виртуальное окружение
builder - venv Env6uilder(with_pip=True)
builder create(venv_name^
рпп1(ГВиртуальное окружение создано: {venvname}")
# Определяем путь к pip в зависимости от ОС
if os.name == 'nt': # Windows
pip path = os path join(venv_name, "Scripts , "pip.exe')
else: # Linux/Mac
pip path = os path join(venv_name "bin", pip' )
# Устанавливаем базовые пакеты
packages = ["requests", "psutil", "beautifulsoup4", "pandas"]
subprocess run(Fpip_pathJ "install ] + packages. check=True)
print("Sa3OBbie пакеты установлены')
except Exception as c
рпп1(ГОшибка при создании виртуального окружения: {е}")
sys exit(l)
if_name___== "___mam___":
crcate_virtual_environment()
Первый скрипт автоматизации - резервное копирование
Теперь напишем наш первый полезный скрипт — систему резервного
копирования:
python
# backup script.py
import os
import shutil
import datetime
import argparse
import zipfile
import logging
# Настройка вотирования
logging basicConfig(
level = logging INFO,
format='%(asctime)s - %(levelname)s - %(message)s'.
handlers-[
logging FileHandler('backu]j.log ),
logging Stream Handlers)
]
)
def create_backup(source_dir, oackup_dir, compress=True):
Создает резервную копию директории
Args:
source dir (str): Директория для резервного копирования
backup_dir (str): Директория для хранения бэкапов
compress (bool): Сжимать ли бэкап в zip
try:
# Проверяем существование исходной директории
if not os.path.exists(source_dir).
raise ЕПеМо1ЕоипбЕггог(Г'Директория {source_dir} не существует")
# Создаем директорию для бэкапов если её нет
os.makedirs(backup, dir, existok-True)
# Создаем имя файла с timestamp
timestamp = datetime datetime now().strftimc( '%Y%m%d_%H%M%S")
backup name = f"backup_{os.path basename(source_dir)}_{timestamp}
backup_path = os path joim,backup_dir, backup_name)
if compress:
# Создаем zip-архив
backup_path += ".zip"
with zipfile ZipFile(backup path, 'w', zipfile ZIP_DEFLA'rED) as zipf-
for root, dirs files in os.walk(scurce_dir):
for file in files:
file_path = os path join(root, file)
arcname = os path relpath(fiie_path start-source_dir)
zipf write(file_path, arcname)
logging info(f"Co3flaH zip-бэкап {backuppath} )
else:
# Копируем директорию
shutil.copytree(source_dir. backup_path)
logging info(f"Co3flaHa копия директории: {backup_path} )
# Удаляем старые бэкапы (больше 30 дней)
cleanup_old_backups(backup_dir, days=30)
return backup_path
except Exception as e:
logging еггог(Р'Ошибка при создании бэкапа: {е} )
raise
def cleanup old backups(backup_dir days=30):
..Удаляет старые бэкапы.....1
now = datetime datetime nowi)
for item in os listdir[backup_dir):
item_path = os path join(backup_dir item)
if os.path isfilefitem path):
# Для файлов
modtime = datetime datetime fromtimestamplos path getmtime(item_path))
else:
# Для директорий
mod_time = datetime.datetime fromtimestamplos.path getctimelitem path))
if (now mod_time).days > days:
try:
it os.path isfile(itcm_path)'
os remove(item_ path)
else:
shutil.rrntree(itempath)
logging тМГ'Удален старый бэкап: {item_path} )
except Exception as e
logging.error(f"Ouin6Ka при удалении {item_path} -fe} )
def main():
parser = argparse.ArgumentParser(descnption="CKpnnT для создания резервных копий")
parser.add_argument("source", Ье1р="Директория для резервного копирования")
parser add argument -d", destination". default="./backups",
Ье1р="Директория для хранения бэкагсв')
parser add_argument( -z", "-no-zip", action="store_false , dest="compress",
help="He сжимать бэкап в zip ')
args = parser parse_args()
try:
backup_path = create_backuplargs source args.destination, args.compress)
рпп1(Г'Резервная копия успешно создана {backup_path} )
except Exception as e
рппЦТОшибка: {е}")
sys exit(l)
if__name__== "__main__
main()
Этот скрипт демонстрирует несколько важных концепций:
Работу с аргументами командной строки
Обработку ошибок и легирование
Манипуляции с файловой системой
Создание zip-архивов
Организацию кода в функции
Глава 2: Мастер работы с файлами и директориями
В згой главе изучим модули os, shutil и pathlib для эффективной работы с
файловой системой,
Использование модуля os для базовых операций
python
# fHe_operatiorrs.py
import os
import shutil
def expkre_directory(path=' ):
..Исследует содержимое директории....
try:
items = os listdir(path)
рппЦТСодержимое директории {path} )
for item in items
item_path = os.path.join(path, item)
if os.path isfile(item_path):
size = os path getsize(iterr_path)
print(f" □ {item} ({size} байт)")
elif os.path isdir(item_path):
print(f" □ {item}/")
elif os path.islink(item_path):
print(f" □ {item} (ссылка)")
except FileNotFoundError:
рпп1(ГДиректория '{path}1 не существует")
except PermissionError
print(f"HeT доступа к директории {path} )
def organize_directory(path= ):
..Организует файлы в директории по типам.........
# Создаем директории для разных типов файлов
categories = {
'images': ['.jpg , 'Jpeg', '.png , '.gif', '.bmp'],
'documents': [ .pdf', '.docx', '.txt', '.xlsx , .pptx'],
'archives': ['.zip', '.rar', .tar', '.gz'],
'code': [ .py , '.js , .html', '.css', ' java']
}
for category in categories
os rnakedirs^os path jointpath category) exist_ok=True)
# Перемещаем файлы в coo гветствующие директории
for item in os listdir (path):
item_path = os.path jointpath, item)
it os path.isfiletitem path)'
ext = os.path.splitcxt(itcm)[l] lower()
for category, extensions in categories.itemst).
if ext in extensions.
dest_dir = os path.jointpath, category)
try:
shutil move(item_path os.path.join(dest_dir, item))
рппиР'Перемещен: {item} > {category}/")
except Exception as e
рпп1(Г0шибка при перемещении {item}: {e}")
break
it___name__== "____main__":
explore_directory()
organize_directory()
рпп1("Организация файлов завершена- )
explore_directory()
Современный подход с pathlib
Модуль pathlib предоставляет объектно-ориентированный интерфейс для
работы с путями:
python
# pathlib_examples.py
from pathlib import Path
import datetime
def demonstrate_pathlib():
..Демонстрирует возможности pathlib
# Создание путей
current_dir = Path cwd()
home dir = Path homeO
config path = home_dir/ .config' I 'myapp' / settings ini'
print(f"TeKyinaq директория {current_dir} )
рпп1(ГДомашняя директория. {home_dir} )
print(f"nyTb к конфигу: {config_path} )
# Создание директорий
config_path parent mkdinparents=True, exist_ol<=True)
# Работа с файлами
hg_file = Path( logs') I 'app.log'
log_ file, parent, mkdir (exist. ok=True)
# Запись в файл
timestamp = datetime datetime now() isoformatf)
lc>g_file write text(f"[{timestamp}] Application started\n'. encoding = 'utf-8')
# Чтение из файла
if log file existsO:
content = log file read_textfencoding='utl-3')
рппДГ'Содержимое лога: {content} )
# Поиск файлов
print("\nPython файлы в текущей директории )
for py_file in PathC ).globi'*.py ):
print(f" {py_file} ({py_file stat().st_size} байт)")
# Рекурсивный поиск
print("\nBce .txt файлы (рекурсивно):")
for txt_file in Path( ).rglob('*.txt):
print(f" {txt_file} )
if__name__== "___main__
demonstratepathiibO
Глава 3: Общение с системой: процессы и командная
строка
Автоматизация часто involves взаимодействие с системными утилитами и
другими программами. Модуль subprocess поедоставляет мощные
инструменты для этого.
Базовое использование subprocess
python
# subprocess_examples.py
import subprocess
import sys
def run command(command, capturc_output=Falsc):
....Выполняет команду и возвращает результат.....
try:
if capture_output
result = subprocess run(
command
shell=True,
capture_output=True,
text-True,
timeout=30
)
return result returncode, result stdout, result.stderr
else:
result = subprocess run(command. shell=True, timeout=30)
return result returncode, None, None
except subprocess.TimeoutExpired
рппЦГ'Команда '{command}' выполняется слишком долго")
return -1, None, None
except Exception as e
рппЦР'Ошибка при выполнении команды {е}")
return -1, None, None
def system_info():
..Собирает информацию о системе......
commands = [
("Дата и время", "date"),
("Имя пользователя", "whoami"),
("Дисковое пространство", "df-h" if sys.platform != "Win32" else "wmic logicaldisk get
size,freespace,caption'),
("Свободная память", "free -h" if sys.platform != "Win32" else "wmic OS get
Free Physical Memory"),
]
for description, command in commands
print(f \n{'='*50}")
print(f (description} )
рппНР{'='*50} )
returncode, stdout stderr = run_command(command. capture_output=True)
if returncode == 0:
print(stdout)
else:
print(f"0uJH6Ka (stderr} )
def monitor_processes().
..Мониторит запущенные процессы......
if sys.platform == "Win32":
command = "tasklist"
else:
command = "ps aux"
rcturncode. stdout, stderr = run_command(command, capture output=True)
if returncode == 0:
рпЩС'Запущенные процессы: )
print(stdout)
# Простой анализ (для Linux/Мас)
if sys platform != "Win32":
lines = stdout split('\n')
user_processes = {}
for line in lines[l:]: # Пропускаем заголовок
if line strip():
parts = line split()
if len(parts) >= 11:
user = parts[O]
user_processes[user] = user_processes.get(user 0) + 1
print( ЛпПроцессы по пользователям:' )
for user, count in user_processes.items():
print(f" {user ] {count} процессе a'1)
else:
print(f"OujM6Ka: {stderr} )
if_name____== ' _main____":
print("C6op информации о системе.. )
system_info()
рпп!("\п\пМониторинг процессов... )
rnonitor_processes()
# Пример с обработкой вывода
рмп1("\п\пПровсрка сетевых соединений.. )
if sys.platform == "Win32":
command = "netstat -an"
else:
command = "netstat tuln'
returncode, stdout, stderr = run_command(command, capture_output=True)
if returncode == 0:
# Анализ сетевых соединений
lines = stdout split('\n )
listening_ports = []
for line in lines:
if "LISTEN" in line or "ПРОСЛУШИВАЕТ" in line
listening_ports append (line. stripO)
print(f"HanfleHO {len(listening_ports)} прослушивающих портов:")
for port in listening_pcrts[:10]: - ем первые 10
print(f" {port} )
else:
print(f"OLLiH6Ka: {stderr} )
Автоматизация работы с Git
python
# gitautamatian.py
import subprocess
import os
from pathlib import Path
class GitAutomator:
def init (self, repopath):
self repo_path = Path(repo_path)
os.chdir(sclf repo_pathj
def iuri git_command(self. command)
..Выполняет git команду"""
full_command = f"git {command}
result - subprocess run(full_command, shell=True, capture_output=True, text=True)
return result.returncode, result.stdout, result stderr
def status(self):
..Показывает статус репозитория.....
return self.run git command! 'status")
def commit_ali(self. message)
..Добавляет все файлы и создает ксммит......
# Добавляем все файлы
returncode, stdout stderr = self run_grt_command( add . )
if returncode 1 = 0:
return returncode, stdout, stderr
# Создаем коммит
return self.run_git_command(f'commit -m {message) ’")
def push(self, remote="origin", branch= ’main"):
..Пушит изменения в удаленный репозиторий.......
return self.run_git_command(f"pjsh {remote} {branch} )
def auto_commit_push(self, message):
..Автоматически добавляет все файлы, коммитит и пушит.........
рппг("Проверка статуса репозитория...1)
returncode, stdout, stderr = self statusO
if returncode == 0:
if "nothing to commit" in stdout
print("HeT изменений для коммита')
return 0, stdout, stderr
else:
print("EcTb изменения, создаем коммит.. )
returncode, stdout, stderr = self.commit aii;message)
if returncode == 0:
print("KoMHT успешно создан, пушим.. )
return self push()
else:
return returncode, stdout, stderr
else:
return returncode, stdout, stderr
# Использование
if__name___== main_______":
# Укажите путь к вашему репозиторию
automator = GitAutomator("/path/to/your/repo')
# Автоматический коммит и пуш
returncode. stdout, stderr = automator auto_commit_push( Автоматический коммит из
Python скрипта1)
if returncode == 0:
рнпЦ"Успешно! )
print(stdout)
else:
рппг("Ошибка:")
print(stderr)
Часть II: Автоматизация в разработке (Dev)
Глава 4: Магия регулярных выражений (regex)
Регулярные выражения — это мощный инструмент для поиска и
обработки текста. В Python они реализованы в модуле ге.
Основы регулярных выражений
python
# regex_examples.py
import re
def demonstrate_regex():
..Демонстрирует различные возможности regex........
text = ..
Контакты:
Email: john.doc@cxample.com, janc_ smith! 23@tcst.co.uk
Телефоны: +7 (123) 456-78-90 8 900-123-45-67, 495-123-45-67
Даты: 2023-12 31, 31.12.2023, 01/01/2024
IP адреса: 192.168.! 1, 8 8.8 8, 2001:0db8 85аЗ:0000 0000 8a2c:0370 7334
# Поиск email-адресов
email_pattern = rl\b[A-Za-zO-9._%+-]+@[A-Za-zO-9.-]+\.[A-Z|a-z]{2,}\bl
emails = re findall(email_pattern. text)
printC'Email адреса:")
for email in emails
print(f" {email}")
# Поиск телефонных номеров (российский формат)
phone_pattern = r'(\+7|8)[\s\-]?\(?\d{3}\)?[\s\-]?\d{3}[\s\-]?\d{2}[\s\-]?\d{2}'
phones = re.findall(phone_pattern text)
рпп!("\пТелефонные номера: )
for phone in phones
print(f" [phone} )
# Поиск дат
date_pattern = r'\b\d{4}[-./]\d{2}[-./]\d{2}\b|\b\d{2}[-./]\d{2}[-./]\d{4}\b'
dates = re findall(date_pattern, text)
print("\nflaTbi:")
for date in dates
printff" {date} )
# Поиск IP-адресов
ip_pattern = r'\b(?:\d{ 1,3 }\. ){3 }\d{ l,3}\b|\b(?:[A-F0-9]{ 1,4}:){ 7} [A-F0-9]{ l,4}\b'
ips = re findall(ip_pattern, text, re IGNORECASE)
print("\nlP-aflpeca: )
for ip .n ips
print(f" {ip} )
# Замена с использованием regex
anonymized_text = re.sub(r'\b\d{l,3}\.\d{l,3}\.\d{l,3}\.\d{l,3}\b', 1XXX. XXX. XXX. XXX',
text)
anonymized_text = re.sub(r'\b[A-Za-zO-9._%+-]+@[A-Za-zO-9.-]+\.[A-Z|a-z]{2,}\b',
'EMAILigJHIi^DEN1, anonymized text)
рг1п1("\пАнонимизировэнный текст: )
print(anonymized text)
def validate, input():
..Валидация пользоватсльско! о ввода с помощью regex...........
patterns = {
'email': rIZ4[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
'phone1: r'~(\+7|8)[\s\ ]?\(?\d{3}\)?[\s\-]?\d{3}[\s\-]?\d{2}[\s\ ]?\d{2}$',
'date': r'~\d{4} \d{2} \d{2}$',
'ip': r'~\d{l,3}\.\d{l,3}\.\d{l,3}\.\d{l,3}$'
}
test_cases = [
('email', 'test<g)example.com'),
('email', 'invalid.email),
('phone', '+7 (123) 456-78-90'),
('phone', '123-456-789'),
('date', '2023-12-31'),
('date', '31.12.2023'),
('ip', '192.168.1.1'),
('ip', '999.999.999.999')
]
for field, value in test_cases
pattern = patternsffield]
is_valid = re.match(pattern, value) is not None
print(f {field} {value} {'/'if is_valid else X'}")
if name
main
рпп1("Демонстрация возможностей регулярных выражений:")
demonstrate_regex()
рпп1("\п\пВалидация пользовательского ввода:")
validate_input()
Парсинг лог-файлов с помощью regex
python
# log_parser.py
import re
from pathlib import Path
from collections import defaultdict
class LogParser:
def iii!t_(self):
self patterns = {
'error': r'ERROR|Error|error',
'warning': r'WARNING|Warning|warning ,
'http': r'HTTP/\d\.\d"\s\d{3}',
'ip': r'\d{l,3}\.\d{l,3}\.\d{l,3}\.\d{l,3}',
'timestamp': r\d{4} \d{2}-\d{2} \d{2}:\d{2}:\d{2}'
}
def parse. Iog_file(self. file_path)'
...Анализирует rwi файл и извлекает информацию"""
log_file = Path(file_path)
if not log file.exists():
raise РПсМо1ЕиигФЕггог(ГФайл {fllepath} не найден')
results = {
'error_count': 0,
'warning_count': 0,
'http_statuses': defaultdict(int),
'uniquejps': set(),
'timestamps': []
}
with open(log_file, r' encoding = 'utf-8 ) as file:
for line in file:
# Поиск ошибок
if re searchfself patterns['error'], line):
results?error_count'] += 1
# Поиск предупреждений
if re.search(self patternsf warning'], liner
results' warning count'] += 1
# Поиск НГГР статусов
http_match = re search(self.patternsf http'], line)
if http_match
status = httpjnatch.group)).split) )[-l]
results) http_statuses'][status] += 1
# Поиск IP-адресов
ip_match = re search(se!f patterns) .p ], line)
if ipmatch
results) uniquejps ] add(ip match.group)))
# Поиск временных меток
tirne_match = re search(self.patterns[ timestamp'], line)
if time_match
resultsl timestamps'].append(time match.group)))
return results
def gcnerate_report(sclf. results)
..Генерирует отчет no анализу лог-файла........
report = []
report.append) '=" * 50)
report append) 'АНАЛИЗ ЛОГ-ФАЙЛА ')
report append) '=" * 50)
report.append(f" Количество ошибок: {results['error_count']}")
report.append(f" Количество предупреждений: {results['warning_count']}")
report.append("\nHTTP статусы:')
for status, count in resultsf'httpstatuses ].items)):
report append(f" {status) {count}")
report.append(f"\nУникaльныe IP-адреса: {len(results['unique_ips'])}")
if resultst'uniquejps']:
report append) 1 + jcin(list(results['unique_iDs'])[:10]))
if len(results['unique_ips']) > 10:
report append(f" ... и еще {len(results[:unique_ips']) - 10} )
report.append(f"\nBpeMeHHbie метки: {len(results['timestamps'])} записей")
if results['timestamps ]:
report append(f Первая {resultsf timestamps'][C]} )
report.append(f" Последняя: {results['timestamps'][-l]}")
return "\n‘ jom(report)
# Использование
if__name_ == " nain_____
parser = LogParsen)
# Пример с искусственным лог-файло.м
samplejog = ....
2023-12-31 23:59:01 INFO: Application started
2023-12-31 23:59:02 WARNING: Conjuration file missing, using defaults
2023 12-31 23:59:03 ERROR Database connection failed
2023-12-31 23:59:04 INFO: Request from 192.168.1.1: GET/api/vl/users HTTP/1.1" 200
2023-12-31 23:59:05 INFO: Request from 192.168.1.2: POST/api/vl/login HTTP/1.1" 401
2023-12-31 23:59:06 WARNING: High memory usage detected
2023-12-31 23:59:07 INFO: Request from 192.168.1.3: GET/api/vl/products HTTP/1.1" 500
# Создаем временный файл для демонстрации
with open('sample.log‘, 'w'. encoding='utf-8') as f:
f.write(sample_ log)
try:
results = parser parsejogJi'lef sample.log')
report = parser.generate_report(results)
print(report)
finally:
# Удаляем временный файл
Path (sample, log1) unlinkfmissingok-True)
Глава 5: Парсим данные: от CSV до JSON и Excel
Работа с данными в различных форматах — важная часть автоматизации.
Python предоставляет отличные инструменты для работы с CSV, JSON,
Excel и другими форматами.
Работа с CSV и JSON
python
# data -processing.ру
import csv
import json
import pandas as pd
from pathlib import Path
def process csv_rile(input_file. output_file):
...Обрабатывает CSV файл и преобразует его в JSON"1"1
data = []
with open(input_tile, 'r', encoding='utf-8 ) as csvfile
reader = csv DictReader(csvfilc)
for r-'W in reader
# Пример обработки данных
row!'processed'] = True
row|'full_name'] = f {row get( firstjiame', ")} {row.get('last_name', )} stripO
data.append(row)
# Сохраняем в JSON
with open(output_file, 'w', encoding= utf-8') as jsonhle:
json dump'data, jsonfile, ensure_ascii=False, indent=2)
return data
def analyzejson_file(json_file):
..Анализирует JSON файл и возвоащает статистику........
with open(json_file, 'r'. enccding = 'utf-8') as file:
data = json.load(file)
if not data
return {}
# Собираем статистику
stats = {
'total_records': ien(data),
'fields': list(data[O].keys()) if data else [],
'field_stats': {}
}
# Для каждого поля собираем статистику
for field in stats['fields ]:
values = [item get(field) for item in data if field .n item]
if values
# Проверяем тип данных
firsi_value = values[0]
if isinstance(first_value, (int, float)):
stats['neld_stats'][field] = {
'type': 'numeric',
'min': n in(values),
'max': max(values),
'avg': sum(values) I len(values)
}
elif isinstance(first_value. str):
stats['field stab'][field] = {
'type': 'string',
'unique_values': len(set(values)),
'maxjength': max(lenlv) foi v in values if v)
}
else:
stats['field_stats'][fieldl = {
'type': 'other',
'count': len(values)
}
return stats
def excel operations(input_file. output_file):
'" Выполняет различные операции c Excel файлами с помощью pandas""
# Чтение Excel файла
df = pd read_excel(input_file)
print(f"l/lcxoflHbie данные: {df.shape[0]} строк, {df.shape[l]} колонок")
print(' \nnepBbie 5 строк )
printfdf. head ())
# Базовая очистка данных
# Удаляем полное гью пустые строки
df.dropna(how='all', inpiace=True)
# Заполняем частично пустые значения
for col in df columns
if df[col] dtype == 'object':
df [col].fi II na('Unknown', inplace=True)
else:
dfrcol].fillna(0, inplace=True)
# Добавляем вычисляемые поля
numeric_cols = df select_dtypes(include = [ number1]).columns
if len(numeric_cols) > 0:
dff'total] = df[numeric_cois].sum(axis=l)
df['average'l = df[numeric_cols].mean(axis=l)
# Сохраняем обработанные данные
with pd ExcelWriter(output file) as writer
df to_excel(writer, sheet_name= Processed lata , index=False)
# Создаем сводную таблицу
if len(numeric_cols) > 0:
pivot = df gruupbyidf.columns[0] if len(df.columns) > 0 else None)[numeric_cols].sum()
pivot.to_cxcel( writer. shcct_namc='Sjmmary')
рпп1(Г\пОбработанные данные сохранены в: {output_file}")
return df
# Создаем демонстрационные данные
def create_sample_data():
..Создает демонстрационные данные для примеров "1
# CSV данные
csv_data = [
['id', 'first_name', 'last_name', 'age', 'city'],
[1, 'John', 'Doe1, 30, 'New York ],
[2, 'Jane', 'Smith', 25, London'],
[3, 'Bob', 'Johnson', 35, 'Paris'],
[4, 'Alice', 'Brown , 28, 'Berlin']
]
with open('sample_data.csv', 'w', newline=", encoding = 'utf-8') as csvfile:
writer = csv writer(csvfile)
writer writerows(csv_data)
# Excel данные
df = pd DataFrame({
'Product1: ['Apple', 'Banana', 'Cherry', 'Cate'],
'Price': [1.20, 0.80, 2.50, 1.50],
'Quantity': [100, 150, 80, 120],
'Category': ['Fruit', 'Fruit', 'Fruit', 'Fruit']
})
df co excel('sample_data.xlsx', index=False)
print( Демонстрационные данные созданы")
if__name__== " nain_____":
# Создаем демонстрационные данные
create_sample_data()
# Обрабатываем CSV
рг1пг("\пОбработка CSV файла. )
csv_data = process_csv_file('sample_data.csv', 'processed_data.json')
print(f"O6pa6oTaHo {len(csv_data)} записей')
# Анализируем JSON
рппЦ"\пАнализ JSON файла... )
stats = analyze json fileCprocessed data । son')
рг1п1(С'Статистика: {stats} )
# Работаем c Excel
print("\nO6pa6oTKa Excel файла... )
df = excel_operations('sample_data.xlsx', 'processed_data.xlsx j
# Очищаем демонстрационные данные
for file in ['sample_data.csv', 'sample_data.xlsx', 'processed_data.json',
'processed_data.xlsx'J:
Path (file).unlink(missing ok=True)
Продвинутая обработка данных c Pandas
python
# advanced_data-processing.py
import pandas as pd
import numpy as np
from datetime import datetime
def advanced_data_analysis():
..Продвинутый анализ данных с помощью pandas........
# Создаем демонстрационный dataset
np random.seed(42)
dates = pd date_range( 20230101' periods=100)
data = {
'date': dates,
'sales': np.random randint(100, 1000, size=100),
'customers': np random randint(10, 100, size=100),
'temperature': np random normal(29, 5, stze=100),
'category': np random choice(['A , 'В', 'C'], size=100)
}
df = pd DataFrame(data)
dffrevenue'] = dff'sales'] * np.random.uniform(5, 20, size=100)
print( 'Исходные данные: )
pnnt(dt headO)
print(f"\nPa3Mep данны> {df shape} )
# 1. Агрегация данных
print( '\nl. Агрегация no категориям:")
catcyory_stats = df.groupby('category') agg({
'sales': ['sum1, 'mean', 'std J,
'customers': ['sum', 'mean ],
'revenue': 'sum'
}).round(2)
print(catcgory_stats)
# 2. Временной анализ
print( '\n2. Временной анализ )
df['month'] = dff'date'] dt month
monthly sales = df groupby('month').agg'{
'sales': 'sum',
'revenue': 'sum',
'customers': 'sum'
})
print(monthly_sales)
# 3. Корреляционный анализ
print("\n3. Корреляция между показателями:")
correlation = df[['sales', 'customers', 'temperature', 'revenue']].corr()
print(correlation)
# 4. Обработка пропущенных значений и выбоосов
print("\n4. Обработка пропущенных значений и выбросов:")
# Добавляем некоторые пропущенные значения и выбросы для демонстрации
df_with_mis5ing = df соруО
df with_missing loc[::10, 'sales'] = пр пап
df_with_missing loc[::15, 'revenue J = пр.пап
# Заполняем пропущенные значения
df cleaned = df_with_missing copyQ
df_cleaned['sales ].fillna(df_cleaned['sales].median]), inplace=True)
df_cleaned['revenue ].fillnatdf_cleanec[ revenue'].mean]), inplace=True)
# Обрабатываем выбросы с помощью iQR метода
QI = dfcleanedl'sales'].quantile(0.25)
Q3 = df-Cleanedfsales'Lquantilefe 75)
IQR = Q3 QI
lower_bound = QI 1.5 * IQR
upper_bound = Q3 + 1 5 * IOR
outliers = df_cleanedHdf_cleaned[ sales'] < lower bound) | (df_cleanedl sales'] >
upper bound)]
рппЦГ'Найдено {len(outliers)} выбросов в данных о продажах")
# 5. Сохранение результатов
with pd.ExcelWriter('sales_analysis.xlsx') as writer:
df .to_excel(writer, sheet_name= Raw Data', irdex^ False)
category stats toexceKwriter sheet_name='Category Stats )
monthly sales to_excel(writer, sheet_name='Monthly Sales')
correlation.to_exceKwriter sheet_name= Correlat on )
рг1п1("\пАнализ завершен. Результаты сохранены в 'sales_analysis.xlsx'")
return df. category stats. monthly_sales, correlation
if__name___== "___main__":
advanced_data_analysis()
Глава 6: Веб-скрейпинг: добыча данных из интернета
Веб-скрейпинг позволяет автоматизировать сбор данных с веб-сайтов. В
этой главе мы изучим библиотеки requests и BeautifulSoup для извлечения
данных из веб-страниц.
Основы веб-скрейпинга с BeautifuiSoup
python
# webscrapingbasics.py
import requests
from bs4 import BeaucifulSoup
import pandas as pd
from urllib parse import urljoin urlparse
import time
class WebScraper:
def _init_(self, delay=l):
seif session = requests SessionO
self delay = delay
self headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/91.0.4472 124 Safan/537.36'
}
def fetch page(self, url):
..Загружает веб страницу.......
try:
time.sleep(self.delay) # Уважаем robots.txt и избегаем блокировок
response = self session.get(url, headers=-self.hcadcrs, timeout-10)
response raise_for_status()
return response.text
except requests RequestException as e
рпп1(ГОшибка при загрузке страницы {url} {e} )
return None
def extract_links(self, html, base_url, frlter_pattern=None):
"" 'Извлекает все ссылки co страницы.....
soup = BeautifulSouprtitml, 'html.parser')
links = []
for link in soup find_all('a', href=True):
href = linkfhref ]
full_url = urljoin(base_url, href)
if filter_pattern
if filter_pattern in full_url
links.append ((full_url, link.get_text() stripf)))
else:
links.append((full_urL link get_text().strip()))
return links
def extract_tables(self, html):
..Извлекает таблицы co страницы.........
soup = BeautifulSoup(html, 'html oarser')
tables = []
for table in soup find_all('table ):
rows = []
for row in table find_all('tr):
cells = [cell.get_text() strip() for cell in row find_all([ th1, 'td'])]
it cells
rows.append(cells)
if rows:
tables. append( rows)
return tables
def scrape_ website data(self. url, selectors):
..Извлекает конкретные данные с веб-страницы с помощью CSS селекторов""
html = self fctchpage(url)
if not html
return None
soup = BeautifulSoup(html, 'htmlparser)
results = {}
for key, selector in selectors items():
elements = soup selccttselector)
if elements
results[key] = [elem gct_text() stripO fcrelem in elements]
else:
results[key] = []
return results
def demo_scraping():
...Демонстрация возможностей веб-скрейпинга...........
scraper = WebScraper(delay-2)
# Пример: получение заголовков новостей с hypothetical news site
url = "https://httpbin.org/html" # Используем тестовый URL
# Для реального примера можно использовать:
# url = "https://news.ycombinator.com"
# selectors = {
# 'titles': '.titleline > a',
# 'scores': '.score',
# 'links': '.titleline > a'
#}
# Для тестового примера:
selectors = {
'heading1: 'hl',
'paragraphs': 'p'
}
data = scraper scrape_website_data(url, selectors)
if data-
рпгД("Извлеченные данные: )
for key. values in data items()
print(f"\n {key} )
for i, value in enumerate(values[:3]): # Покажем первые 3 элемента
print(f" {i Fl} {value} )
if len(values) > 3:
print(f" ... и еще {lcn(valucs) - 3} элементов )
# Пример извлечения таблиц
tables_url = "https://httpbin.org/html" # Замените на URL с таблицами
html content = scraper fetch pageltabtes url)
if html_content
tables = scraper extract_tables(html_content)
print(f"\nHai/ifleH5 таблиц: {len(tables)} )
for i, table in enumerate(tables[:2]): # Покажем первые 2 таблицы
рпп1(Р'\пТаблица {i+1}: )
for row in table[:5]:
print( " + " | ’ join(row))
if len(table) > 5:
print(f" ... и еще {len(table) - 5} строк")
def ethical_scraping_guidelines():
"" ’Руководство по этичному веб-скрейпингу.......
guidelines = [
"Всегда проверяйте файл robots.txt сайта ,
"Уважайте настройки Crawl-Delay в robots.txt",
"Используйте задержки между запросами (не менее 1-2 секунд)",
"Кэшируйте загруженные страницы для избежания повторных запросов",
"Идентифицируйте себя с помощью правильного User-Agent",
"Не скрейпите сайты, которые явно запрещают это в своих условиях
использования",
"Рассмотрите использование официального API вместо скрейпинга",
"Ограничьте обьем запрашиваемых данных до необходимого минимума",
"Не перенагружайте серверы сайта большим количеством запросов"
]
print("PyKOBOflCTBO по этичному веб-скрейпингу:")
tor I, guideline in enumerate(guidclines 1):
print(f"{i} {guideline} )
|f_ name _ == "__main
print( Демонстрация веб-скрейпинга )
demo scrapingO
print("\n" + " = "*50)
ethical_scraping guidelinesO
Продвинутый скрейпинг: обработка JavaScript и пагинации
python
# advanced scraping, ру
import requests
from bs4 import BeautifuiSoup
import pandas as pd
import time
from selenium import webdriver
from selenium webdriver.common.by import By
from selenium webdriver.support ui import WebDriverWait
from selenium webdriver support import expectedconditions as EC
class AdvancedScraper:
def _init (self, use selenium=False):
self.use_selenium = useselenium
if use_ selenium
self driver = webdriver.Chromel) Требуется ChromeDriver
else:
self session = requests SessionO
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
def scrape_dynamic_contenttself, url. wait_for=None):
..Скрапинг javaScript-генерируемого контента с помощью Selenium........
if not self.use_seleniurn
print("Selenium не инициализирован")
return None
try:
self driver get(url)
# Ожидаем загрузки конкретного элемента
if wait_for
WebDrivei Wait(self.driver, 10) until(
EC presence_of_elen?ent_located((By CSS_SELEC10K wait_for))
)
# Даем время для полной загрузки JavaScript
time.sleep(2)
page_source = self driver.page_sourcc
return page_source
except Exception as e
print(f"0uJH6Ka при загрузке динамического контента: {е}")
return None
def handle_pagination(self base url. page_param='page', max_pages=10):
..Обработка пагинации на сайте'""
all_data = []
for page m range(l, max pages + 1):
print(f"06pa6oTKa страницы (page} )
# Формируем URL страницы
if ?' in base_uri
url = f"{base_url}&(page_param}=-{page}
else:
url = f {base_url}?{page_param} = {page}"
if self use_selenium
html = self scrape_dynamic_content(url wait_for='.product) електора
else:
html = self fetch_page(url)
if not html
print(f"He удалось загрузить страницу {page}")
break
# Извлекаем данные cc страницы (зависит от структуры сайта)
page data = self extract_page_data(html)
if not page data
print(f"HeT данных на странице {page}, прекращаем пагинацию")
break
all_data.extend(page_data)
time.sleep(l) # Задержка между запросами
return all data
def fetch page(self. url):
...Загружает веб-страницу (для статических сайтов)........
try:
response = self.session.get(url, headers-self headers. timeout=10)
response. raise_for_status()
return response.text
except requests RcqucstException as e
рпп1(ГОшибка при загрузке страницы {url} {e} )
return None
def extract_page_data(self, html)
...Извлекает данные co страницы (шаблонный метод)'1""
# Эта функция должна быть адаптирована под конкретный сайт
soup = BeautifulSoup(html 'htm parser')
data = []
# Пример: извлечение данных продуктов
products = soup.selectC.product') # Пример селектора
for product in products.
try:
name = product select_one( .product-name ).get_text().strip()
price = product.select_one(‘.price ) get_text().strip()
data.append({ name:: name, 'price': price})
except AttributeErrcr:
continue
return data
def close(self):
..Закрывает ресурсы.....
if self use_selenium
self driver quitO
def ecommerce_scraping_example():
..Пример скрейпинга интернет-магазина
# Note: Это демонстрационный код, который нужно адаптировать под конкретный
сайт
scraper = AdvancedScraper(use_selenium=True) # Используем Selenium для JavaScript
try:
# Пример URL (замените на реальный)
base_url = "https://httpbin.org/html" # Тестовый URL
# Для реального примера:
# base_url = "https://example.com/products"
# data = scraper handle pagmation(oase_url, max pages=5)
# Демонстрация с тестовыми данными
рмпЦ"Загрузка динамического контента...")
html = scraper.scrape dynamic_content(basc_url wait_for= hl)
if html:
soup = BeautifulSoupihtml 'html.parser')
heading = soup find('hl')
print(f"3ar-' ловок страницы {heading get_text() if heading else 'He найден'} )
# Сохранение данных в CSV
# df= pd.DataFrame(data)
# df.to_csv(products.csv', index=Faise, encoding='utf-8‘)
# print(f"CoxpaHCHO {len(data)} продуктов в products.csv")
finally:
scraper closet)
if__name___== "___main__":
print( Пример продвинутого веб-скрейпинга: )
ecommerce_scraping_example()
Заключение
В этой книге мы рассмотрели множество аспектов автоматизации с
помощью Python — от базовых операций с файлами до веб-скрейпинга и
работы с данными. Каждая глава содержала практические примеры кода,
которые вы можете адаптировать под свои задачи.
Ключевые takeaways:
1. Python предоставляет богатую экосистему библиотек для автоматизации
практически любых задач
2. Правильная организация кода и обработка ошибок критически важны для
надежных скриптов
3. Этичный подход к веб-скрейпингу помогает поддерживать хорошие
отношения с владельцами сайтов
4. Автоматизация рутинных задач высвобождает время для более важной
работы
Дальнейшие шаги:
1. Изучите официальную документацию библиотек, упомянутых в книге
2. Присоединитесь к сообществам Python для обмена опытом
3. Постепенно адаптируйте примеры из книги под свои конкретные задачи
4. Изучайте исходный код популярных библиотек для лучшего понимания
лучших практик
Помните, что лучший способ обучения — это практика. Начните с
автоматизации небольших задач и постепенно переходите к более
сложным проектам.
Приложения
Приложение А: Шпаргалка по основным модулям Python
Для работы с файловой системой:
• os — базовые операции с файловой системой
• shutil — высокоуровневые файловые операции
• pathlib — объектно-ориентированная работа с путями
Для работы с данными:
csv — чтение и запись CSV файлов
json — работа cJSON данными
pandas — мощная обработка и анализ данных
Для веб-скрейпинга:
requests — HTTP запросы
BeautifulSoup — парсинг HTML
Selenium — авгоматизация браузера
Для системного администрирования:
subprocess — управление процессами
psutil — мониторинг системы
paramiko — SSH соединения
Приложение Б: Best Practices
Организация кода:
1. Используйте виртуальные окружения для изоляции зависимостей
2. Разбивайте большие скрипты на модули и функции
3. Добавляйте комментарии и docstrings для документирования кода
4. Используйте систему контроля версий (Git)
Обработка ошибок:
1. Всегда используйте try-except для обработки исключений
2. Добавляйте информативные сообщения об ошибках
3. Реализуйте легирование для отладки и мониторинга
Производительное ть:
1. Используйте генераторы для работы с большими объемами данных
2. Кэшируйте результаты дорогостоящих операций
3. Используйте асинхронное программирование для I/O операций
Безопасность:
1. Никогда не храните credentials в коде
2. Используйте env ronment variables для конфиденциальных данных
3. Проверяйте и санируйте пользовательский ввод
Этичный скрейпинг:
1. Уважайте robots.txt
2. Используйте задержки между запросами
3. Кэшируйте загруженные страницы
4. Используйте официальные API когда это возможно
Эта книга дала вам основы автоматизации на Python, но настоящие
знания приходят с практикой. Начните автоматизировать свои
повседневные задачи сегодня, и вы быстро увидите, насколько Python
может повысить вашу продуктивность.