Перейти к основному содержимому

Работа с файлами, сетью и внешними API

Разработчику Архитектору

См. также: Сетевое программирование (socket) · Работа с базами данных в Python · Автоматизация и DevOps · Python — файлы и текст (Lab)


Работа с данными и внешним миром

Материал этой главы объединяет сразу несколько зон практики — файловые операции, форматы данных, HTTP, парсинг и автоматизацию. Чтобы не воспринимать текст как "набор отдельных техник", полезно держать в голове общий конвейер:

  1. Получить данные (файл или API).
  2. Проверить формат и валидность.
  3. Преобразовать в рабочую структуру Python.
  4. Сохранить результат и зафиксировать логи.
  5. Повторить задачу по расписанию или в пайплайне.

Этот конвейер встречается почти в каждом прикладном проекте — от аналитики до DevOps-скриптов.

Готовая галерея скриптов с разбором каждой строки — Python — работа с файлами и текстом.


Работа с файлами и системой

Python предоставляет широкий спектр средств для взаимодействия с файловой системой и внешними ресурсами. Эти возможности реализованы как в стандартной библиотеке, так и через специализированные модули.

В Python файлы обрабатываются как объекты потока (file objects), предоставляющие интерфейс для последовательного доступа к данным. Основная функция для открытия файлов — open(). Она возвращает объект файла, через который выполняются операции ввода-вывода.

Синтаксис:

file = open(path, mode='r', encoding=None)

Параметры:

  • path — строка или объект пути, указывающий на файл.
  • mode — режим открытия файла (подробно ниже).
  • encoding — кодировка текста (актуально для текстовых режимов).

Прямое использование open() требует явного вызова метода .close(), что чревато утечками ресурсов при ошибках. Для гарантированного освобождения ресурсов применяется контекстный менеджер with.

with open('data.txt', 'r', encoding='utf-8') as f:
content = f.read()
# Файл закрыт автоматически после выхода из блока

Контекстный менеджер обеспечивает корректное завершение операций даже в случае исключений, вызывая метод __exit__ у объекта файла, который, в свою очередь, вызывает .close().

Использование with считается стандартной практикой при работе с файлами. Прямые вызовы open() без close() допустимы только в специализированных случаях (например, при передаче дескриптора между функциями).

Строка режима open()

Строка режима функции open() собирается из базовых символов и модификаторов. Ниже — допустимые комбинации по спецификации Python 3.x.

РежимОписаниеКлючевое поведение
rЧтениеПо умолчанию. Файл должен существовать.
wЗаписьСоздаёт файл или полностью перезаписывает существующий.
xИсключительное созданиеЗапись только если файла нет. Иначе FileExistsError.
aДобавлениеЗапись всегда в конец. Файл создаётся при отсутствии.
bДвоичный режимРабота с bytes. Несовместим с t.
tТекстовый режимРабота с str. По умолчанию.
+ОбновлениеРазрешает чтение и запись в одном потоке.
r+Чтение и записьУказатель в начале. Файл не создаётся и не обрезается.
w+Чтение и записьФайл создаётся или перезаписывается перед открытием.
a+Чтение и записьЗапись в конец, чтение с начала. Файл создаётся при отсутствии.
x+Чтение и запись (создание)Аналог x с возможностью чтения.
rb, wb, ab, xbДвоичные базовые режимыПоведение идентично r/w/a/x, но возвращают/принимают bytes.
r+b / rb+Двоичное чтение и записьБез обрезки, указатель в начале.
w+b / wb+Двоичное чтение и запись (перезапись)Файл создаётся или перезаписывается.
a+b / ab+Двоичное чтение и запись (добавление)Запись в конец, чтение с начала.
x+b / xb+Двоичное создание с чтениемИсключительное создание и чтение/запись.
rt, wt, at, xtЯвный текстовый режимПоведение совпадает с r/w/a/x.

Технические ограничения и правила

  1. Порядок символов в строке режима на работу интерпретатора не влияет; общепринятая запись — базовый символ, затем +, затем b или t.
  2. Модификаторы t и b взаимоисключающие. Если ни один из них не указан, подразумевается t.
  3. В режимах с + переключение между чтением и записью требует промежуточного f.seek() или f.flush(). Без этого поведение стандартом не определено.
  4. В режимах a и a+ позиция записи перед каждым write() принудительно сбрасывается в конец файла, независимо от текущего положения указателя.
  5. Режимы x и x+ доступны начиная с Python 3.3.

Бинарные режимы (rb, wb и комбинации с +) не выполняют преобразования окончаний строк (\n\r\n) и игнорируют параметр encoding. Они нужны при работе с изображениями, архивами и сериализованными объектами. В текстовых режимах кодировку задают аргументом encoding при вызове open() (см. примеры выше).

Чтение текстового файла:

with open('example.txt', 'r', encoding='utf-8') as f:
lines = f.readlines()

Разбор фрагмента:

  • with open(...) открывает файл и гарантированно закрывает его после выхода из блока.
  • Режим 'r' включает чтение текста, а encoding='utf-8' задаёт корректную декодировку символов.
  • readlines() возвращает список строк, что удобно для построчной фильтрации и анализа.

Запись в бинарный файл:

data = b'\x00\xFF\xA5'
with open('binary.dat', 'wb') as f:
f.write(data)

Добавление строки в лог:

with open('app.log', 'a', encoding='utf-8') as f:
f.write("Ошибка подключения\n")

Разбор фрагмента:

  • Режим 'a' всегда пишет в конец файла и не затирает старые записи.
  • write(...) сохраняет строку как есть; \n добавляет перевод строки.
  • Такой паттерн часто используют для простого логирования событий скрипта.

Файловые пути — центральный элемент взаимодействия с файловой системой.

Python предлагает два основных подхода: традиционный модуль os.path и современный объектно-ориентированный pathlib.

Модуль os.path - часть модуля os, предоставляющая функции для манипуляции путями в виде строк.


import os

path = '/home/user/docs/file.txt'

print(os.path.basename(path)) # 'file.txt'
print(os.path.dirname(path)) # '/home/user/docs'
print(os.path.splitext(path)) # ('/home/user/docs/file', '.txt')
print(os.path.exists(path)) # True/False
print(os.path.isfile(path)) # Проверка типа
print(os.path.join('folder', 'sub', 'file.txt')) # Сборка пути с учётом ОС

Особенность: все функции работают со строками. Это может привести к ошибкам при некорректной обработке разделителей (\ и /).

Модуль pathlib появился в Python 3.4 как более современная альтернатива. Предоставляет объектно-ориентированный интерфейс для путей.

Код ITЗагрузка примера кода…

pathlib является предпочтительным выбором в новых проектах благодаря читаемости кода, поддержке оператора / для объединения путей, и наличию методов для итерации, фильтрации и рекурсивного обхода. Он интегрирован с большинством функций стандартной библиотеки, принимающих строки путей.

Для массовых операций над файлами используются комбинация обхода файловой системы и системных вызовов.

Обход каталогов через os.listdir() и os.walk():


import os

# Простой список файлов в каталоге
for filename in os.listdir('/path/to/dir'):
print(filename)

# Рекурсивный обход
for root, dirs, files in os.walk('/path/to/root'):
for file in files:
filepath = os.path.join(root, file)
print(filepath)

Через pathlib:

from pathlib import Path

p = Path('/path/to/dir')
for file in p.rglob('*'): # Рекурсивно
if file.is_file():
print(file)

Переименование файлов через метод os.rename() или Path.rename():


import os

os.rename('old_name.txt', 'new_name.txt')

# Или через pathlib
Path('old.txt').rename('new.txt')

rename() может перемещать файлы между каталогами, но не гарантирует работу между разными файловыми системами.

Копирование и удаление использует модуль shutil. Но базовые операции есть и в os:


import os

# Удаление файла
os.remove('file.txt') # или os.unlink()

# Удаление пустого каталога
os.rmdir('empty_dir/')

# Удаление непустого каталога — невозможно через os

Модуль shutil предоставляет удобные функции для копирования, перемещения и удаления файлов и деревьев каталогов.

Копирование:


import shutil

# Копирование файла
shutil.copy('source.txt', 'dest.txt') # Сохраняет метаданные (время модификации)
shutil.copy2('source.txt', 'dest.txt') # Сохраняет больше метаданных (включая время доступа)

# Копирование дерева каталогов
shutil.copytree('src_folder/', 'backup/')

copytree() создаёт целевую директорию и рекурсивно копирует содержимое. При наличии целевого каталога — ошибка. Можно использовать dirs_exist_ok=True (начиная с Python 3.8).

Перемещение:

shutil.move('old_location/', 'new_location/')

Это аналог mv в Unix. Может перемещать как файлы, так и каталоги. Если целевой путь находится на другой файловой системе — выполняется копирование с последующим удалением источника.

Удаление дерева каталогов:

shutil.rmtree('unwanted_dir/')

Удаляет каталог и всё его содержимое. Аналог rm -rf. Будьте осторожны: операция необратима.

Если же речь идёт о создании структуры проекта, то автоматизация создания директорий — частая задача при инициализации проектов.

Создание каталогов:


import os

from pathlib import Path

# Через os
os.makedirs('project/src/utils', exist_ok=True) # exist_ok избегает ошибки, если путь уже есть

# Через pathlib
Path('project/data/raw').mkdir(parents=True, exist_ok=True)

Оба метода поддерживают parents=True (создание промежуточных каталогов) и exist_ok=True (игнорирование ошибки существования).

Пример - скрипт инициализации проекта:

Код ITЗагрузка примера кода…

Python часто используется для написания скриптов автоматической очистки временных файлов, логов, кэшей. Пример: очистка старых логов:

Код ITЗагрузка примера кода…

Такие скрипты могут быть запланированы через cron (Linux) или Task Scheduler (Windows).

Python предоставляет полный набор инструментов для работы с файловой системой на уровне, достаточном для создания сложных системных утилит, скриптов развёртывания, обработки данных и управления проектами. Современные практики рекомендуют использовать pathlib для работы с путями, with для управления контекстом файлов и shutil для сложных операций с каталогами.

Выбор между os и pathlib зависит от контекста: os остаётся актуальным в легаси-коде и при необходимости низкоуровневого контроля, тогда как pathlib предпочтителен в новых проектах за счёт своей ясности и безопасности.

Все операции с файловой системой должны выполняться с учётом особенностей платформы (разделители путей, права доступа, кодировки), а также с должным уровнем обработки исключений (try...except), особенно при автоматизации.


Частые ошибки новичков в файловых скриптах

  • Жёстко прописанные абсолютные пути без учёта среды запуска.
  • Отсутствие резервной копии перед массовым переименованием/удалением.
  • Смешивание чтения текстовых и бинарных режимов.
  • Игнорирование кодировок и различий utf-8 / cp1251 / utf-8-sig.
  • Запись без явной нормализации переводов строк в CI-чувствительных проектах.

Для больших скриптов полезно добавлять режим dry-run, когда программа только печатает план операций и не вносит изменений в файловую систему.


Практические сценарии — от точечных правок до контроля целостности

Ниже — типовые задачи, которые решают скриптами на Python в документации, DevOps и подготовке контента. Каждый блок можно запускать отдельно; для продакшена добавьте argparse, логирование и проверку путей.


Точечная замена текста в Markdown

Задача: в одном файле заменить устаревший термин, не трогая остальные .md в каталоге.

from pathlib import Path

def replace_in_markdown(path: Path, old: str, new: str) -> bool:
text = path.read_text(encoding="utf-8")
if old not in text:
return False
path.write_text(text.replace(old, new), encoding="utf-8", newline="\n")
return True

replace_in_markdown(Path("docs/guide.md"), "старое-имя", "новое-имя")

Для нескольких файлов оберните в цикл по Path("docs").rglob("*.md") с предварительным if old in path.read_text(...).


Проверка подстроки, замена и фиксированные переносы строк

Часто CI требует LF (\n), а редактор на Windows сохраняет CRLF. Сначала проверяем наличие шаблона, затем нормализуем окончания строк:

Код ITЗагрузка примера кода…

newline="\n" в write_text на Windows всё равно может вмешиваться ОС — запись через write_bytes даёт предсказуемый результат.


Организация и сортировка каталогов

Критерии — расширение, дата изменения (st_mtime), размер (st_size), "сигнатура" — первые байты файла (magic bytes):

Код ITЗагрузка примера кода…


Пакетное переименование и метаданные

Переименование по шаблону; время доступа/модификации — через os.utime, на Windows дополнительно os.chmod при необходимости:

from pathlib import Path

import os
import time

def batch_rename(folder: Path, prefix: str) -> None:
for i, path in enumerate(sorted(folder.glob("*.jpg")), start=1):
target = folder / f"{prefix}_{i:03d}{path.suffix}"
path.rename(target)

def touch_mtime(path: Path, when: float | None = None) -> None:
ts = when or time.time()
os.utime(path, (ts, ts))

Конвертация форматов и нормализация кодировок

Текст: прочитать в одной кодировке, записать в UTF-8. Таблицы: CSV → JSON (см. ниже в разделе форматов). Для "неизвестной" кодировки — chardet (сторонний пакет) или эвристика utf-8 / cp1251:

from pathlib import Path

def to_utf8(src: Path, src_encoding: str = "cp1251") -> None:
text = src.read_text(encoding=src_encoding)
dst = src.with_suffix(src.suffix + ".utf8")
dst.write_text(text, encoding="utf-8")

Парсинг, фильтрация и агрегация (лог, CSV, JSON, XML, Markdown)

Лог-файл — подсчёт уровней ERROR:

Код ITЗагрузка примера кода…

CSV — фильтр строк:

Код ITЗагрузка примера кода…

JSON — агрегация по ключу:


import json

from pathlib import Path

def sum_by_category(path: Path) -> dict:
items = json.loads(path.read_text(encoding="utf-8"))
totals: dict = {}
for row in items:
cat = row["category"]
totals[cat] = totals.get(cat, 0) + row["amount"]
return totals

Markdown — заголовки второго уровня:


import re

from pathlib import Path

def extract_h2(md_path: Path) -> list[str]:
return re.findall(r"^## (.+)$", md_path.read_text(encoding="utf-8"), re.MULTILINE)

Архивирование, сжатие, разбиение и склейка

Код ITЗагрузка примера кода…

zipfile удобен для обмена с Windows; tarfile — для Unix-пайплайнов и .tar.gz.


Резервное копирование и синхронизация каталогов

Полная копияshutil.copytree. Инкремент — копировать только файлы новее или отсутствующие в целевом каталоге:

Код ITЗагрузка примера кода…

Зеркалирование (удаление лишнего в приёмнике) — осторожно — сначала dry-run, список файлов к удалению, затем unlink. Для зрелых сценариев используют rsync через subprocess или библиотеку dirsync.


Мониторинг файловой системы (события)

Модуль watchdog (установка: pip install watchdog) реагирует на создание, изменение и удаление:

Код ITЗагрузка примера кода…

В stdlib есть inotify только косвенно; для кроссплатформенных приложений watchdog предпочтительнее.


Криптография — хеш, шифрование, безопасное затирание

Код ITЗагрузка примера кода…

Симметричное шифрование файла — модуль cryptography (Fernet): ключ хранить вне репозитория (переменные окружения, vault).


Генерация из шаблонов и scaffolding проекта

Код ITЗагрузка примера кода…

Для сложных каркасов — Cookiecutter, copier.


Валидация целостности и синтаксиса

Сверка контрольных сумм с эталонным манифестом:

Код ITЗагрузка примера кода…

JSONjson.loads + jsonschema; XMLxml.etree или lxml + XSD; Markdown — линтеры (markdownlint через CLI) или проверка битых ссылок скриптом по regex.


Форматы данных — JSON, CSV, XML

JSON — текстовый формат представления структурированных данных на основе пар "ключ-значение" и упорядоченных списков. Поддерживает типы — строки, числа, логические значения, null, объекты и массивы.

Выбор формата обычно зависит от целевой системы:

  • JSON удобен для API и вложенных структур.
  • CSV удобен для табличных выгрузок и обмена с BI/Excel.
  • XML остаётся важным в enterprise-интеграциях и некоторых стандартизованных протоколах.

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

Python предоставляет встроенную поддержку через модуль json. Основные функции:

  • json.dumps(obj) - Сериализует объект Python в строку JSON;
  • json.loads(s) - Десериализует строку JSON в объект Python;
  • json.dump(obj, file) - Записывает сериализованный объект в файловый объект;
  • json.load(file) - Читает и десериализует JSON из файлового объекта.

Сериализация и десериализация:

Код ITЗагрузка примера кода…

Думаю, нужно пояснить параметры:

  • ensure_ascii=False — позволяет сохранять кириллицу и другие символы без экранирования.
  • indent=2 — форматирует вывод с отступами для читаемости.

Также стоит отметить и валидацию данных при загрузке.

Модуль json не выполняет семантической валидации. Он проверяет только синтаксис. При ошибке парсинга выбрасывается исключение json.JSONDecodeError.

try:
data = json.loads(invalid_json_string)
except json.JSONDecodeError as e:
print(f"Ошибка парсинга JSON: {e}")

Для полноценной валидации структуры данных (например, обязательных полей, типов) следует использовать сторонние библиотеки (jsonschema) или ручные проверки.

Пример обработки datetime:

from datetime import datetime

import json

data = {"timestamp": datetime.now()}

def serialize(obj):
if isinstance(obj, datetime):
return obj.isoformat()
raise TypeError(f"Тип {type(obj)} не сериализуем в JSON")

json_str = json.dumps(data, default=serialize)

CSV — простой формат таблиц данных, где каждая строка представляет запись, а поля разделяются разделителем (чаще всего запятой).

Поддерживает различные диалекты (разделители, кавычки, экранирование).

Модуль csv предоставляет гибкий интерфейс для чтения и записи CSV-файлов.

Чтение CSV:


import csv

with open('users.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
header = next(reader) # Заголовок
for row in reader:
print(row) # row — список значений

Если файл содержит заголовок, удобнее использовать DictReader:

with open('users.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
print(row['name'], row['age']) # Доступ по ключу

Запись CSV:

headers = ['name', 'age', 'city']
data = [
['Alice', 30, 'Moscow'],
['Bob', 25, 'Saint Petersburg']
]

with open('output.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(headers)
writer.writerows(data)

Параметр newline='' обязателен при записи в Windows, чтобы избежать лишних пустых строк.

Использование DictWriter:

with open('output.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f, fieldnames=headers)
writer.writeheader()
writer.writerow({'name': 'Alice', 'age': 30, 'city': 'Moscow'})

Можно задавать разделитель, кавычки, способ экранирования:

csv.reader(f, delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL)

XML — иерархический формат разметки, основанный на тегах. Поддерживает атрибуты, вложенные элементы, пространства имён. Используется в конфигурациях, веб-сервисах (SOAP), документах.

Модуль xml.etree.ElementTree (часто импортируется как ET) — стандартный инструмент для работы с XML.

Чтение и разбор XML:


import xml.etree.ElementTree as ET

tree = ET.parse('users.xml')
root = tree.getroot()

for user in root.findall('user'):
user_id = user.get('id') # Атрибут
name = user.find('name').text
age = user.find('age').text
print(f"ID: {user_id}, Name: {name}, Age: {age}")

Создание XML:

root = ET.Element('users')

user1 = ET.SubElement(root, 'user', {'id': '1'})
ET.SubElement(user1, 'name').text = 'Alice'
ET.SubElement(user1, 'age').text = '30'

tree = ET.ElementTree(root)
tree.write('new_users.xml', encoding='utf-8', xml_declaration=True)

Особенности:

  • find() — возвращает первый совпадающий элемент.
  • findall() — все совпадения.
  • get(attr) — значение атрибута.
  • text — содержимое элемента.
  • Поддержка XPath-подобных выражений (ограниченных): root.find('user[@id="1"]').

Рассмотрим задачу конвертации CSV-файла в JSON с сохранением типов данных.

Исходный CSV (input.csv):

name,age,active
Alice,30,true
Bob,25,false

Реализация

Код ITЗагрузка примера кода…

Результат (output.json):

[
{
"name": "Alice",
"age": 30,
"active": true
},
{
"name": "Bob",
"age": 25,
"active": false
}
]

Парсинг и веб-скрапинг

В условиях роста объёмов публичных данных в интернете, извлечение информации с веб-сайтов — распространённая задача для анализа рынка, мониторинга цен, сбора новостей и обучения моделей. Python предоставляет мощные инструменты для автоматизированного получения и обработки веб-контента. В данной подглаве рассматриваются основные подходы к веб-скрапингу, ключевые библиотеки, типичные паттерны использования и ограничения.

Для получения HTML-страниц (или любого HTTP-ресурса) используется библиотека requests — де-факто стандарт для HTTP-взаимодействия в Python. Полный обзор HTTP-клиентов, сокетов, DNS, SSH и других сетевых инструментов — Сетевое программирование на Python.

Примечание: requests не является частью стандартной библиотеки. Требует установки:

pip install requests

Основные операции:


import requests

# GET-запрос
response = requests.get('https://httpbin.org/html')

# Проверка статуса
if response.status_code == 200:
html = response.text # Текст ответа (в кодировке, указанной в headers)
else:
print(f"Ошибка: {response.status_code}")

Разбор фрагмента:

  • requests.get(...) отправляет HTTP GET и возвращает объект Response.
  • response.status_code содержит код ответа сервера.
  • При 200 тело ответа читается через response.text.
  • В ветке else обрабатывается неуспешный сценарий, чтобы не работать с некорректными данными.

Для корректного взаимодействия с серверами рекомендуется настраивать заголовки, параметры и таймауты.

Код ITЗагрузка примера кода…

Использование User-Agent помогает избежать блокировок, так как многие сайты фильтруют запросы от "неизвестных" клиентов. timeout предотвращает зависание скрипта при недоступности сервера. А raise_for_status() позволяет централизованно обрабатывать HTTP-ошибки.

Иногда сервер не указывает правильную кодировку. В этом случае можно принудительно задать её:

response.encoding = 'utf-8' # или 'cp1251' для старых русскоязычных сайтов
html = response.text

Полученный HTML требует структурированной обработки. Обзор инструментов — Парсинг на Python; углублённо по BeautifulSoup — отдельная статья с примерами.

pip install beautifulsoup4

Основы использования:

from bs4 import BeautifulSoup

soup = BeautifulSoup(html, 'html.parser') # 'lxml' или 'html5lib' — альтернативы

Разбор фрагмента:

  • BeautifulSoup(html, "html.parser") строит дерево документа из HTML-строки.
  • html.parser — встроенный парсер; для скорости часто используют lxml.
  • После инициализации объект soup предоставляет методы поиска (find, find_all, CSS-селекторы).

Поддерживаются различные парсеры:

  • html.parser — встроенный, не требует зависимостей.
  • lxml — быстрее, но требует установки lxml.
  • html5lib — наиболее корректно обрабатывает "грязный" HTML.

Поиск элементов:

# По тегу
title = soup.find('h1')
all_links = soup.find_all('a')

# По классу CSS
price = soup.find('div', class_='price')

# По атрибутам
img = soup.find('img', src=True)

# По нескольким условиям
product = soup.find('div', {'class': 'product', 'data-id': '123'})

Навигация по дереву:

# Дочерние элементы
for child in soup.div.children:
print(child.name)

# Родитель
parent = price.parent

# Соседи
next_elem = price.next_sibling

Извлечение данных:

text = element.get_text(strip=True) # Удаление лишних пробелов
href = link['href'] # Атрибут (выбрасывает KeyError, если нет)
src = img.get('src') # Безопасное получение (вернёт None)

Рассмотрим гипотетический сценарий — извлечение названий и цен товаров со страницы каталога. Цель - спарсить все товары с URL https://shop.example.com/category/laptops, получить:

  • название (<h3 class="title">)
  • цену (<span class="price">)
  • ссылку (<a href="...">)

Реализация:

Код ITЗагрузка примера кода…

Файл robots.txt определяет правила доступа для скраперов. Перед началом сбора данных его следует проверить:


import urllib.robotparser

rp = urllib.robotparser.RobotFileParser()
rp.set_url("https://example.com/robots.txt")
rp.read()

can_fetch = rp.can_fetch("MyBot", "/category/laptops")
print(can_fetch) # True/False

Даже если robots.txt разрешает доступ, это не означает, что скрапинг допустим юридически.

Чрезмерно частые запросы могут перегружать сервер. Рекомендуется добавлять задержки:


import time

for url in urls:
data = scrape(url)
time.sleep(1.5) # Пауза между запросами

Юридические и этические аспекты? Да, они есть. Нельзя парсить и скрапить всё подряд. Условия использования сайта могут запрещать автоматизированный сбор данных. Некоторые данные защищены авторским правом (например, тексты, цены). Обход CAPTCHA или авторизации может считаться нарушением закона (например, CFAA в США). Персональные данные (ФИО, email, телефоны) регулируются законами (GDPR, CCPA).

Для динамических страниц можно использовать Selenium.

Многие современные сайты используют JavaScript для загрузки контента. В таких случаях статический HTML, возвращаемый requests, не содержит нужных данных.

Когда использовать Selenium?

  • Контент подгружается через AJAX.
  • Необходимо взаимодействие — клики, прокрутка, форма входа.
  • Страница требует выполнения JS для отображения данных.

Это сторонняя библиотека, так что требуется установка:

pip install selenium

Требуется также драйвер браузера (например, chromedriver).

Пример: загрузка динамического контента:

Код ITЗагрузка примера кода…

Подключение к СУБД, ORM, миграции и эксплуатация баз — в отдельной главе: Работа с базами данных в Python.


Мини-чеклист безопасного скрапинга и интеграций

  • Проверяйте условия использования источника и robots.txt.
  • Выставляйте разумные timeout, retry и паузы между запросами.
  • Логируйте статус-коды и URL ошибок отдельно от успешных ответов.
  • Делайте валидацию структуры данных после парсинга.
  • Избегайте хранения токенов и паролей в коде, используйте переменные окружения.

Если проект активно общается с сетью, полезно параллельно изучать Сетевое программирование на Python и Асинхронность и многопоточность, чтобы понимать поведение I/O при росте нагрузки.


Практикум по популярным модулям интеграции

Этот блок дополняет базовый requests из главы и закрывает частые задачи production-интеграций.

HTTPX и таймауты


import httpx

with httpx.Client(timeout=httpx.Timeout(10.0, connect=2.0)) as client:
r = client.get("https://api.example.com/health")
r.raise_for_status()
data = r.json()

Разбор фрагмента:

  • httpx.Client(...) создаёт клиент с пулом соединений, чтобы переиспользовать TCP/TLS между запросами.
  • timeout=httpx.Timeout(10.0, connect=2.0) задаёт отдельный лимит на установку соединения и общий лимит чтения ответа.
  • client.get(...) отправляет GET-запрос.
  • raise_for_status() выбрасывает исключение при 4xx/5xx, чтобы ошибка не прошла тихо.
  • r.json() парсит JSON-ответ в словарь/список Python.

Повторы с Retrying

from retrying import retry

import requests

@retry(stop_max_attempt_number=4, wait_exponential_multiplier=500)
def load_profile(user_id: int) -> dict:
r = requests.get(f"https://api.example.com/users/{user_id}", timeout=5)
r.raise_for_status()
return r.json()

Разбор фрагмента:

  • @retry(...) декорирует функцию и автоматически перезапускает её при исключениях.
  • stop_max_attempt_number=4 ограничивает число попыток, чтобы скрипт не зациклился.
  • wait_exponential_multiplier=500 включает экспоненциальный backoff между ретраями.
  • timeout=5 ограничивает ожидание ответа.
  • Возврат r.json() делает функцию чистой: на выходе всегда готовые данные, а не объект Response.

Валидация ответа через Pydantic

from pydantic import BaseModel, HttpUrl

class Profile(BaseModel):
id: int
name: str
avatar: HttpUrl | None = None

Разбор фрагмента:

  • BaseModel описывает контракт данных.
  • Поле id: int гарантирует, что id должен быть числом.
  • avatar: HttpUrl | None проверяет корректность URL и допускает отсутствие значения.
  • При создании Profile(...) Pydantic валидирует типы и выбрасывает ValidationError при несоответствии.

Парсинг дат в естественном виде


import dateparser

ts = dateparser.parse("next monday 10:30")

Разбор фрагмента:

  • dateparser.parse(...) разбирает строку естественного языка в datetime.
  • Библиотека полезна для пользовательского ввода, где формат даты заранее не стандартизован.

Pymorphy2 и Pymystem3 для русского текста


import pymorphy2

from pymystem3 import Mystem

morph = pymorphy2.MorphAnalyzer()
mystem = Mystem()

print(morph.parse("книги")[0].normal_form) # книга
print(mystem.lemmatize("Книги лежали на столе."))

Разбор фрагмента:

  • MorphAnalyzer() загружает словари для морфологического анализа.
  • morph.parse("книги") возвращает варианты разбора; [0] берёт самый вероятный.
  • normal_form даёт лемму слова.
  • Mystem() использует движок Яндекса для лемматизации текста целиком.
  • lemmatize(...) возвращает список лемм и служебных токенов.

Прогресс и логирование в CLI-задачах

from tqdm import tqdm
from loguru import logger
from rich.console import Console

console = Console()
for page in tqdm(range(1, 21), desc="sync"):
logger.info("Обрабатываем страницу {}", page)
console.print("[green]Синхронизация завершена[/green]")

Разбор фрагмента:

  • tqdm(...) рисует прогресс-бар и показывает скорость обработки.
  • logger.info(...) пишет структурированный лог по каждой итерации.
  • Console().print(...) выводит итог с цветовой разметкой Rich.
  • Такой набор полезен для долгих CLI-процессов и batch-скриптов.

string и math для прикладной предобработки


import math
import string

def normalize_and_score(text: str) -> tuple[str, float]:
cleaned = text.strip().lower()
cleaned = cleaned.translate(str.maketrans("", "", string.punctuation))
words = [w for w in cleaned.split() if w]
score = math.log1p(len(words))
return " ".join(words), score

Разбор фрагмента:

  • string.punctuation содержит стандартный набор знаков пунктуации.
  • str.maketrans("", "", ...) строит таблицу удаления символов, а translate(...) применяет её к строке.
  • math.log1p(n) считает ln(1 + n) и стабильнее обычного log(1+n) на малых значениях.
  • В примере функция возвращает нормализованный текст и простой числовой скор.

Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.


См. также