Работа данными по сети#

Работа с HTTP/HTTPS#

Для работы с HTTP/HTTPS будем использовать модуль requests.

Отправка запроса#

import requests

# Выполнение запроса GET (получение данных с сервера)
ret = requests.get('http://site.example.com/page?a=b')

# Выполнение запроса POST (отправка данных на сервер)
ret = requests.post('http://site.example.com/page',
                    params={'form':'send',
                            'mail':'test@example.com'})

# Выполнение запроса GET с базовой авторизацией
from requests.auth import HTTPBasicAuth
requests.get('http://site.example.com/page', auth=HTTPBasicAuth('user', 'pass'))

# Выполнение запросов с поддержкой cookie
s = requests.Session() 
ret = s.post('http://site.example.com/login',data={'form':'send', 'user':'test@example.com', 'password':'pass'})
ret = s.get('http://site.example.com/page')
---------------------------------------------------------------------------
gaierror                                  Traceback (most recent call last)
File /usr/lib/python3.12/site-packages/urllib3/connection.py:174, in HTTPConnection._new_conn(self)
    173 try:
--> 174     conn = connection.create_connection(
    175         (self._dns_host, self.port), self.timeout, **extra_kw
    176     )
    178 except SocketTimeout:

File /usr/lib/python3.12/site-packages/urllib3/util/connection.py:72, in create_connection(address, timeout, source_address, socket_options)
     68     return six.raise_from(
     69         LocationParseError(u"'%s', label empty or too long" % host), None
     70     )
---> 72 for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
     73     af, socktype, proto, canonname, sa = res

File /usr/lib/python3.12/socket.py:976, in getaddrinfo(host, port, family, type, proto, flags)
    975 addrlist = []
--> 976 for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
    977     af, socktype, proto, canonname, sa = res

gaierror: [Errno -2] Name or service not known

During handling of the above exception, another exception occurred:

NewConnectionError                        Traceback (most recent call last)
File /usr/lib/python3.12/site-packages/urllib3/connectionpool.py:716, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    715 # Make the request on the httplib connection object.
--> 716 httplib_response = self._make_request(
    717     conn,
    718     method,
    719     url,
    720     timeout=timeout_obj,
    721     body=body,
    722     headers=headers,
    723     chunked=chunked,
    724 )
    726 # If we're going to release the connection in ``finally:``, then
    727 # the response doesn't need to know about the connection. Otherwise
    728 # it will also try to release it and we'll have a double-release
    729 # mess.

File /usr/lib/python3.12/site-packages/urllib3/connectionpool.py:416, in HTTPConnectionPool._make_request(self, conn, method, url, timeout, chunked, **httplib_request_kw)
    415     else:
--> 416         conn.request(method, url, **httplib_request_kw)
    418 # We are swallowing BrokenPipeError (errno.EPIPE) since the server is
    419 # legitimately able to close the connection after sending a valid response.
    420 # With this behaviour, the received response is still readable.

File /usr/lib/python3.12/site-packages/urllib3/connection.py:244, in HTTPConnection.request(self, method, url, body, headers)
    243     headers["User-Agent"] = _get_default_user_agent()
--> 244 super(HTTPConnection, self).request(method, url, body=body, headers=headers)

File /usr/lib/python3.12/http/client.py:1336, in HTTPConnection.request(self, method, url, body, headers, encode_chunked)
   1335 """Send a complete request to the server."""
-> 1336 self._send_request(method, url, body, headers, encode_chunked)

File /usr/lib/python3.12/http/client.py:1382, in HTTPConnection._send_request(self, method, url, body, headers, encode_chunked)
   1381     body = _encode(body, 'body')
-> 1382 self.endheaders(body, encode_chunked=encode_chunked)

File /usr/lib/python3.12/http/client.py:1331, in HTTPConnection.endheaders(self, message_body, encode_chunked)
   1330     raise CannotSendHeader()
-> 1331 self._send_output(message_body, encode_chunked=encode_chunked)

File /usr/lib/python3.12/http/client.py:1091, in HTTPConnection._send_output(self, message_body, encode_chunked)
   1090 del self._buffer[:]
-> 1091 self.send(msg)
   1093 if message_body is not None:
   1094 
   1095     # create a consistent interface to message_body

File /usr/lib/python3.12/http/client.py:1035, in HTTPConnection.send(self, data)
   1034 if self.auto_open:
-> 1035     self.connect()
   1036 else:

File /usr/lib/python3.12/site-packages/urllib3/connection.py:205, in HTTPConnection.connect(self)
    204 def connect(self):
--> 205     conn = self._new_conn()
    206     self._prepare_conn(conn)

File /usr/lib/python3.12/site-packages/urllib3/connection.py:186, in HTTPConnection._new_conn(self)
    185 except SocketError as e:
--> 186     raise NewConnectionError(
    187         self, "Failed to establish a new connection: %s" % e
    188     )
    190 return conn

NewConnectionError: <urllib3.connection.HTTPConnection object at 0x75c14c184950>: Failed to establish a new connection: [Errno -2] Name or service not known

During handling of the above exception, another exception occurred:

MaxRetryError                             Traceback (most recent call last)
File /usr/lib/python3.12/site-packages/requests/adapters.py:667, in HTTPAdapter.send(self, request, stream, timeout, verify, cert, proxies)
    666 try:
--> 667     resp = conn.urlopen(
    668         method=request.method,
    669         url=url,
    670         body=request.body,
    671         headers=request.headers,
    672         redirect=False,
    673         assert_same_host=False,
    674         preload_content=False,
    675         decode_content=False,
    676         retries=self.max_retries,
    677         timeout=timeout,
    678         chunked=chunked,
    679     )
    681 except (ProtocolError, OSError) as err:

File /usr/lib/python3.12/site-packages/urllib3/connectionpool.py:802, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    800     e = ProtocolError("Connection aborted.", e)
--> 802 retries = retries.increment(
    803     method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2]
    804 )
    805 retries.sleep()

File /usr/lib/python3.12/site-packages/urllib3/util/retry.py:594, in Retry.increment(self, method, url, response, error, _pool, _stacktrace)
    593 if new_retry.is_exhausted():
--> 594     raise MaxRetryError(_pool, url, error or ResponseError(cause))
    596 log.debug("Incremented Retry for (url='%s'): %r", url, new_retry)

MaxRetryError: HTTPConnectionPool(host='site.example.com', port=80): Max retries exceeded with url: /page?a=b (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x75c14c184950>: Failed to establish a new connection: [Errno -2] Name or service not known'))

During handling of the above exception, another exception occurred:

ConnectionError                           Traceback (most recent call last)
Cell In[1], line 4
      1 import requests
      3 # Выполнение запроса GET (получение данных с сервера)
----> 4 ret = requests.get('http://site.example.com/page?a=b')
      6 # Выполнение запроса POST (отправка данных на сервер)
      7 ret = requests.post('http://site.example.com/page',
      8                     params={'form':'send',
      9                             'mail':'test@example.com'})

File /usr/lib/python3.12/site-packages/requests/api.py:73, in get(url, params, **kwargs)
     62 def get(url, params=None, **kwargs):
     63     r"""Sends a GET request.
     64 
     65     :param url: URL for the new :class:`Request` object.
   (...)
     70     :rtype: requests.Response
     71     """
---> 73     return request("get", url, params=params, **kwargs)

File /usr/lib/python3.12/site-packages/requests/api.py:59, in request(method, url, **kwargs)
     55 # By using the 'with' statement we are sure the session is closed, thus we
     56 # avoid leaving sockets open which can trigger a ResourceWarning in some
     57 # cases, and look like a memory leak in others.
     58 with sessions.Session() as session:
---> 59     return session.request(method=method, url=url, **kwargs)

File /usr/lib/python3.12/site-packages/requests/sessions.py:589, in Session.request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)
    584 send_kwargs = {
    585     "timeout": timeout,
    586     "allow_redirects": allow_redirects,
    587 }
    588 send_kwargs.update(settings)
--> 589 resp = self.send(prep, **send_kwargs)
    591 return resp

File /usr/lib/python3.12/site-packages/requests/sessions.py:703, in Session.send(self, request, **kwargs)
    700 start = preferred_clock()
    702 # Send the request
--> 703 r = adapter.send(request, **kwargs)
    705 # Total elapsed time of the request (approximately)
    706 elapsed = preferred_clock() - start

File /usr/lib/python3.12/site-packages/requests/adapters.py:700, in HTTPAdapter.send(self, request, stream, timeout, verify, cert, proxies)
    696     if isinstance(e.reason, _SSLError):
    697         # This branch is for urllib3 v1.22 and later.
    698         raise SSLError(e, request=request)
--> 700     raise ConnectionError(e, request=request)
    702 except ClosedPoolError as e:
    703     raise ConnectionError(e, request=request)

ConnectionError: HTTPConnectionPool(host='site.example.com', port=80): Max retries exceeded with url: /page?a=b (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x75c14c184950>: Failed to establish a new connection: [Errno -2] Name or service not known'))

Разбор результата#

# Код состояния, в случае успеха 2xx, как правило 200
print(ret.status_code)

# MIME код типа данных ответа, например 'application/json'
print(ret.headers['content-type'])

# Данные в двоичном виде
print(ret.content)

# Текстовые данные (например HTML) можно декодировать средствами requests
print(ret.text)

Работа FTP#

Для передачи данных по FTP/FTPS будем использовать модуль стандартной библиотеки ftplib.

Установка соединения#

import ftplib
 
# Устанавливаем соединение
con = ftplib.FTP("ftp.example.com", "user", "password")

# Аналогично для FTPS
con = ftplib.FTP_TLS("ftp.example.com", "user", "password")

# Переходим в нужную папку на сервере
con.cwd("/var/tmp")

Передача файлов#

# Загрузка файла на сервер
fh = open(r"C:\upload\a.txt", 'rb')
con.storbinary('STOR a.txt', fh)
fh.close()

# Получение файла с сервера
fh = open(r"C:\download\b.txt", 'wb')
con.retrbinary('RETR b.txt', fh.write)
fh.close()

Любые файлы целесообразно передавать в binary режиме FTP. Команды storbinary и retrbinary принимают не просто имя файла, а соответствующую FTP команду.

Работа с SSH и sFTP#

Для работы с ssh используется модуль paramikopip.

Подготовка к установке соединения#

import paramiko

# Создание клиента
client = paramiko.SSHClient()

# Опционально - загрузка локальной связки ключей
client.load_system_host_keys()

# Можно ходить на хосты для которых нет ключей в связке
client.set_missing_host_key_policy(paramiko.WarningPolicy)

# Автоматически добавлять ключи в связку
client.set_missing_host_key_policy(paramiko.AutoAddPolicy)

Выполнение команд#

# Установка соединения с использованием пароля
client.connect("example.net", port=22, username="admin", password="password")

# Установка соединения с использованием сертификата
client.connect(hostname, username=myuser, key_filename="ssh_key.pub")

# Выполнение команд
stdin, stdout, stderr = client.exec_command("ls -la")

# stdin, stdout, stderr — псевдофайлы
print (stdout.read())

# Закрываем соединение
client.close()

Создание транспорта#

import paramiko

# Создаем специальный объект - транспорт
transport = paramiko.Transport(("example.net", 22))

# Установка соединения с использованием пароля
transport.connect(username="admin", password="password")

# Установка соединения с использованием сертификата
transport.connect(username=myuser, key_filename="ssh_key.pub")

# Получаем объект sftp

sftp = paramiko.SFTPClient.from_transport(transport)

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

# Скачиваем файлы
sftp.get("/home/admin/img.jpg", "img.jpg")

# Загружаем файлы
sftp.put("img2.jpg", "/home/admin/img2.jpg")

# Работаем непосредственно с файлом
fobj = sftp.open("/home/admin/img3.jpg", 'rb')

# Закрываем
fobj.close()
sftp.close()
transport.close()