Архив рубрики: Python

Django: как получить похожие посты на сайте – лучшие способы

Django: как получить похожие посты на сайте

В век цифровых технологий и интернета создание сайта является широко распространенным явлением среди проектов практически любой области. Однако не всегда создание сайта – это идеальное решение для удобства пользователей. Уже давно известно, что просто сделать сайт недостаточно – продвижение и улучшение его функционала играют соответственно ключевую роль в развитии сайта. В данном случае поможет настроить алгоритм поиска похожих постов на сайте. Это статья будет полезна тем людям, которые используют Django для создания своего собственного сайта и хотели бы настроить функцию поиска похожих постов.

Django делает это гораздо проще, чем может показаться. В Django есть подготовленный модуль `django.contrib.postgres.search`, который реализует полнотекстовый поиск на основе PostgreSQL. Обычный выборка, как правило, основывается на сравнении строк столбцов, что не очень эффективно в работе с большими объемами данных. Полнотекстовый поиск в свою очередь базируется на инфраструктуре, где индексируются узлы баз данных. Так что этот способ может считаться одним из наиболее эффективных.

В этой статье мы рассмотрим, как построить поиск контента на сайте, который будет использовать полнотекстовый поиск и давать более качественные результаты поиска похожих постов.

Как получить похожие посты на сайте Django

Существует несколько способов получения похожих постов на сайте Django:

  • Использование стандартных методов Django. Django предоставляет методы для поиска объектов по определенным критериям, таким как совпадение тегов, категорий или ключевых слов. Эти методы можно использовать для поиска похожих постов.

  • Использование сторонних библиотек. Существуют библиотеки, которые могут помочь в поиске похожих постов, используя алгоритмы машинного обучения или NLP (Natural Language Processing).

Пример использования стандартных методов Django:

  1. Создайте функцию, которая будет искать похожие посты по заданным критериям:

    *Пример поиска постов по тегам

    from django.db.models import Q

    def get_similar_posts(post):

    return Post.objects.filter(Q(tags__name__in=post.tags.names())

    &~Q(id=post.id)).distinct().order_by('-created_at')[:4]

  2. Вызовите эту функцию в представлении, передав в нее выбранный пост:

    def post_detail(request, post_id):

    post = get_object_or_404(Post, id=post_id)

    similar_posts = get_similar_posts(post)

    return render(request, 'blog/post_detail.html', {'post': post, 'similar_posts': similar_posts})

  3. Отобразите найденные похожие посты на странице шаблона:

    {% for similar_post in similar_posts %}

    {{ similar_post.title }}

    {{ similar_post.content }}

    {% endfor %}

Помимо стандартных методов Django, можно использовать сторонние библиотеки, такие как gensim, nltk, sklearn и другие, для более точного и эффективного поиска похожих постов. В этом случае, необходимо будет провести более детальную настройку и обучение алгоритма, что займет больше времени и ресурсов.

Что такое похожие посты

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

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

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

Предоставление похожих постов на сайте – это один из важных элементов повышения удобства использования сайта пользователем и повышения его эффективности как средства информационной связи.

Как определить похожие посты

Как определить похожие посты

Для определения похожих постов на сайте необходимо учитывать ряд факторов:

  • Ключевые слова и фразы: определение пересечения слов и фраз в текстах постов может показать насколько они схожи между собой.
  • Тема и контекст: тексты с похожим контекстом и тематическим наполнением могут быть более релевантными для пользователей.
  • Дата и время публикации: чем более свежи посты, тем более вероятно, что другие посты на ту же тему будут актуальны и интересны для пользователя.

Для реализации алгоритма определения похожих постов можно использовать различные методы:

  1. Математические модели: использование алгоритмов на основе статистических моделей и нахождения сходства с использованием метрик близости.
  2. Машинное обучение: обучение модели на основе обработки большого объема данных и определения закономерностей, что позволяет выделить характеристики, которые являются наиболее значимыми для определения схожести постов.
  3. Ранжирование: определение наиболее важных признаков, которые позволяют определить порядок ранжирования постов с наибольшей схожестью.

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

Косинусное расстояние

Косинусное расстояние — это мера сходства между двумя векторами в пространстве. Оно широко используется в задачах анализа данных, в том числе в поиске похожих объектов на сайте.

Для вычисления косинусного расстояния необходимо представить объекты в виде векторов с использованием численных признаков. Затем вычисляется косинус угла между векторами. Чем ближе косинусное расстояние к 1, тем больше сходство между объектами; чем ближе к 0, тем меньше сходство.

Применение косинусного расстояния может быть полезным в контексте создания системы рекомендаций для пользователей. Например, на основе сходства между векторами, можно определять похожие посты на сайте и предлагать их пользователям.

Хотя косинусное расстояние является простым и эффективным методом для вычисления сходства, его использование может столкнуться с недостатком при большом объеме данных, требующих расчетов. В таких случаях может быть более разумной альтернативой использование более сложных алгоритмов.

TF-IDF

TF-IDF – это аббревиатура от “term frequency/inverse document frequency” – статистический метод, используемый для оценки важности слова в текстах, основанных на их частоте встречаемости.

TF-IDF рассчитывает вес каждого слова в документах, с помощью того, как часто оно встречается в данном документе (term frequency), и насколько часто оно встречается во всех документах в корпусе (inverse document frequency). Чем чаще слово встречается в данном документе и реже в других документах, тем большую важность он получает.

TF-IDF может быть использован для решения различных задач, таких как классификация документов, информационный поиск, анализ тональности и многое другое. В рекомендательных системах, TF-IDF может быть использован для нахождения похожих постов, основываясь на содержании.

Для реализации TF-IDF алгоритма в Django, необходимо сперва построить индекс терминов, который будет содержать все уникальные слова во всех документах. Затем, для каждого слова рассчитывается его вес, используя формулу TF-IDF. Далее, для поиска похожих постов на сайте, мы можем проанализировать вес каждого слова в каждом посте и найти наиболее похожие.

  • Ключевые шаги реализации алгоритма TF-IDF:
    1. Получить все посты из базы данных;
    2. Очистить каждый пост от лишних символов, стоп-слов, HTML тегов;
    3. Cоздать индекс терминов из всех слов в постах;
    4. Рассчитать TF-IDF для каждого слова в каждом посте, используя индекс терминов;
    5. Проанализировать вес каждого слова в каждом посте и найти наиболее похожие;

Таким образом, TF-IDF является мощной техникой для анализа и оценки текстовых данных, которая может использоваться для различных задач в информационных системах.

Как реализовать получение похожих постов на сайте Django

Для того чтобы реализовать получение похожих постов на сайте Django, необходимо использовать алгоритмы машинного обучения. В Django применяется метод поиска похожих объектов на основе векторного представления данных.

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

Далее, необходимо создать модель и обучить её на основе данных о постах. Можно использовать библиотеки машинного обучения, такие как scikit-learn, чтобы создать векторное представление данных и найти сходство между постами.

Полученные результаты можно отобразить на странице с помощью шаблонов Django. Для этого необходимо написать соответствующий код, который будет отображать список похожих постов для каждого поста на странице.

Важно помнить, что результаты будут зависеть от качества данных, использованных в обучении модели. Поэтому необходимо проверять и обновлять данные регулярно, чтобы получать наиболее точные результаты.

В итоге, реализация получения похожих постов на сайте Django – это достаточно сложная задача, которая требует использования алгоритмов машинного обучения и хорошо структурированных данных. Однако, с помощью правильного подхода и использования соответствующих библиотек, можно достичь хороших результатов.

Установка библиотеки scikit-learn

Scikit-learn – это библиотека для машинного обучения на языке Python. Эта библиотека содержит широкий спектр алгоритмов машинного обучения и инструментов для работы с данными. Scikit-learn является одной из наиболее популярных библиотек машинного обучения в Python и используется во многих научных и коммерческих проектах.

Установка библиотеки scikit-learn в Python обычно происходит с помощью менеджера пакетов pip. Для того чтобы установить scikit-learn, необходимо открыть командную строку и выполнить команду:

pip install scikit-learn

Эта команда установит последнюю версию библиотеки. Если вам нужна конкретная версия библиотеки, вы можете указать ее номер вместо слова “latest”:

pip install scikit-learn==0.23.2

После установки библиотеки вы можете использовать ее в своих проектах. Для этого необходимо импортировать ее в коде:

import sklearn

Используя библиотеку scikit-learn вы можете создавать модели машинного обучения для решения различных задач, таких как классификация, регрессия, кластеризация и т.д.

Например, чтобы обучить модель линейной регрессии на данных, вы можете создать объект класса LinearRegression из библиотеки scikit-learn:

from sklearn.linear_model import LinearRegression

model = LinearRegression()

model.fit(X, y)

где X – это матрица объектов-признаков, а y – это вектор целевых значений.

Реализация в Django

В Django реализация получения похожих постов на сайте может осуществляться с помощью фреймворка Elasticsearch. Для начала нужно установить необходимые модули:

  • elasticsearch-dsl – модуль для работы с Elasticsearch на уровне Python кода;
  • jieba – китайский анализатор текстов.

После установки модулей можно приступать к написанию кода. Сначала нужно определить индексы, которые будут использованы Elasticsearch. Для этого создаем файл search_indexes.py:

from haystack import indexes

from myapp.models import Post

class PostIndex(indexes.SearchIndex, indexes.Indexable):

text = indexes.CharField(document=True, use_template=True)

def get_model(self):

return Post

def index_queryset(self, using=None):

return self.get_model().objects.all()

Здесь мы определяем индекс для модели Post, указываем, что поле text будет индексироваться и сохраняем индекс идентификатора модели. Также обычно настраиваются параметры анализатора методом prepare.

Затем создадим файл search_views.py, определив представление для поиска похожих постов и сериализатор для вывода результатов в формате JSON:

from django.core.paginator import Paginator

from django.http import JsonResponse

from elasticsearch_dsl import Q

from elasticsearch_dsl.search import Search

from jieba.analyse import ChineseAnalyzer

from myapp.search_indexes import PostIndex

from myapp.models import Post

analyzer = ChineseAnalyzer()

def search_similar_posts(request, post_id):

post = Post.objects.get(id=post_id)

text = post.text

similar_posts = get_similar_posts(text)

paginator = Paginator(similar_posts, 10)

page_number = request.GET.get('page')

page_obj = paginator.get_page(page_number)

data = {

'similar_posts': [{"id": post.id, "title": post.title, "text": post.text} for post in page_obj],

'has_previous': page_obj.has_previous(),

'previous_page_number': page_obj.previous_page_number() if page_obj.has_previous() else None,

'number': page_obj.number,

'num_pages': paginator.num_pages,

'has_next': page_obj.has_next(),

'next_page_number': page_obj.next_page_number() if page_obj.has_next() else None

}

return JsonResponse(data)

def get_similar_posts(text):

s = Search().using('default').indexes('search')

s = s.query(Q('more_like_this', fields=['text'], like=text, max_query_terms=30, analyzer=analyzer))

s = s.filter('terms', model=['post'])

response = s.execute()

posts = [p for p in Post.objects.filter(id__in=[hit.id for hit in response])]

return posts

Здесь мы используем анализатор китайского языка и методы Elasticsearch для поиска похожих постов.

Таким образом, реализация поиска похожих постов в Django с помощью Elasticsearch довольно проста и может быть легко интегрирована на сайте.

Вопрос-ответ:

Зачем нужна функция “похожие посты” в Django?

Функция “похожие посты” позволяет предложить пользователю на сайте похожие статьи, которые могут быть ему интересны. Это повышает удобство использования сайта, увеличивает время, проведенное на нем, и улучшает взаимодействие пользователя с контентом.

Как работает функция “похожие посты” в Django?

Функция “похожие посты” в Django основывается на анализе текстовых данных статей. Алгоритм анализирует вес и частоту встречаемости ключевых слов, которые задаются в соответствии с тематикой статьи. На основе полученных данных функция выводит список максимально похожих статей и предлагает их пользователю.

Какие дополнительные модули нужны для работы функции “похожие посты” в Django?

Для работы функции “похожие посты” в Django необходимо установить модуль scikit-learn, который предоставляет инструменты для анализа данных и машинного обучения. Также нужно убедиться, что в проекте настроена поддержка Natural Language Toolkit, которая используется для обработки текстовых данных.

Как можно определить, насколько точны результаты работы функции “похожие посты” в Django?

Определить точность работы функции “похожие посты” можно с помощью метрик, таких как precision и recall. Precision позволяет оценить, как много результатов, полученных алгоритмом, являются действительно похожими на исходную статью. Recall, в свою очередь, показывает, насколько много похожих статей было найдено в общем количестве статей на сайте.

Можно ли ускорить работу функции “похожие посты” в Django?

Для ускорения работы функции “похожие посты” в Django можно использовать кэширование результатов, то есть сохранять вычисления в памяти и предоставлять их при повторном запросе. Также можно увеличить скорость работы алгоритма, используя более оптимизированные алгоритмы машинного обучения.

Как использовать функцию “похожие посты” в Django для увеличения трафика на сайте?

Использование функции “похожие посты” в Django позволяет увеличить время, проводимое пользователем на сайте, что в свою очередь повышает вероятность его возвращения. Также можно использовать эту функцию для увеличения трафика на сайте, предлагая пользователям похожие статьи на сайте и за его пределами, например, через социальные сети и мессенджеры.

Видео:

Мастер-класс по веб-разработке на Django

Мастер-класс по веб-разработке на Django by Академия Яндекса 9 months ago 57 minutes 4,578 views

9 URL и View(представление): что это такое и для чего они нужны Django

9 URL и View(представление): что это такое и для чего они нужны Django by egoroff_channel 1 year ago 5 minutes, 35 seconds 12,906 views

Сообщение Django: как получить похожие посты на сайте – лучшие способы появились сначала на Программирование на Python.

Что такое “self” в Python?

В языке Python “self” обозначает сам объект класса. С его помощью можно получить доступ к атрибутам и методам класса.

Зеленый круг с надписью Class. Из него выходит и в него же возвращается синяя стрелка, подписанная как self.

Например, класс Fruit при создании присваивает себе пользовательские имя и цвет (name и color). Затем к ним можно будет получить доступ с помощью метода info():

class Fruit:
    def __init__(self, name, color):
        self.name = name
        self.color = color
    
    def info(self):
        print(self.color, self.name)
    
banana = Fruit("Banana", "Yellow")
banana.info()

# Вывод:
# Yellow Banana

Чтобы понять, как это работает, давайте подробнее разберемся с “self” в Python. Это руководство предназначено для тех, кто уже знаком с классами, но кому понятие “self” кажется несколько туманным.

Ключевое слово “self” в Python

В Python класс – это схема для создания объектов.

Каждый объект Python является представителем некоторого класса. В Python объект также называется экземпляром класса. Если вы хотите создать объект в Python, у вас должен быть класс, на основе которого вы можете его создать.

Типичный класс Python состоит из методов, которые выполняют действия на основе атрибутов, присвоенных объекту.

Например, класс Weight может хранить килограммы и иметь возможность конвертировать их в фунты.

В классе Python параметр self ссылается на сам экземпляр класса. Это полезно, поскольку в противном случае не было бы возможности получить доступ к атрибутам и методам класса.

Пример: класс Fruit

Чтобы лучше понять роль “self” в классе, давайте рассмотрим простой класс Fruit. Он позволяет создавать объекты Fruit с пользовательскими именем и цветом (name и color):

class Fruit:
    def __init__(self, name, color):
        self.name = name
        self.color = color
    
    def info(self):
        print(self.color, self.name)

Теперь вы можете создавать объекты Fruit. Делается это так:

banana = Fruit("Banana", "Yellow")
apple = Fruit("Apple", "Red")

И вы можете вывести информацию, связанную с ними:

banana.info()
apple.info()

Вывод:

Yellow Banana
Red Apple

Как работает “self” в этом примере?

Рассмотрим, как работает класс Fruit.

При создании нового объекта Fruit под капотом вызывается метод __init__. Он отвечает за создание объектов. Этот метод принимает три аргумента:

  1. self
  2. name
  3. color

При создании объекта Fruit метод __init__ вызывается с пользовательскими именем и цветом. Обратите внимание, что вам не нужно передавать self в качестве аргумента. Python делает это автоматически.

Таким образом, при вызове banana = Fruit("Banana", "Red"):

  • Метод __init__ начинает инициализацию объекта Banana с заданными аргументами name и color.
  • Он создает новые атрибуты self.name и self.color и сохраняет в них введенные данные name и color.
  • После этого можно получить доступ к атрибутам Banana name и color в любом месте объекта через self.name и self.color.

Посмотрим также, что произойдет при вызове banana.info().

Когда вы вызываете banana.info(), Python автоматически передает self в вызов метода в качестве аргумента. После этого метод info() может использовать self для доступа к атрибутам объекта name и color. Без self он не смог бы этого сделать.

В Python “self” может содержать что угодно

Зеленый круг Class. Внутрь круга, к слову self, ведут стрелки извне, от слов name, age, height, weight, sayHi(), showInfo(), add().

Ранее вы видели, как параметр self может использоваться для хранения атрибутов объектов.

Точнее, вы увидели типичный подход, когда вы передаете __init__ аргументы метода и присваиваете их self с помощью того же имени:

def __init__(self, name, color):
    self.name = name
    self.color = color

В связи с этим может возникнуть вопрос: “Должны ли имена аргументов совпадать с теми, что присваиваются self?”. Ответ – нет.

Подобно тому, как в Python можно создать любую переменную в любом месте, через self вы можете присвоить объекту любые атрибуты.

Это означает, что вы можете хранить в self любые значения с любыми именами. Их даже не обязательно передавать в качестве аргументов в метод __init__.

Пример: класс Point

Например, создадим класс Point, представляющий точку в трехмерном пространстве. Давайте инициализируем объекты Point в начале координат, не запрашивая координаты в качестве аргументов при инициализации.

Для этого необходимо присвоить xy и z значения 0 для self в методе __init__:

class Point:
    def __init__(self):
        self.x = 0
        self.y = 0
        self.z = 0

Теперь можно создавать объекты Point, привязанные к началу координат:

p = Point()
print(p.x, p.y, p.z)

# Вывод:
# 0 0 0 

Этим мы хотим продемонстрировать, как можно свободно добавлять любые атрибуты к свойству объекта self.

Может ли метод не иметь аргумента “self”?

В Python метод нуждается в аргументе self. В противном случае он не знает, к какому классу принадлежит, и не может выполнять над ним действия.

Чтобы посмотреть, что произойдет, если метод не будет принимать аргумент self , давайте пропустим его в методе info() :

class Fruit:
    def __init__(self, name, color):
        self.name = name
        self.color = color
    
    def info():
        print(self.color, self.name)
banana = Fruit("Banana", "Yellow")
banana.info()

Вывод:

Traceback (most recent call last):
  File "main.py", line 10, in <module>
    banana.info()
TypeError: info() takes 0 positional arguments but 1 was given

Последняя строка говорит о том, что метод info() не принимает аргументов, а мы передали один. Но, судя по приведенному выше коду, мы не передавали никаких аргументов. Так почему же интерпретатор думает, что мы это сделали?

Даже если вы не передаете методу info() никаких аргументов,  Python автоматически пытается внедрить self в вызов. Это происходит потому, что Python знает, что для корректной работы все методы класса должны иметь ссылку на сам класс. Но в нашей реализации info() нет аргумента self. Поэтому передача в нее аргумента self за кулисами не удастся.

Это показывает, что в методах необходимо использовать аргумент self.

Доказательство того, что “self” относится к самому объекту

Для наглядности докажем, что self действительно относится к самому объекту.

Для этого проверим расположение в памяти self и объекта Fruit.

В языке Python адрес объекта в памяти можно проверить с помощью функции id(). Класс Fruit упрощен для облегчения восприятия.

class Fruit:
    def __init__(self):
        print("Self address =", id(self))
 
fruit = Fruit()
print("Object address =", id(fruit))

Вывод:

Self address = 140243905604576
Object address = 140243905604576

Как видно, адреса памяти одинаковы. Это говорит о том, что self внутри класса Fruit указывает на тот же адрес, что и объект fruit, который вы только что создали. Другими словами, параметр self действительно ссылается на сам объект.

Ключевое слово “self” не является зарезервированным в Python

Обратите внимание, что self не является зарезервированным ключевым словом в Python. Вместо него можно использовать любое имя, лишь бы оно было.

Зеленый круг, подписанный Class. Из него выходит и в него же возвращается синяя стрелка с рядом подписей на выбор: self, this, me, myself, whatever.

Например, изменим реализацию класса Fruit из предыдущих разделов. На этот раз вместо имени self будем использовать this:

class Fruit:
    def __init__(this, name, color):
        this.name = name
        this.color = color
    
    def info(this):
        print(this.color, this.name)
banana = Fruit("Banana", "Yellow")
apple = Fruit("Apple", "Red")

Этот код работает точно так же, как и тот, в котором используется self.

Но поскольку Python-разработчики чаще всего используют self, не рекомендуется использовать другие слова. При работе с классами в Python вы вряд ли столкнетесь с чем-то еще, кроме self.

Заключение

Сегодня вы узнали, что “self” делает в классе Python.

Напомним, что self в Python обозначает сам объект. Благодаря ему класс знает, как получить доступ к своим собственным атрибутам и методам.

При создании объекта вызывается метод __init__. В этом методе обычно присваиваются атрибуты экземпляру класса через self.

Обратите внимание, что “self” не является зарезервированным ключевым словом. Вместо него можно использовать любое другое слово, если передавать его в качестве первого аргумента для каждого метода класса. Однако  “self” настолько часто используется, что не следует использовать что-либо другое, даже если это возможно.

Спасибо, что прочитали. Надеюсь, вы нашли то, что искали. Удачного кодинга!

Перевод статьи «What Is ‘self’ in Python? A Complete Guide (with Examples)».

Сообщение Что такое “self” в Python? появились сначала на pythonturbo.


Source: pythonturbo.ru

Библиотека urllib в Python

В этой статье мы вам расскажем, как библиотека urllib применяется в Python для веб-скреппинга, выполнения API-запросов и т.д. Вы познакомитесь с модулями urllib и лучшими практиками использования этой библиотеки.

Содержание

Введение в urllib

Urllib – это библиотека Python, предоставляющая набор модулей для работы с URL (Uniform Resource Locators). Она позволяет взаимодействовать с веб-ресурсами, выполняя HTTP-запросы, разбирая URL и обрабатывая различные аспекты веб-коммуникаций.

Зачем нужна библиотека urllib? Это мощный инструмент для решения веб-задач на языке Python. Библиотека широко используется для веб-скреппинга, выполнения API-запросов, загрузки файлов из Интернета и т.д. С помощью urllib можно автоматизировать различные процессы, связанные с веб, что делает ее незаменимой библиотекой для веб-разработчиков и специалистов по исследованию данных.

Установка urllib

Urllib входит в состав стандартной библиотеки Python, поэтому ее не нужно устанавливать отдельно. Вы можете начать использовать ее, импортировав соответствующие модули в свой Python-скрипт.

Эта библиотека доступна как в Python 2, так и в Python 3, однако его использование может несколько отличаться в разных версиях. Рекомендуется использовать Python 3, так как Python 2 больше не поддерживается.

Модули urllib

Библиотека urllib состоит из нескольких модулей, каждый из которых выполняет определенную задачу. Рассмотрим основные из них.

urllib.request

Модуль urllib.request предоставляет функции для выполнения HTTP-запросов, включая GET- и POST-запросы, и обработки ответов.

import urllib.request

# Пример: отправка GET-запроса
response = urllib.request.urlopen('https://example.com')
html = response.read()
print(html)

urllib.parse

Модуль urllib.parse предназначен для парсинга URL-адресов. Он разбивает их их на такие компоненты, как scheme, netloc, path, query и fragment.

import urllib.parse

# Пример: парсинг URL
url = 'https://www.example.com/path?param=value'
parsed_url = urllib.parse.urlparse(url)
print(parsed_url)

urllib.error

Модуль urllib.error обрабатывает исключения и ошибки, которые могут возникать при выполнении HTTP-запросов.

import urllib.error

try:
    response = urllib.request.urlopen('https://nonexistent-url.com')
except urllib.error.HTTPError as e:
    print(f'HTTP Error: {e.code}')
except urllib.error.URLError as e:
    print(f'URL Error: {e.reason}')

urllib.robotparser

Модуль urllib.robotparser используется для разбора файлов robots.txt, чтобы проверить, разрешен ли доступ веб-краулеру к определенным частям сайта.

import urllib.robotparser

rp = urllib.robotparser.RobotFileParser()
rp.set_url('https://example.com/robots.txt')
rp.read()
allowed = rp.can_fetch('MyCrawler', 'https://example.com/page')
print(allowed)

Основные HTTP-запросы

Отправка GET-запросов

Получение веб-содержимого при помощи GET-запросов – одна из основных операций в urllib.

import urllib.request

response = urllib.request.urlopen('https://example.com')
html = response.read()
print(html)

Отправка POST-запросов

POST-запросы служат для отправки данных на сервер. Они часто используются в веб-формах.

import urllib.request
import urllib.parse

data = urllib.parse.urlencode({'param1': 'value1', 'param2': 'value2'}).encode('utf-8')
response = urllib.request.urlopen('https://example.com/post', data=data)
html = response.read()
print(html)

Работа с HTTP-ответами

Вы можете получить доступ к различным свойствам HTTP-ответа, таким как код состояния, заголовки и содержимое.

import urllib.request

response = urllib.request.urlopen('https://example.com')
status_code = response.getcode()
headers = response.info()
html = response.read()
print(f'Status Code: {status_code}')
print(f'Headers: {headers}')
print(html)

Обработка ошибок HTTP

Urllib обеспечивает обработку ошибок, связанных с HTTP, таких как 404 Not Found или ошибки подключения.

import urllib.error

try:
    response = urllib.request.urlopen('https://nonexistent-url.com')
except urllib.error.HTTPError as e:
    print(f'HTTP Error: {e.code}')
except urllib.error.URLError as e:
    print(f'URL Error: {e.reason}')

Работа с URL-адресами

Парсинг URL-адресов

При помощи модуля urllib.parse можно разобрать URL на составляющие.

import urllib.parse

url = 'https://www.example.com/path?param=value'
parsed_url = urllib.parse.urlparse(url)
print(f'Scheme: {parsed_url.scheme}')
print(f'Netloc: {parsed_url.netloc}')
print(f'Path: {parsed_url.path}')
print(f'Query: {parsed_url.query}')

Конструирование URL-адресов

Вы можете конструировать URL, комбинируя их компоненты с помощью urllib.parse.urlunparse() или добавляя параметры запроса к существующему URL.

import urllib.parse

components = ('https', 'example.com', 'path', '', 'param=value', '')
constructed_url = urllib.parse.urlunparse(components)
print(constructed_url)

Продвинутые приемы использования urllib

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

Urllib может работать с cookies с помощью модуля http.cookiejar. Он позволяет управлять данными сессии между запросами.

import urllib.request
import http.cookiejar

# Create a cookie jar to store cookies
cookie_jar = http.cookiejar.CookieJar()
# Create an opener with the cookie jar
cookie_handler = urllib.request.HTTPCookieProcessor(cookie_jar)
opener = urllib.request.build_opener(cookie_handler)
# Make a GET request to a website that sets cookies
url = 'https://httpbin.org/cookies/set?cookie1=value1&cookie2=value2'
response = opener.open(url)
# Check if cookies have been received and stored
if cookie_jar:
    print("Cookies Received:")
    for cookie in cookie_jar:
        print(f"{cookie.name}: {cookie.value}")

Работа с заголовками

Вы можете манипулировать HTTP-заголовками для включения в запросы дополнительной информации, например, User-Agent или пользовательских заголовков.

import urllib.request

url = 'https://example.com'
headers = {'User-Agent': 'My User Agent'}
req = urllib.request.Request(url, headers=headers)
response = urllib.request.urlopen(req)

Работа с перенаправлениями

Urllib может автоматически следовать за HTTP-перенаправлениями. При необходимости это поведение можно отключить.

import urllib.request

# Create a Request object with a URL that redirects
url = 'http://www.example.com'  # This URL redirects to 'https://www.example.com'
req = urllib.request.Request(url, headers={'User-Agent': 'My User Agent'})
# Open the URL without following redirects
response = urllib.request.urlopen(req, allow_redirects=False)
# Check the response status code to see if it's a redirect
if response.status == 302 or response.status == 301:
    print(f'Redirect detected: Status Code {response.status}')
else:
    final_url = response.geturl()  # Get the final URL
    print(f'Final URL: {final_url}')

Работа с тайм-аутами

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

import urllib.request
import urllib.error

url = 'https://example.com'
try:
    response = urllib.request.urlopen(url, timeout=10)  # Set a timeout of 10 seconds
    html = response.read()
    print(html)
except urllib.error.URLError as e:
    if isinstance(e.reason, socket.timeout):
        print("Request timed out.")
    else:
        print(f"URL Error: {e.reason}")

Веб-скрепинг с помощью urllib

Получение HTML-содержимого

Urllib можно использовать для веб-скрапинга, посылая GET-запросы на сайты и получая HTML-контент.

import urllib.request

url = 'https://example.com'
response = urllib.request.urlopen(url)
html = response.read()

Парсинг HTML с помощью BeautifulSoup

Для извлечения данных из HTML можно объединить urllib с библиотекой типа BeautifulSoup.

import urllib.request
from bs4 import BeautifulSoup

# Send a GET request to a web page and retrieve its HTML content
url = 'https://example.com'
response = urllib.request.urlopen(url)
html = response.read()
# Parse the HTML content using BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')
# Find and print a specific element from the HTML (e.g., the page title)
title_element = soup.find('title')
if title_element:
    print('Page Title:', title_element.text)
else:
    print('Title not found on the page.')

Сбор данных с веб-страниц

Вы можете собирать данные с веб-страниц, определяя нужные HTML-элементы и извлекая их содержимое с помощью BeautifulSoup.

import urllib.request
from bs4 import BeautifulSoup

# URL of the web page to scrape
url = 'https://example-news-site.com'

# Send an HTTP GET request to the URL
response = urllib.request.urlopen(url)

# Read the HTML content of the page
html = response.read()

# Parse the HTML content using BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')

# Find and extract article titles
article_titles = []

# Assuming article titles are in h2 tags with a specific class
for h2_tag in soup.find_all('h2', class_='article-title'):
    article_titles.append(h2_tag.text)

# Print the extracted article titles
for title in article_titles:
    print(title)

Примечание редакции: на тему скрапинга читайте также “Скрапинг с помощью Python и Selenium”.

Библиотека urllib и работа с API

Выполнение GET-запросов к API

С помощью urllib можно выполнять GET-запросы к API и получать данные.

import urllib.request

api_url = 'https://api.example.com/data'
response = urllib.request.urlopen(api_url)
data = response.read()
# Parse the JSON response if applicable.

Выполнение POST-запросов к API

Аналогичным образом можно отправлять POST-запросы к API, включив необходимые данные в тело запроса.

import urllib.request
import urllib.parse

data = urllib.parse.urlencode({'param1': 'value1', 'param2': 'value2'}).encode('utf-8')
api_url = 'https://api.example.com/data'
response = urllib.request.urlopen(api_url, data=data)
data = response.read()
# Parse the JSON response if applicable.

Работа с ответами в формате JSON

Многие API-интерфейсы возвращают данные в формате JSON, поэтому для их анализа и работы с ними можно использовать модуль json языка Python.

import urllib.request
import json

api_url = 'https://api.example.com/data'
response = urllib.request.urlopen(api_url)
data = json.loads(response.read().decode('utf-8'))

Скачивание файлов

Загрузка файлов из Интернета

Вы можете использовать urllib для загрузки файлов из Интернета, например, изображений, PDF-файлов или других документов.

import urllib.request

file_url = 'https://example.com/file.pdf'
urllib.request.urlretrieve(file_url, 'downloaded_file.pdf')

Обработка загрузки больших файлов

Для загрузки больших файлов можно использовать потоковый подход для экономии памяти.

import urllib.request

file_url = 'https://example.com/large_file.zip'
with urllib.request.urlopen(file_url) as response, open('downloaded_file.zip', 'wb') as out_file:
    while True:
        data = response.read(4096)
        if not data:
            break
        out_file.write(data)

Лучшие практики

Обработка ошибок

Всегда обрабатывайте исключения и ошибки при выполнении HTTP-запросов или работе с URL-адресами, чтобы обеспечить надежность кода.

import urllib.error
import urllib.request

try:
    response = urllib.request.urlopen('https://nonexistent-url.com')
except urllib.error.HTTPError as e:
    print(f'HTTP Error: {e.code}')
except urllib.error.URLError as e:
    print(f'URL Error: {e.reason}')
else:
    # Code to execute if there are no errors
    html = response.read()
    print(html)

Заголовки User-Agent

Устанавливайте в запросах заголовок User-Agent для идентификации вашего скрипта или приложения при взаимодействии с веб-сайтами или API.

import urllib.request

# Define the User-Agent header
user_agent = 'My Custom User Agent'
# Create a request object with the User-Agent header
url = 'https://example.com'
headers = {'User-Agent': user_agent}
req = urllib.request.Request(url, headers=headers)
# Send the request
response = urllib.request.urlopen(req)
# Now you can work with the response as needed
html = response.read()
print(html)

Соблюдайте правила Robots.txt

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

import urllib.robotparser

# Create a RobotFileParser object and specify the URL of the website's robots.txt file.
rp = urllib.robotparser.RobotFileParser()
rp.set_url('https://example.com/robots.txt')
# Read and parse the robots.txt file.
rp.read()
# Check if it's allowed to crawl a specific URL.
is_allowed = rp.can_fetch('MyCrawler', 'https://example.com/some-page')
if is_allowed:
    print("Crawling is allowed for this URL.")
else:
    print("Crawling is not allowed for this URL according to robots.txt.")

Ограничение скорости

При выполнении запросов к API следует соблюдать все политики ограничения скорости, чтобы не перегружать сервер.

import urllib.request
import time

# Define the API URL and the rate limit (requests per minute)
api_url = 'https://api.example.com/data'
rate_limit = 60  # 60 requests per minute
# Function to make an API request with rate limiting
def make_api_request_with_rate_limit(url):
    # Calculate the time interval between requests
    time_interval = 60 / rate_limit  # 60 seconds in a minute
    time_since_last_request = time.time() - last_request_time
    if time_since_last_request < time_interval:
        time.sleep(time_interval - time_since_last_request)
    response = urllib.request.urlopen(url)
    return response.read()
# Initialize the time of the last request
last_request_time = time.time()
# Make API requests with rate limiting
for _ in range(10):  # Make 10 requests
    data = make_api_request_with_rate_limit(api_url)
    print(data)
# Update the time of the last request
last_request_time = time.time()

Заключение

Urllib – это универсальная библиотека на языке Python, позволяющая работать с URL-адресами, выполнять HTTP-запросы и эффективно взаимодействовать с веб-ресурсами. Если вы собираете данные с сайтов, взаимодействуете с API или загружаете файлы из Интернета,  эта библиотека вам точно пригодится. Познакомившись с ее модулями, вы сможете использовать всю ее мощь для решения задач, связанных с веб.

Перевод статьи «urllib in Python».

Сообщение Библиотека urllib в Python появились сначала на pythonturbo.


Source: pythonturbo.ru

Наиболее используемые функции Pandas

Pandas – одна из самых популярных и используемых библиотек Python. Функций в ней так много, что бывает трудно запомнить все. Но запомнить основые вполне возможно. В этой статье мы рассмотрим некоторые функции Pandas из числа наиболее используемых.

1. Считывание CSV-файла

Существует множество случаев, когда данные представлены в CSV-файле. Для загрузки таких файлов мы используем функцию read_csv(). Она имеет следующие параметры:

  • filepath – адрес файла для чтения в кавычках
  • sep для указания разделителя (по умолчанию – запятая)
  • header для указания номера строки, содержащей метки столбцов
  • names  (опциональный) для явного указания меток столбцов
  • index_col (опциональный) для указания, какой столбец используется в качестве меток строк

Пример:

df = pd.read_csv('train.csv')

Примечание редакции: подробнее о чтении CSV читайте в статье “Как прочитать CSV-файл в Python”.

2. head() и tail()

Для отображения первых и последних строк данных мы используем функции head() и tail() соответственно. Мы можем указать, сколько строк нужно вывести, передав число в функцию. По умолчанию она выводит пять строк.

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

Вывод первых строк:

df.head()

Результат:

Таблица из пяти строк - вывод функции df.head() без указания количества выводимых строк.

Вывод двух последних строк:

df.tail(2)

Результат:

Таблица из двух строк, результат работы функции df.tail(2)

3. Shape

Для отображения размеров DataFrame, т.е. количества строк и столбцов, мы используем атрибут shape.

df.shape

# Результат:
#(891, 12)

4. info()

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

df.info()
Вывод функции df.info()

Функция info() выводит общее количество записей в данных вместе с диапазоном. Приведенные данные имеют 11 столбцов, которые относятся к типам float, int и object (string). Они занимают 83,7 КБ памяти.

5. Describe

С помощью функции Pandas describe() мы можем вывести множество статистических значений в нескольких столбцах при помощи всего одной строки кода.

Для непрерывных переменных эта функция возвращает количество, среднее значение, медиану, стандартное отклонение, 25-й и 75-й процентили, максимальное и минимальное значения.

df.describe()
Вывод функции Pandas describe(): таблица на 7 столбцов

Функция describe() имеет параметр percentiles, в котором мы можем указать перцентили, которые хотим включить в вывод.

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

df.describe(include='all')

Результат:

Вывод функции df.describe(include='all'). Таблица на 12 столбцов.

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

Мы можем указать, хотим ли мы отображать только столбцы одного типа, например, числовые, объектные или категориальные. Давайте выведем только строковые:

df.describe(include = [np.object])
# используйте include = ['category'] для категориальных столбцов
# используйте include = [np.number] для числовых столбцов

Результат:

Таблица на 5 столбцов и 4 строки

Мы также можем использовать describe() для конкретного столбца (столбцов). Если же мы хотим удалить некоторые столбцы из сводки статистики, можно использовать параметр exclude.

6. value_counts

Для вычисления и отображения частотности каждого значения в столбце DataFrame используется функция value_counts().

df['Survived'].value_counts()

# Результат:
# 0    549
# 1    342
# Name: Survived, dtype: int64

Мы можем получить процентные значения для каждого уникального элемента в столбце, используя аргумент normalize=True.

df['Survived'].value_counts(normalize=True)*100

# Результат:
# 0    61.616162
# 1    38.383838
# Name: Survived, dtype: float64

Для наглядного представления чисел можно вывести график:

df['Survived'].value_counts(normalize=True).plot.bar()
Столбцовая диаграмма на два столбца: 0 и 1

7. Drop

Иногда нам необходимо удалить из данных некоторые столбцы и строки. Для этого используется функция drop().

С помощью параметра axis мы можем сообщить, столбец это или строка.

# столбец
df.drop(['Ticket'], axis=1, inplace=True)

# строка
df.drop(3, axis=0, inplace=True

Как вы уже поняли, axis=1 указывает на столбец, а axis=0 – на строку. inplace=True означает, что изменения сохраняются в исходном DataFrame.

8. Columns

Для отображения имен столбцов в DataFrame мы используем атрибут columns.

df.columns


# Результат:
# Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
#        'Parch', 'Fare', 'Cabin', 'Embarked'],
#       dtype='object')

9. rename()

Часто имена столбцов имеют нестандартный формат. Чтобы их изменить, мы используем функцию rename(). Для этого в функцию нужно передать текущее имя столбца и его новое имя:

df.rename(columns={'PassengerId' : 'ID'}, inplace=True)

Примечание редакции: об этом и других изменениях в столбцах читайте в статье “Обновление строк и столбцов в Pandas”.

10. unique() и nunique()

Для нахождения всех уникальных значений в столбце используется функция unique(), а для нахождения их количества – функция nunique().

df['Embarked'].unique()

# array(['S', 'C', 'Q', nan], dtype=object)

df['Embarked'].nunique()

# 3

Функция unique() включает значения nan, а функция unique() исключает их.

Перевод статьи «The Most Used Functions of Pandas».

Сообщение Наиболее используемые функции Pandas появились сначала на pythonturbo.


Source: pythonturbo.ru

Как начать использовать list comprehension в Python: практическое руководство для новичков

Как использовать list comprehension: руководство для начинающих

List comprehension в Python — это способ создания списка на основе другого списка или другого итерабельного объекта в одной строке кода. Это удобный и быстрый способ для работы с данными и избежания лишнего кода.

List comprehension состоит из трех основных элементов: выражение, переменная и итерабельный объект. Выражение определяет действие, которое будет выполнено для каждого элемента итерабельного объекта. Переменная — это имя переменной, которая будет использоваться для каждого элемента итерабельного объекта. Итерабельный объект — это объект, который можно перебрать, такой как список, кортеж, множество или строка.

List comprehension можно использовать для фильтрации элементов, изменения элементов, создания новых списков и многого другого. Он может быть полезным для начинающих программистов, которые хотят улучшить свои навыки в Python. В этой статье мы подробно рассмотрим, как использовать list comprehension в своих проектах и как получить максимальную пользу от этого инструмента.

Что такое list comprehension

Что такое list comprehension

List comprehension – это способ создания нового списка из старого списка с помощью одной строки кода в языке Python.

Это конструкция языка с помощью которой можем сократить код и сделать его более читаемым для нас и других разработчиков.

Компактность и мощь list comprehension делают его очень полезным инструментом для работы с данными.

В основе лежит идея использования условных выражений и циклов для создания нового списка.

С помощью list comprehension можно создать новый список, отфильтрованный по какому-либо условию, изменить каждый элемент списка или объединить несколько списков в один.

  • Также данный инструмент может быть использован для выполнения нескольких действий сразу.
  • Главное его преимущество в том, что он является более быстрым, эффективным и понятным, чем обычный цикл for.

Важно отметить, что использование list comprehension может также улучшить читаемость вашего кода, делая его более лаконичным и понятным.

В целом, list comprehension – это один из наиболее мощных и полезных инструментов в языке Python, который позволяет упростить создание и изменение списков в одну строку кода.

Определение

List comprehension – это конструкция языка Python, которая позволяет создать список или другой итерируемый объект в одной строке кода. Это компактный и элегантный способ для работы со списками и множествами данных, которые часто используются в работе программистов на Python. List comprehension очень эффективен и быстр, поскольку он использует встроенные функции Python.

List comprehension позволяет сгенерировать новый список на основе старого, применяя к каждому элементу списка некоторую операцию. Конструкция состоит из элементов в квадратных скобках и операции, которую необходимо выполнить с каждым элементом списка. List comprehension позволяет сократить код и получить более быстрый и компактный результат.

List comprehension имеет несколько основных компонентов, включая итерируемый объект, переменную и условие. Итерируемый объект является исходным списком или множеством, переменная используется для представления каждого отдельного элемента в списке, а условие определяет, какие элементы будут принадлежать новому списку. List comprehension можно использовать для многих целей, включая фильтрацию данных, преобразование типов, создание новых списков и множеств, и многие другие задачи в программировании.

Преимущества использования

Преимущества использования

Преимущества использования list comprehension в Python заключаются в улучшении читаемости кода, ускорении процесса написания и более компактном коде. Списковое включение позволяет сократить код благодаря использованию одной строки кода вместо нескольких строк кода. Таким образом, программист получает возможность уменьшить объем кода и повысить эффективность его работы.

Кроме этого, list comprehension также позволяет сократить время выполнения программы и ускорить процесс обработки данных. Используя эту технику, можно увеличить скорость работы кода за счет уменьшения используемых циклов и операций.

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

Использование list comprehension также способствует уменьшению вероятности ошибок в коде. Поскольку при использовании спискового включения не требуется явно задавать циклы и условия, вероятность ошибок в коде значительно снижается.

В целом, использование list comprehension делает код более эффективным, лаконичным и понятным, что помогает ускорить и упростить процесс разработки программного обеспечения в Python.

Синтаксис list comprehension

Одной из главных особенностей list comprehension является компактность и удобочитаемость кода. Синтаксис данной конструкции позволяет сократить количество строк кода и сделать его более лаконичным.

Основной синтаксис list comprehension выглядит следующим образом:

  • [ выражение для элемента for переменная in образец if условие на переменную ]

Где:

  • выражение для элемента – выражение, определяющее значение элемента, который будет добавлен в новый список.
  • переменная – переменная, которая будет использоваться при итерации через элементы исходного списка.
  • образец – итерируемая коллекция объектов.
  • условие на переменную (необязательно) – условие, которое будет проверяться на каждой итерации.

Ниже приведены примеры использования синтаксиса list comprehension:

  1. [ x**2 for x in range(5) ] – создаст новый список, содержащий квадраты чисел от 0 до 4.
  2. [ x for x in lst if x % 2 == 0 ] – создаст новый список, содержащий только четные элементы исходного списка lst.
  3. [ word[::-1] for word in words ] – создаст новый список, содержащий перевернутые слова из списка words.

Важно помнить, что list comprehension может содержать несколько блоков for и/или блоков if. Также можно использовать другие итерируемые объекты, такие как кортежи и множества.

Основные элементы синтаксиса

List comprehension – это эффективный и удобный способ создания новых списков на основе существующих данных. Синтаксис list comprehension позволяет создавать новые списки более кратко и читабельно, чем использование циклов.

Синтаксис list comprehension состоит из выражения, за которым следует один или несколько циклов, а также условий фильтрации элементов:

  • Выражение: определяет значение каждого элемента в конечном списке.
  • Цикл: может быть один или несколько циклов, используемых для итерации по элементам.
  • Условие: фильтрует элементы на основе определенных критериев и определяет, какие элементы будут включены в конечный список.

Пример:

  1. Разбиваем строку на символы и создаем список, содержащий только гласные буквы:
Старый способ:Новый способ:
s = “Hello, world!”

vowels = [‘a’, ‘e’, ‘i’, ‘o’, ‘u’]

result = []

for letter in s:

if letter in vowels:

result.append(letter)

print(result)

s = “Hello, world!”

vowels = [‘a’, ‘e’, ‘i’, ‘o’, ‘u’]

result = [letter for letter in s if letter in vowels]

print(result)

Как видно из примера, использование list comprehension позволяет написать более чистый код и получить тот же результат с меньшим количеством строк кода.

Примеры использования

List comprehension в Python – это удобный и эффективный способ создания списков на основе других списков. Ниже приведены несколько примеров использования этой конструкции для решения различных задач.

  • Фильтрация списка: С помощью list comprehension можно быстро и легко отфильтровать элементы списка, удовлетворяющие определенному условию. Например, можно создать новый список только из четных чисел:

“`python

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

even_numbers = [x for x in numbers if x % 2 == 0]

print(even_numbers)

“`

Вывод:

[2, 4, 6, 8, 10]

  • Генерация нового списка: С помощью list comprehension можно быстро и легко создать новый список на основе старого. Например, можно создать новый список, содержащий квадраты чисел:

“`python

numbers = [1, 2, 3, 4, 5]

squares = [x**2 for x in numbers]

print(squares)

“`

Вывод:

[1, 4, 9, 16, 25]

  • Преобразование списка: С помощью list comprehension можно быстро и легко преобразовать элементы списка с помощью функции или метода. Например, можно создать новый список, содержащий только первую букву каждого слова:

“`python

words = [‘apple’, ‘banana’, ‘cherry’]

first_letters = [word[0] for word in words]

print(first_letters)

“`

Вывод:

['a', 'b', 'c']

  • Создание словаря: С помощью list comprehension можно быстро создать словарь на основе списков ключей и значений. Например, можно создать словарь с квадратами чисел:

“`python

numbers = [1, 2, 3, 4, 5]

squares_dict = {x: x**2 for x in numbers}

print(squares_dict)

“`

Вывод:

{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

  • Группировка элементов списка: С помощью list comprehension можно быстро и легко сгруппировать элементы списка по определенным критериям. Например, можно создать список только из целых чисел:

“`python

mixed_list = [1, ‘apple’, 2, ‘banana’, 3, ‘cherry’]

int_list = [x for x in mixed_list if isinstance(x, int)]

print(int_list)

“`

Вывод:

[1, 2, 3]

Помимо вышеперечисленных примеров, list comprehension можно использовать во многих других задачах.

Условное выражение в list comprehension

List comprehension может использовать условное выражение для фильтрации элементов списка, которые удовлетворяют определенному условию. Это делается с помощью ключевого слова “if”, которое размещается после выражения, которое определяет элементы.

Например, мы можем использовать условное выражение, чтобы отфильтровать все четные числа в списке:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

even_numbers = [x for x in numbers if x % 2 == 0]

В этом примере мы определили список чисел и затем создали новый список, содержащий только четные числа из исходного списка. Выражение “if x % 2 == 0” проверяет, делится ли число на 2 без остатка, и только те элементы списка, которые удовлетворяют этому условию, будут добавлены в новый список.

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

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

even_and_gt_5_numbers = [x for x in numbers if x % 2 == 0 and x > 5]

Это создаст список, содержащий только четные числа, которые больше 5.

Условные выражения могут быть также вложены, чтобы обеспечить более сложную фильтрацию:

pairs = [(x, y) for x in range(10) for y in range(10) if x < y]

В этом примере мы создали список пар чисел, где первое число всегда меньше второго.

Также можно включить условие "else", чтобы определить, что будет происходить с элементом, который не удовлетворяет условию "if". Например:

numbers = [1, 2, 3, 4, 5]

new_numbers = [x if x % 2 == 0 else x * 2 for x in numbers]

В этом примере мы создали список новых чисел, где четные числа остаются без изменений, а нечетные числа умножаются на 2.

Синтаксис условия в list comprehension

Условные выражения используются в list comprehension для фильтрации элементов списка. Синтаксис условия в list comprehension имеет следующий вид:

[expression for item in list if condition]

Выражение состоит из трех частей:

  • expression - выражение, которое будет применено к каждому элементу списка, прошедшему через условие;
  • item - элемент списка;
  • condition - условие, которому должны соответствовать элементы списка.

Если условие выполняется для элемента списка, то он проходит через выражение и добавляется в итоговый список. Если условие не выполняется, то элемент пропускается.

Пример:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]

even_numbers = [number for number in numbers if number % 2 == 0]

print(even_numbers) # [2, 4, 6, 8]

В этом примере создается список четных чисел из списка numbers с помощью условия if number % 2 == 0. Это условие проверяет, делится ли число на 2 без остатка.

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

words = ["hello", "world", "python", "list", "comprehension"]

long_words = [word for word in words if len(word) > 5 and "o" in word]

print(long_words) # ['python', 'comprehension']

Этот пример создает список слов с длиной более 5 символов и содержащих букву "o" с помощью условия if len(word) > 5 and "o" in word.

Использование условий в list comprehension позволяет более компактно и эффективно фильтровать элементы списка, чем с помощью цикла и условия внутри него.

Примеры использования условий

Одним из преимуществ list comprehension является возможность использования условий для фильтрации элементов списка. Например, можно выбрать только те элементы, которые удовлетворяют определенному условию, или преобразовать элементы, которые удовлетворяют условию, и оставить остальные без изменений.

Рассмотрим пример, где мы хотим создать список всех четных чисел в диапазоне от 1 до 10:

  • используя условие внутри list comprehension:

numbers = [x for x in range(1, 11) if x % 2 == 0]

Результат:

[2, 4, 6, 8, 10]

  • используя условие вместе с тернарным оператором:

numbers = [x if x % 2 == 0 else None for x in range(1, 11)]

Результат:

[None, 2, None, 4, None, 6, None, 8, None, 10]

В этом примере мы используем тернарный оператор, чтобы заменить нечетные элементы списка на None.

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

list1 = [1, 2, 3]

list2 = [3, 4, 5]

result = [x for x in list1 + list2 if x not in result]

Результат:

[1, 2, 3, 4, 5]

Здесь мы используем условие, чтобы проверить, не содержится ли элемент уже в результате, созданном с помощью list comprehension.

Также можно использовать сложные условия с несколькими операторами, такими как "и" (and) и "или" (or) :

numbers = [x for x in range(1, 11) if x % 2 == 0 or x % 3 == 0]

Результат:

[2, 3, 4, 6, 8, 9, 10]

В этом примере мы выбираем только четные числа и числа, которые делятся на 3.

Вывод: использование условий в list comprehension - это мощный инструмент, который позволяет создавать новые списки и фильтровать элементы в одном выражении.

Вложенные циклы в list comprehension

List comprehension позволяет использовать вложенные циклы для работы с многомерными списками. Это особенно полезно, когда требуется выполнить некоторые операции над каждым элементом списка.

Пример использования вложенных циклов в list comprehension:

Пример 1:

```python

a = [[1,2,3], [4,5,6], [7,8,9]]

b = [x for y in a for x in y]

print(b)

```

В данном примере создается список 'a', который представляет из себя многомерный список. Затем с помощью list comprehension создается список 'b', который представляет из себя список всех элементов списка 'a'.

Пример 2:

```python

a = [[1,2,3], [4,5,6], [7,8,9]]

b = [[x**2 for x in y] for y in a]

print(b)

```

В данном примере с помощью list comprehension создается список 'b', который представляет из себя список квадратов всех элементов списка 'a'. Сначала выполняется вложенный цикл по элементам списка 'a', затем в каждом элементе выполняется цикл по его элементам.

Использование вложенных циклов в list comprehension может быть сложным, но может упростить код, особенно при работе с многомерными списками. Не стоит забывать, что вложенные циклы могут снижать производительность вашего кода и могут приводить к ошибкам, поэтому необходимо осторожно использовать вложенные циклы в list comprehension.

Синтаксис вложенных циклов

В языке Python существует возможность использования вложенных циклов в list comprehension. Это может быть полезным, когда необходимо обрабатывать элементы из нескольких списков или при работе с многомерными массивами.

Для создания вложенных циклов в list comprehension используется следующий синтаксис:

[выражение for элемент1 in список1 for элемент2 in список2]

В данном примере элементы из списка1 будут помещены в элемент1, а элементы из списка2 будут помещены в элемент2.

Также можно использовать несколько условий с помощью ключевого слова if. Например:

[выражение for элемент1 in список1 if условие1 for элемент2 in список2 if условие2]

В данном примере будут обработаны только те элементы, которые удовлетворяют условиям условие1 и условие2.

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

Примеры использования вложенных циклов

В Python list comprehension позволяет использовать вложенные циклы. Такой подход позволяет создавать новые списки на основе списков, которые уже существуют.

Пример использования вложенных циклов:

numbers = [[1,2,3], [4,5,6], [7,8,9]]

new_list = [item for sublist in numbers for item in sublist]

print(new_list)

# Результат: [1, 2, 3, 4, 5, 6, 7, 8, 9]

В данном примере мы создали список numbers, содержащий три вложенных списка. Затем мы создали новый список new_list, используя вложенные циклы. Внутренний цикл возвращает каждый элемент вложенного списка, а внешний цикл проходит по каждому вложенному списку и добавляет каждый элемент в новый список.

Вложенные циклы также могут быть использованы для фильтрации списков. Например:

numbers = [[1,2,3], [4,5], [6], [7,8,9]]

filtered_numbers = [item for sublist in numbers if len(sublist) > 1 for item in sublist]

print(filtered_numbers)

# Результат: [1, 2, 3, 4, 5, 7, 8, 9]

В данном примере мы создали список numbers, содержащий четыре вложенных списка разной длины. Затем мы создали новый список filtered_numbers, используя вложенные циклы и условие len(sublist) > 1. В результате мы получаем только элементы вложенных списков, длина которых больше 1.

Использование функций в list comprehension

Как правило, в выражениях list comprehension можно использовать некоторые функции, которые помогут обрабатывать элементы списка до того, как их добавят в новый список. Таким образом, вы можете объединить функции и выражения в одной строке кода, что упрощает и ускоряет вашу работу.

Вот несколько примеров использования функций в list comprehension:

  • map() - позволяет применить функцию к каждому элементу списка. Например, если есть список чисел, вы можете использовать map(), чтобы удвоить каждый элемент:
  • new_list = [2*x for x in old_list]
  • filter() - позволяет фильтровать элементы списка с помощью функции. Например, если есть список строк, вы можете использовать filter(), чтобы вернуть только те строки, которые содержат цифры:
  • new_list = [x for x in old_list if any(i.isdigit() for i in x)]
  • reduce() - позволяет свести все элементы списка к одному значению, используя функцию. Например, если есть список чисел, вы можете использовать reduce(), чтобы найти их сумму:
  • sum = reduce(lambda x, y: x+y, [1,2,3,4])

Кроме того, вы можете определять свои собственные функции и использовать их в list comprehension для более сложных манипуляций со списками.

Использование функций в list comprehension поможет вам создавать более гибкий и мощный код, который можно легко читать и поддерживать. Независимо от того, насколько сложный ваш список, вы можете использовать list comprehension и функции, чтобы сделать его обработку более эффективной.

Обзор функции map()

Функция map() - это функция в Python, которая принимает функцию и итерируемый объект в качестве аргументов и применяет эту функцию к каждому элементу итерируемого объекта.

Синтаксис:

map(function, iterable)

  • function: функция, которая будет применена к каждому элементу из iterable.
  • iterable: итерируемый объект, к которому нужно применить функцию.

Пример:

numbers = [1, 2, 3, 4, 5]

squares = map(lambda x: x**2, numbers)

print(list(squares))

output: [1, 4, 9, 16, 25]

Объяснение:

  • Функция lambda x: x**2 определяет квадрат каждого элемента.
  • numbers - итерируемый объект.
  • Функция map() применяет функцию lambda к каждому элементу numbers.
  • Результат сохраняется в переменную squares.
  • list(squares) - возвращает список квадратов каждого элемента в numbers.
  • Функция map() позволяет легко изменять элементы в списке и других итерируемых объектах. Она также может использоваться в комбинации с функциями filter() и reduce(), для манипулирования списками и другими итерируемыми объектами.

    Использование функций вместе с list comprehension

    Функции могут быть полезным инструментом для обработки данных, которые получены с помощью list comprehension. Например, функция filter() может использоваться для фильтрации данных, а функция map() - для преобразования данных.

    Функция filter() принимает два аргумента: функцию фильтрации и список, который нужно отфильтровать. Функция должна возвращать True или False в зависимости от того, должен ли элемент быть сохранен. List comprehension и функция filter() могут быть использованы вместе для фильтрации данных:

    Пример:

    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    even_numbers = [x for x in numbers if x % 2 == 0]

    print(even_numbers) # [2, 4, 6, 8, 10]

    even_numbers_with_filter = list(filter(lambda x: x % 2 == 0, numbers))

    print(even_numbers_with_filter) # [2, 4, 6, 8, 10]

    Функция map() может использоваться для преобразования данных. Она применяет функцию к каждому элементу списка и возвращает список с результатами. List comprehension и функция map() могут быть использованы вместе для преобразования данных:

    Пример:

    numbers = [1, 2, 3, 4, 5]

    squared_numbers = [x**2 for x in numbers]

    print(squared_numbers) # [1, 4, 9, 16, 25]

    squared_numbers_with_map = list(map(lambda x: x**2, numbers))

    print(squared_numbers_with_map) # [1, 4, 9, 16, 25]

    Таким образом, функции могут быть использованы вместе с list comprehension для более гибкой обработки данных. Это позволяет сделать код более читаемым и продуктивным.

    Подводя итоги

    В данной статье мы рассмотрели основы работы с list comprehension в Python. Основным достоинством этого метода является возможность сократить код, не уменьшая его читаемости. Благодаря использованию list comprehension программа работает быстрее и экономит ресурсы процессора и памяти.

    Мы узнали, что с помощью list comprehension можно создавать новые списки, фильтровать элементы, преобразовывать данные и даже создавать сложные структуры данных. Множество примеров, представленных в статье, помогут начинающим программистам быстро овладеть этой техникой и использовать ее в своих проектах.

    Напомним, что основным принципом работы list comprehension является создание списка на основе другого списка или последовательности. При этом не нужно создавать дополнительные переменные и циклы – все реализуется в одной строке кода.

    В заключении хотим подчеркнуть, что использование list comprehension – это не только возможность сократить код и повысить эффективность работы программы, но и средство повышения производительности программиста. Благодаря использованию этой техники можно значительно ускорить процесс написания кода и сделать его более читаемым и легким в поддержке.

    Сравнение list comprehension с другими методами работы с массивами

    List comprehension - это удобный и лаконичный способ работы с массивами в Python. Однако, не стоит забывать о других методах, которые тоже могут быть полезны в некоторых ситуациях.

    Циклы for: работают с массивами путем итерации по каждому элементу. Они могут быть полезны, когда нужно применить к элементам какую-то функцию или просто выполнить определенный код для каждого элемента. Однако, синтаксис циклов более громоздкий и менее лаконичный, чем у list comprehension.

    Функции map/filter: работают со списками, также применяя какую-то функцию к каждому элементу. Однако, они не поддерживают условия и переменные внутри себя, что делает их менее гибкими, чем list comprehension.

    Генераторы: будут полезны в тех случаях, когда нужно работать с очень большим количеством данных. Они создают последовательность, которая вычисляется только в момент обращения к ней, что позволяет эффективно использовать память. Однако, они не поддерживают условия внутри себя, что делает их менее гибкими, чем list comprehension.

    Таким образом, list comprehension является мощным и универсальным инструментом для работы с массивами в Python. Однако, все остальные методы тоже могут быть полезны в зависимости от конкретной задачи.

    Полезные советы по использованию list comprehension

    1. Не злоупотребляйте list comprehension. Даже если операции, которые вы хотите выполнить, могут быть выполнены с помощью list comprehension, вам не обязательно нужно использовать их всегда. Не забывайте, что комбинация нескольких сложных операций в list comprehension может привести к трудночитаемому и неэффективному коду.

    2. Используйте list comprehension вместо циклов, когда это возможно. List comprehension всегда более читабелен и компактен, чем эквивалентные циклы. Он также может работать намного быстрее, поскольку является оптимизированной формой цикла.

    3. Не забывайте использовать условное выражение при необходимости. Условное выражение может быть полезно, если нужно изменить значение элемента в list comprehension только в некоторых случаях. Например, вы можете использовать его для проверки, является ли число четным или нечетным.

    4. Используйте list comprehension для фильтрации элементов. Если вы хотите выбрать только определенные элементы списка, вы можете использовать list comprehension для фильтрации. Это может быть особенно полезно, когда вы работаете с большими наборами данных и хотите выбрать только подмножество элементов, удовлетворяющих определенным условиям.

    5. Используйте сведения об итераторах. List comprehension работает с итераторами и может использоваться для создания новых списков из любых итерируемых объектов. Например, если у вас есть строка, вы можете использовать list comprehension, чтобы создать список, содержащий все символы этой строки.

    6. Изучайте другие подходы к работе со списками. Не забывайте, что list comprehension - это только один из способов работы со списками в Python. Иногда использование других функций, таких как map(), filter() и reduce(), может быть более легким и эффективным. Используйте list comprehension только тогда, когда это действительно необходимо, и изучайте другие подходы к работе со списками, чтобы стать более компетентным в Python.

    • list comprehension может ускорить выполнение программы, поэтому целесообразно использовать его при работе со списками в Python
    • В любой момент лучше использовать list comprehension, когда необходима обработка данных, но необходимо действовать осторожно и избегать усложнения кода
    • нужно учитывать, что любой список можно обработать множеством способов в Python. Поэтому следует изучить и использовать другие функции для работы с данными в Python.

    Вопрос-ответ:

    Что такое list comprehension?

    List comprehension – это сокращенный способ создания нового списка на основе уже существующего.

    Какие операции можно выполнять в list comprehension?

    Можно использовать различные операции, такие как фильтрация, манипуляции элементами списка, экранирование и т.д.

    Как применить фильтрацию в list comprehension?

    Просто добавьте условие в скобки. Например, [x for x in range(10) if x % 2 == 0] вернет список только с четными числами.

    Можно ли использовать list comprehension вместо циклов for?

    Да, list comprehension позволяет выполнять ту же работу, что и цикл for, но гораздо короче и быстрее.

    Какие преимущества имеет использование list comprehension?

    Оно упрощает написание кода и делает его более читабельным, также позволяет экономить время при написании простых конструкций.

    Можно ли использовать list comprehension для многомерных списков?

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

    Видео:

    list comprehension using lambda function | list comprehension in python | labda method

    list comprehension using lambda function | list comprehension in python | labda method by Mangesh Bagul 1 year ago 23 seconds 2,736 views

    List Comprehension - BEST Python feature !!! Fast and Efficient

    List Comprehension - BEST Python feature !!! Fast and Efficient by Python Simplified 11 months ago 14 minutes, 51 seconds 136,780 views

    Сообщение Как начать использовать list comprehension в Python: практическое руководство для новичков появились сначала на Программирование на Python.

    Введение в HTTP в Python3

    В этой статье мы погрузимся в мир веб-протоколов, в частности HTTP, а также рассмотрим связь HTTP с TCP и UDP. Попутно, в качестве примеров, мы приведем примеры реализации на Python клиентов и серверов, использующих эти протоколы.

    HTTP (Hypertext Transfer Protocol) является основой мира Интернета. Когда вы посещаете какой-нибудь сайт, ваш браузер посылает HTTP-запросы на сервер, который в ответ выдает веб-страницы. Это похоже на разговор между браузером и сервером. Например, если послать запрос с текстом “Joe”, то сервер может ответить “Hi there, Joe”.

    Ниже приведен базовый пример работы такого HTTP-сервера в Python3. Мы будем использовать встроенные модули http.client и http.server. Это простой пример, поэтому он не строго соответствует стандартам HTTP и не рекомендуется для использования в производстве, поскольку реализует только базовые проверки безопасности. Но для наших целей это подойдет.

    import http.server
    import http.client
    
    PORT = 8001
    
    class Store:
        def __init__(self):
            self.requestBody = ''
            self.responseBody = ''
    
    store = Store()
    
    class MyHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
        def do_POST(self):
            content_length = int(self.headers['Content-Length'])
            content = self.rfile.read(content_length).decode('utf-8')
            store.requestBody = content
    
            response_content = f'Hi there, {content}'.encode('utf-8')
            self.send_response(200)
            self.send_header('Content-Type', 'text/plain')
            self.send_header('Content-Length', len(response_content))
            self.end_headers()
            self.wfile.write(response_content)
    
    def server_listen():
        with http.server.HTTPServer(('localhost', PORT), MyHTTPRequestHandler) as server:
            print(f'HTTP server listening on {PORT}')
            http_request()
    
    def http_request():
        conn = http.client.HTTPConnection('localhost', PORT)
        content = 'Joe'
        headers = {
            'Content-Type': 'text/plain',
            'Content-Length': str(len(content))
        }
        conn.request('POST', '/greet', body=content, headers=headers)
        response = conn.getresponse()
        data = response.read().decode('utf-8')
        store.responseBody = data
        close_connections()
    
    def close_connections():
        server.server_close()
    
        print(store.requestBody)  # Joe
        print(store.responseBody)  # Hi there, Joe
    
    server_listen()
    

    TCP-соединение

    Теперь познакомимся с TCP  (Transmission Control Protocol). TCP является базовым протоколом, на котором построен HTTP, как видно из официальных спецификаций последнего. И хотя я уже об этом сказал, я попрошу вас сделать вид, что вы этого еще не знаете. Давайте докажем, что HTTP базируется на TCP!

    В Python имеются встроенные модули threading и socket, которые помогают нам создавать TCP-клиенты и серверы.

    Следует знать, что TCP отличается от HTTP по нескольким параметрам:

    • Запросы не могут отправляться спонтанно. Сначала должно быть установлено соединение.
    • После установки соединения сообщения могут передаваться в обоих направлениях.
    • Установленное соединение должно быть закрыто вручную.

    Ниже приведена простая реализация TCP-клиента, который желает получить приветствие от сервера:

    import socket
    import threading
    
    PORT = 8001
    MAXIMUM_BYTES_RECEIVABLE = 1024
    
    class Store:
        def __init__(self):
            self.requestBody = ''
            self.responseBody = ''
    
    store = Store()
    
    def handle_client(client_socket):
        request_data = client_socket.recv(MAXIMUM_BYTES_RECEIVABLE).decode('utf-8')
        store.requestBody = request_data
    
        response_data = f'Hi there, {request_data}'.encode('utf-8')
        client_socket.send(response_data)
    
        response = client_socket.recv(MAXIMUM_BYTES_RECEIVABLE).decode('utf-8')
        store.responseBody = response
    
        client_socket.close()
    
    def server_listen():
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # When the socket type is socket.SOCK_STREAM the protocol being used is TCP by default.
        server.bind(('0.0.0.0', PORT))
        server.listen(5)
        print(f'TCP server listening on {PORT}')
    
        while True:
            client_socket, addr = server.accept() # Blocks execution and waits for an incoming connection.
            client_handler = threading.Thread(target=handle_client, args=(client_socket,))
            client_handler.start()
    
    def http_request():
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect(('localhost', PORT))
        content = 'Joe'
        client.send(content.encode('utf-8'))
        client.shutdown(socket.SHUT_WR)
        response = client.recv(MAXIMUM_BYTES_RECEIVABLE).decode('utf-8')
        store.responseBody = response
        client.close()
        close_connections()
    
    def close_connections():
        server.close()
    
        print(store.requestBody)  # Joe
        print(store.responseBody)  # Hi there, Joe
    
    if __name__ == '__main__':
        server_listen()
        http_request()
    

    Теперь представьте, что у вас есть TCP-прокси, который может передавать сообщения между HTTP-клиентами и серверами. Даже если этот прокси не понимает HTTP, он все равно может передавать запросы и ответы.

    Вот как будет выглядеть его реализация:

    import socket
    import http.client
    import threading
    
    HTTP_PORT = 8001
    PROXY_TCP_PORT = 8002
    MAXIMUM_BYTES_RECEIVABLE = 1024
    
    class Store:
        def __init__(self):
            self.requestBody = ''
            self.responseBody = ''
    
    store = Store()
    
    def proxy_handler(local_socket):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as remote_socket:
            remote_socket.connect(('localhost', HTTP_PORT))
            
            def forward(src, dst):
                while True:
                    data = src.recv(MAXIMUM_BYTES_RECEIVABLE)
                    if not data:
                        break
                    dst.send(data)
            
            threading.Thread(target=forward, args=(local_socket, remote_socket)).start()
            threading.Thread(target=forward, args=(remote_socket, local_socket)).start()
    
    def http_server_handler(client_socket):
        data = client_socket.recv(MAXIMUM_BYTES_RECEIVABLE).decode('utf-8')
        store.requestBody = data
    
        response_data = f'Hi there, {data}'.encode('utf-8')
        client_socket.send(response_data)
    
        response = client_socket.recv(MAXIMUM_BYTES_RECEIVABLE).decode('utf-8')
        store.responseBody = response
    
    def http_server_listen():
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
            server.bind(('0.0.0.0', HTTP_PORT)
            server.listen(5)
            print(f'HTTP server listening on {HTTP_PORT}')
    
            while True:
                client_socket, addr = server.accept()
                threading.Thread(target=http_server_handler, args=(client_socket,)).start()
    
    def proxy_listen():
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as proxy_server:
            proxy_server.bind(('0.0.0.0', PROXY_TCP_PORT))
            proxy_server.listen(5)
            print(f'TCP proxy listening on {PROXY_TCP_PORT}')
    
            while True:
                local_socket, addr = proxy_server.accept()
                threading.Thread(target=proxy_handler, args=(local_socket,)).start()
    
    def http_request():
        conn = http.client.HTTPConnection('localhost', PROXY_TCP_PORT)
        content = 'Joe'
        headers = {
            'Content-Type': 'text/plain',
            'Content-Length': str(len(content))
        }
        conn.request('POST', '/greet', body=content, headers=headers)
        response = conn.getresponse()
        data = response.read().decode('utf-8')
        close_connections()
    
    def close_connections():
        http_server_listen_thread.join()
        proxy_listen_thread.join()
    
        print(store.requestBody)  # Joe
        print(store.responseBody)  # Hi there, Joe
    
    if __name__ == '__main__':
        http_server_listen_thread = threading.Thread(target=http_server_listen)
        proxy_listen_thread = threading.Thread(target=proxy_listen)
        http_server_listen_thread.start()
        http_server_listen_thread.join()
        proxy_listen_thread.start()
        http_request()

    Как уже говорилось, хотя TCP-прокси-сервер не знает, что такое HTTP, запросы и ответы полностью проходят через него.

    Понимание особенностей TCP

    Прежде чем мы продолжим, несколько фактов о TCP:

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

    Неудивительно, что TCP так распространен, но… Вы же знали, что будет какое-то “но”, верно?

    TCP может быть несколько тяжеловат. Чтобы сокетное соединение могло разрешить отправку данных, требуется установить три пакета. В мире HTTP это означает, что для выполнения параллельных запросов HTTP/1.1 требуется несколько TCP-соединений, что может потребовать значительных ресурсов.

    HTTP/2 пытается улучшить эту ситуацию, обрабатывая параллельные запросы через одно соединение. Однако при этом возникают проблемы. Когда один пакет задерживается или приходит не по порядку, это приводит к остановке всех запросов.

    А теперь представьте, что есть альтернатива TCP, позволяющая параллельные HTTP-сообщения без этих последствий. Звучит неплохо, не так ли? Эта альтернатива – UDP (User Datagram Protocol).

    UDP-соединение

    Начнем с того, чем UDP отличается от TCP:

    • Здесь нет понятия соединения. Вы отправляете данные и надеетесь, что кто-то их получит.
    • Вы можете передавать только небольшие фрагменты данных, которые не обязательно представляют собой целое сообщение (подробнее об этом можно почитать в статье Википедии), а встроенные разделители отсутствуют, если они не включены в явном виде.
    • В результате создание даже базового механизма запрос/ответ становится более сложным (но все же возможным).

    Давайте рассмотрим пример UDP-клиента, который хочет взаимодействовать с сервером. На этот раз мы определим наш сокет как SOCK_DGRAM:

    import socket
    
    PORT = 8001
    EOS = b'{$content}'  # End of stream
    MAXIMUM_BYTES_RECEIVABLE = 1024
    
    class Store:
        def __init__(self):
            self.requestBody = ''
            self.responseBody = ''
    
    store = Store()
    
    def slice_but_last(data, encoding='utf-8'):
        return data[:-1].decode(encoding)
    
    def server_listen():
        sender = None
    
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as server:
            server.bind(('0.0.0.0', PORT))
            print(f'UDP server listening on {PORT}')
    
            while True:
                chunk, addr = server.recvfrom(MAXIMUM_BYTES_RECEIVABLE)
                sender = addr if sender is None else sender
                store.requestBody += slice_but_last(chunk)
    
                if chunk[-1:] == EOS:
                    response_data = f'Hi there, {store.requestBody}'.encode('utf-8') + EOS
                    server.sendto(response_data, sender)
    
                    # Note: You can choose to close the server here if needed
                    break
    
    def http_request():
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as client:
            content = 'Joe'.encode('utf-8') + EOS
            client.sendto(content, ('localhost', PORT))
            response_data, _ = client.recvfrom(MAXIMUM_BYTES_RECEIVABLE)
            store.responseBody = slice_but_last(response_data)
    
            close_connections()
    
    def close_connections():
        print(store.requestBody)  # Joe
        print(store.responseBody)  # Hi there, Joe
    
    if __name__ == '__main__':
        server_listen()
        http_request()
    

    Итак, учитывая, что у нас есть парсер HTTP (http-parser, например), вот как можно реализовать HTTP-решение через UDP:

    import socket
    from http_parser.parser import HttpParser
    
    PORT = 8001
    CRLF = 'rn'
    MAXIMUM_BYTES_RECEIVABLE = 1024
    
    class Store:
        def __init__(self):
            self.requestBody = ''
            self.responseBody = ''
    
    store = Store()
    
    def server_listen():
        parser = HttpParser()
    
        def on_body(data):
            store.requestBody += data
    
        def on_message_complete():
            content = f'Hi there, {store.requestBody}'
            response = f'HTTP/1.1 200 OK{CRLF}' 
                       f'Content-Type: text/plain{CRLF}' 
                       f'Content-Length: {len(content)}{CRLF}' 
                       f'{CRLF}' 
                       f'{content}'
            server.sendto(response.encode('utf-8'), sender)
    
        parser.on_body = on_body
        parser.on_message_complete = on_message_complete
    
        sender = None
    
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as server:
            server.bind(('0.0.0.0', PORT))
            print(f'UDP server listening on {PORT}')
    
            while True:
                chunk, sender = server.recvfrom(MAXIMUM_BYTES_RECEIVABLE)
                parser.execute(chunk)
    
    def http_request():
        parser = HttpParser()
    
        def on_body(data):
            store.responseBody += data
    
        def on_message_complete():
            close_connections()
    
        parser.on_body = on_body
        parser.on_message_complete = on_message_complete
    
        content = 'Joe'
        request = f'POST /greet HTTP/1.1{CRLF}' 
                  f'Content-Type: text/plain{CRLF}' 
                  f'Content-Length: {len(content)}{CRLF}' 
                  f'{CRLF}' 
                  f'{content}'
    
        client.sendto(request.encode('utf-8'), ('localhost', PORT))
    
    def close_connections():
        server.close()
        client.close()
    
        print(store.requestBody)  # Joe
        print(store.responseBody)  # Hi there, Joe
    
    if __name__ == '__main__':
        client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        server_listen()
        http_request()

    Выглядит неплохо. У нас есть полноценная реализация с использованием UDP. Но пока не стоит слишком радоваться. UDP имеет ряд существенных недостатков:

    • Ненадежность. Вы не можете быть уверены, что ваше сообщение дойдет до адресата.
    • Пакеты приходят не по порядку, поэтому нельзя гарантировать порядок следования сообщений.

    Возникновение QUIC и HTTP/3

    Для устранения недостатков UDP был создан новый протокол – QUIC . Он построен на основе UDP и использует “умные” алгоритмы для его реализации. Отличительные особенности QUIC:

    • Надежность
    • Обеспечение упорядоченной доставки пакетов
    • Легкость

    Это приводит нас прямо к HTTP/3, который все еще является относительно новым и экспериментальным. В нем используется QUIC для устранения проблем, возникших в HTTP/2. В HTTP/3 нет соединений, поэтому сессии не влияют друг на друга.

    Таблица HTTP-семантики. Три столбца с версиями HTTP и соответствующими им протоколами.

    HTTP/3 – перспективное направление развития веб-протоколов, использующее сильные стороны QUIC и UDP.

    Хотя встроенная поддержка протокола QUIC отсутствует, можно воспользоваться модулем aioquic, который поддерживает реализацию как QUIC, так и HTTP/3.

    Пример с использованием протокола QUIC

    Рассмотрим простой пример сервера, использующего QUIC:

    import asyncio
    import ssl
    from aioquic.asyncio import connect, connect_udp, Connection, serve
    from aioquic.asyncio.protocol import BaseProtocol, DatagramProtocol
    from aioquic.asyncio.protocol.stream import DataReceived
    
    class HTTPServerProtocol(BaseProtocol):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
        async def data_received(self, data):
            await super().data_received(data)
            if isinstance(self._quic, Connection):
                for stream_id, buffer in self._quic._events[DataReceived]:
                    data = buffer.read()
                    response = f'HTTP/1.1 200 OKrnContent-Length: {len(data)}rnrn{data.decode("utf-8")}'
                    self._quic.send_stream_data(stream_id, response.encode('utf-8'))
    
    async def main():
        loop = asyncio.get_event_loop()
    
        # Create QUIC server context
        quic_server = await loop.create_server(HTTPServerProtocol, 'localhost', 8001)
    
        async with quic_server:
            await quic_server.serve_forever()
    
    if __name__ == '__main__':
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())

    А это – клиент:

    import asyncio
    import ssl
    from aioquic.asyncio import connect, connect_udp, Connection
    from aioquic.asyncio.protocol import BaseProtocol
    
    class HTTPClientProtocol(BaseProtocol):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.connected_event = asyncio.Event()
    
        def quic_event_received(self, event):
            super().quic_event_received(event)
            if event.matches('connected'):
                self.connected_event.set()
    
        async def request(self, path, data=None):
            stream_id = self._quic.get_next_available_stream_id()
            self._quic.send_stream_data(stream_id, data)
            await self.connected_event.wait()
            response = await self._quic.receive_data(stream_id)
            return response
    
    async def main():
        loop = asyncio.get_event_loop()
    
        # Create QUIC client context
        quic = connect('localhost', 8001)
    
        async with quic as protocol:
            client_protocol = HTTPClientProtocol(quic, protocol._session_id, None)
            await client_protocol.connected_event.wait()
            
            data = 'Hello, Joe!'
            response = await client_protocol.request('/greet', data.encode('utf-8'))
            print(response.decode('utf-8'))
    
    if __name__ == '__main__':
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
    

    Пример с использованием протокола HTTP/3

    А чтобы вы получили полное представление, приведем пример с использованием протокола HTTP/3 (с помощью модуля aioquic).

    Сервер:

    import asyncio
    from aioquic.asyncio.protocol import connect, connect_udp, serve, QuicProtocol
    from aioquic.asyncio.protocol.stream import DataReceived
    from h11 import Response, Connection
    from h11._events import Data
    
    class HTTP3ServerProtocol(QuicProtocol):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.conn = Connection()
    
        def quic_event_received(self, event):
            super().quic_event_received(event)
            if event.matches('handshake_completed'):
                self.conn.initiate_upgrade_for_http2()
    
        async def data_received(self, data):
            await super().data_received(data)
            if isinstance(self._quic, QuicProtocol):
                for stream_id, buffer in self._quic._events[DataReceived]:
                    data = buffer.read()
                    response = Response(status_code=200, headers=[('content-length', str(len(data)))], content=data)
                    data = self.conn.send(response)
                    self._quic.transmit_data(stream_id, data)
    
    async def main():
        loop = asyncio.get_event_loop()
    
        # Create QUIC server context
        quic_server = await loop.create_server(HTTP3ServerProtocol, 'localhost', 8001)
    
        async with quic_server:
            await quic_server.serve_forever()
    
    if __name__ == '__main__':
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())

    И клиент:

    import asyncio
    from aioquic.asyncio.protocol import connect, connect_udp, QuicProtocol
    from h11 import Request, Response, Connection
    from h11._events import Data
    
    class HTTP3ClientProtocol(QuicProtocol):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.conn = Connection()
    
        async def request(self, path, data=None):
            stream_id = self._quic.get_next_available_stream_id()
            request = Request(method='POST', target=path, headers=[('content-length', str(len(data)))]
                if data else [])
            data = self.conn.send(request)
            self._quic.transmit_data(stream_id, data)
    
            while True:
                event = self.conn.next_event()
                if isinstance(event, Data):
                    self._quic.transmit_data(stream_id, event.data)
                elif event == h11.EndOfMessage():
                    break
    
            response = await self._quic.receive_data(stream_id)
            return response
    
    async def main():
        loop = asyncio.get_event_loop()
    
        # Create QUIC client context
        quic = connect('localhost', 8001)
    
        async with quic as protocol:
            client_protocol = HTTP3ClientProtocol(quic, protocol._session_id, None)
            
            data = 'Hello, Joe!'
            response = await client_protocol.request('/greet', data.encode('utf-8'))
            print(response.content.decode('utf-8'))
    
    if __name__ == '__main__':
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())

    Итоги

    Такая эволюция ставит вопрос о том, сможем ли мы в будущем распрощаться с TCP, но это уже тема для другой статьи и, возможно, для будущего.

    На этом мы завершаем наше путешествие по HTTP, TCP и UDP в Python3! Тема может показаться сложной, но под поверхностью каждого посещаемого вами сайта скрывается увлекательный мир веб-коммуникаций, с которым стоит познакомиться.

    Перевод статьи «Introduction to HTTP in Python3».

    Сообщение Введение в HTTP в Python3 появились сначала на pythonturbo.


    Source: pythonturbo.ru