О, какой сложный узор мы ткем…
Вальтер Скотт. Мармион
На французско-швейцарской границе располагается CERN — Институт исследования физики частиц, где частенько сталкивают атомы друг с другом.
При этом процессе генерируется множество данных. В 1989 году английский ученый Тим Бернерс-Ли впервые внес предложение помочь распространять информацию внутри CERN, а также среди всего исследовательского сообщества. Он назвал его World Wide Web (Всемирная паутина) и довольно быстро выделил три основные идеи, которые должны были лечь в основу ее дизайна:
• HTTP (Hypertext Transfer Protocol — протокол передачи гипертекста) — протокол для веб-клиентов и серверов для обмена запросами и ответами;
• HTML (Hypertext Markup Language — гипертекстовый язык разметки) — формат для представления результатов;
• URL (Uniform Resource Locator — единообразный локатор ресурса) — способ уникально обозначить сервер и ресурс на этом сервере.
В самом простом варианте использования веб-клиент (я думаю, что Бернерс-Ли был первым, кто употребил слово «браузер») соединяется с веб-сервером с помощью протокола HTTP, запрашивает URL и получает HTML.
Все это было создано на базе сети Интернет, который в то время был некоммерческим, им пользовались лишь несколько университетов и исследовательских организаций.
Бернерс-Ли написал первый браузер и сервер на компьютере NeXT. Известность Всемирной паутины значительно возросла в 1993-м, когда группа студентов Иллинойсского университета выпустила браузер Mosaic (для Windows, Macintosh и Unix) и сервер NCSA . Загрузив тем летом Mosaic и начав создавать сайты, я даже не догадывался, что Всемирная паутина и Интернет станут частью повседневной жизни. В то время Интернет все еще был некоммерческим официально, в мире существовало всего 500 известных веб-серверов (). К концу 1994 года их количество увеличилось до 10 000. Интернет был открыт для коммерческого использования, и авторы браузера Mosaic основали компанию Netscape, чтобы писать коммерческие веб-приложения. Компания Netscape стала достоянием общественности как часть возникшего в то время раннего интернет-безумия, и взрывной рост Всемирной паутины не остановился до сих пор.
Практически каждый язык программирования был использован для написания веб-клиентов и веб-серверов. Динамические языки Perl, PHP и Ruby стали особенно популярными. В этой главе я покажу вам, почему Python особенно хорош для работы в Интернете на любом из таких уровней, как:
• клиенты для удаленного доступа;
• серверы, предоставляющие данные для сайтов и веб-API;
• веб-API и сервисы, позволяющие обмениваться данными другими способами, отличающимися от просматриваемых веб-страниц.
Выполняя упражнения в конце главы, мы создадим настоящий интерактивный сайт.
Низкоуровневая система проводящих путей Интернета называется Transmission Control Protocol/Internet Protocol (протокол управления передачей/интернет-протокол) или просто TCP/IP (в одноименном разделе главы 17 на с. 368 данный протокол рассматривается более подробно). Он перемещает байты между компьютерами, но не обращает внимания на то, что они значат. Это работа высокоуровневых протоколов — определений синтаксиса для некоторых целей. HTTP — стандартный протокол для обмена данными в Сети.
Всемирная паутина — это клиент-серверная система. Клиент делает запрос серверу: он открывает соединение TCP/IP, отправляет URL и другую информацию с помощью HTTP и получает ответ.
Формат ответа также определяется протоколом HTTP. Он включает в себя статус запроса и (в том случае, если запрос выполнен успешно) данные и формат ответа.
Самый известный веб-клиент — это браузер. Он может создавать HTTP-запросы несколькими способами. Вы можете инициировать запрос вручную, написав URL в адресной строке или нажав ссылку на веб-странице. Очень часто для отображения сайта используются возвращаемые данные: HTML-документы, файлы JavaScript, файлы CSS и изображения, но данные могут быть любого типа, в том числе и не предназначенные для отображения.
Важный аспект HTTP — этот протокол не имеет состояния. Каждое создаваемое вами соединение HTTP не зависит от других. Это упрощает базовые операции, но усложняет другие. Рассмотрим несколько примеров таких усложнений.
•Кэширование. Удаленный контент, который не меняется, должен быть сохранен веб-клиентом и использован для того, чтобы не загружать его с сервера снова.
• Сессии. Интернет-магазин должен запоминать содержимое вашей корзины.
•Аутентификация. Сайты, которые требуют ваши имя пользователя и пароль, должны запоминать их, пока вы авторизованы.
Решения для описанных усложнений включают в себя cookie, в которых сервер отправляет клиенту довольно специфическую информацию, позволяющую их распознать, когда клиент отправляет эти файлы назад.
HTTP — протокол, основанный на тексте, поэтому вы можете вручную вводить его код во время тестирования. Древняя программа telnet позволяет подключиться к любому серверу и порту и вводить команды для любого сервиса, запущенного на нем. Создавать безопасные (зашифрованные) соединения с другими машинами следует с помощью ssh.
Запросим у любимого многими тестового сайта Google базовую информацию о его главной странице. Введем следующее:
$ telnet 80
Если на порте 80 (на нем работает незашифрованный ; зашифрованный протокол использует порт 443) по адресу google.com существует веб-сервер (я думаю, это беспроигрышный вариант), то telnet выведет на экран подтверждающую информацию, а затем отобразит пустую строку, которая является приглашением ввести что-то еще:
Trying 74.125.225.177...
Connected to .
Escape character is '^]'.
Теперь введем настоящую команду HTTP для telnet, которую он отправит на веб-сервер Google. Самая распространенная команда HTTP (ее использует ваш браузер каждый раз, когда вы вводите URL в адресной строке) — это GET. Она позволяет получить содержимое заданного ресурса, такого как HTML-файл, и возвращает его клиенту. Для первой проверки мы используем команду HTTP HEAD, просто получающую некую базовую информацию о ресурсе:
HEAD / HTTP/1.1
Добавьте дополнительный символ возврата каретки, чтобы удаленный сервер понял: вы закончили вводить команду и хотите получить ответ. Конструкция HEAD/ отправляет запрос HTTP HEADглагол (команда) с целью получить информацию о главной странице (/). Вы получите ответ наподобие следующего (я обрезал некоторые длинные строки с помощью многоточий, чтобы они не вываливались за пределы страницы):
HTTP/1.1 200 OK
Date: Mon, 10 Jun 2019 16:12:13 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
Server: gws
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Set-Cookie: 1P_JAR=...; expires=... GMT; path=/; domain=.google.com
Set-Cookie: NID=...; expires=... GMT; path=/; domain=.google.com; HttpOnly
Transfer-Encoding: chunked
Accept-Ranges: none
Vary: Accept-Encoding
Так выглядят заголовки ответов HTTP и их значения. Некоторые из них, такие как Date или Content-Type, обязательны. Другие, например Set-Cookie, используются для отслеживания вашей активности в течение нескольких посещений (мы поговорим об управлении состоянием чуть позже). Делая запрос HTTP HEAD, вы получаете в ответ только заголовки. Если вы задействовали команды HTTP GET или HTTP POST, то также получите данные от главной страницы (смесь HTML, CSS, JavaScript и всего прочего, что Google решит разместить на своей главной странице).
Я не хочу, чтобы вы зависли в telnet. Закрыть его можно, введя следующее:
q
Использовать telnet довольно просто, но это делается исключительно вручную. Программа curl (/), возможно, самый популярный веб-клиент, работающий в командной строке. Его документация включает в себя книгу Everything Curl (), доступную в форматах HTML, PDF и ebook. В этой таблице () сравниваются curl и похожие инструменты. На странице скачивания () перечислены все основные, а также многие узкоспециализированные платформы.
Простейший вариант использования curl — неявный запрос GET (здесь его вывод будет обрезан):
$ curl
<!doctype html>
<html>
<head>
<title>Example Domain</title>
...
Здесь используется HEAD:
$ curl --head
HTTP/1.1 200 OK
Content-Encoding: gzip
Accept-Ranges: bytes
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Sun, 05 May 2019 16:14:30 GMT
Etag: "1541025663"
Expires: Sun, 12 May 2019 16:14:30 GMT
Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT
Server: ECS (agb/52B1)
X-Cache: HIT
Content-Length: 606
Если вы передаете аргументы, то можете включить их в командную строку или в файл данных. В этих примерах я использую следующее:
• URL любого сайта;
• файл data.txt — текстовый файл данных со следующим содержимым: a=1&b=2;
• файл data.json — файл данных JSON с таким содержимым: {"a":1,"b":2};
•a=1&b=2 в качестве двух аргументов.
Использование аргументов по умолчанию (закодированных в форме):
$ curl -X POST -d "a=1&b=2" url
$ curl -X POST -d "@data.txt" url
Для аргументов, закодированных в JSON:
$ curl -X POST -d "{'a':1,'b':2}" -H "Content-Type: application/json" url
$ curl -X POST -d "@data.json" url
Альтернативой curl в духе Python является (/).
$ pip install
Чтобы выполнить запрос POST, сделаем шаги, аналогичные шагам для curl, показанным выше (-f — это синоним для --form):
$ -f POST url a=1 b=2
$ POST -f url < data.txt
Кодировкой по умолчанию является JSON:
$ POST url a=1 b=2
$ POST url < data.json
Программа также обрабатывает заголовки HTTP, cookies, загрузки файлов, аутентификацию, перенаправление, переадресацию, SSL и т.д. Как обычно, вы можете обратиться к документации ().
Вы можете тестировать свои веб-запросы для сайта (/) или же загрузить и запустить сайт в локальном образе Docker:
$ docker run -p 80:80 kennethreitz/
В Python 2 модули веб-клиентов и веб-серверов были слегка разбросаны. Одна из целей Python 3 заключается в том, чтобы разместить эти модули в двух пакетах (как вы помните из главы 11, пакет — всего лишь каталог для хранения файлов модулей):
• управляет всеми деталями клиент-серверного взаимодействия HTTP:
• client выполняет всю работу на стороне клиента;
• server помогает написать веб-сервер Python;
• cookies и cookiejar управляют cookies, которые сохраняют данные между посещениями;
• urllib работает на базе :
• request обрабатывает клиентские запросы;
• response обрабатывает ответы сервера;
• parse разбивает URL на части.
Если вы хотите написать код, который совместим с Python 2 и Python 3, то вам следует помнить о том, что библиотека urllib значительно () изменилась при переходе на Python 3. Чуть ниже, в подразделе «За пределами стандартной библиотеки: requests» на с. 407, представлена более удобная альтернатива.
Воспользуемся стандартной библиотекой с целью получить что-либо с сайта. URL в следующем примере возвращает информацию с тестового сайта:
>>> import urllib.request as ur
>>>
>>> url = '/'
>>> conn = ur.urlopen(url)
Этот небольшой фрагмент кода открыл соединение TCP/IP с удаленным веб-сервером , создал запрос HTTP и получил HTTP-ответ. Ответ содержит не только данные о странице (цитату). Из официальной документации () мы можем узнать, что conn является объектом класса HTTPResponse, содержащим несколько методов и атрибутов. Одна из наиболее важных частей ответа — код статуса HTTP:
>>> print(conn.status)
200
Значение 200 означает, что все прошло гладко. Существуют десятки кодов статуса HTTP, объединенных в пять диапазонов в соответствии с их первой цифрой (сотни):
•1xx (информация) — сервер получил запрос, но имеет некую дополнительную информацию для клиента;
• 2xx (успех) — сработало, каждый код успеха, кроме 200, сообщает дополнительные детали;
• 3xx (перенаправление) — ресурс был перемещен, поэтому ответ возвращает клиенту новый URL;
• 4xx (ошибка клиента) — некие проблемы на стороне клиента, например знаменитая ошибка 404 (ресурс не найден). Код 418 (I’m a teapot) был первоапрельской шуткой;
•5xx (ошибка сервера) — код 500 — общая ошибка. Вы можете встретить ошибку 502 (ошибочный шлюз), если произошел разрыв связи между веб-сервером и машинным интерфейсом.
Чтобы получить содержимое веб-страницы, используйте метод read() переменной conn. Вы получите значение типа bytes. Получим данные и выведем на экран первые 50 байт:
>>> data = conn.read()
>>> print(data[:50])
b'<!doctype html>\n<html>\n<head>\n <title>Example D'
Мы можем преобразовать эти байты в строку и вывести 50 первых символов:
>>> str_data = data.decode('utf8')
>>> print(str_data[:50])
<!doctype html>
<html>
<head>
<title>Example D
>>>
Остальная часть решения включает в себя дополнительный код HTML и CSS.
Из любопытства взглянем, какие еще заголовки HTTP были отправлены нам:
>>> for key, value in conn.getheaders():
... print(key, value)
...
Cache-Control max-age=604800
Content-Type text/html; charset=UTF-8
Date Sun, 05 May 2019 03:09:26 GMT
Etag "1541025663+ident"
Expires Sun, 12 May 2019 03:09:26 GMT
Last-Modified Fri, 09 Aug 2013 23:54:35 GMT
Server ECS (agb/5296)
Vary Accept-Encoding
X-Cache HIT
Content-Length 1270
Connection close
Помните пример работы с telnet, который я показывал ранее? Теперь наша библиотека Python может разбирать заголовки этих HTTP-запросов и размещать их в словарь. Date и Server кажутся довольно очевидными, некоторые другие — нет. Полезно знать, что HTTP имеет набор стандартных заголовков, таких как Content-Type, и множество опциональных.
В начале главы 1 вы увидели программу, которая получает доступ к Wayback Machine API с помощью стандартных библиотек urllib.request и json. За ним следовал другой пример, использовавший стороннюю библиотеку requests. Он был короче и проще для понимания.
Я считаю, что для большинства задач, связанных с разработкой веб-клиентов, проще использовать библиотеку requests. Вы можете просмотреть ее документацию по адресу (она довольно хорошо написана), чтобы получить более подробную информацию. Я покажу основные принципы работы с этой библиотекой в данном подразделе и буду задействовать ее на протяжении всей книги для решения задач, связанных с веб-клиентами.
Для начала нужно установить библиотеку requests:
$ pip install requests
Переделаем предыдущий вызов сервиса с example.com с помощью библиотеки requests:
>>> import requests
>>> resp = requests.get('')
>>> resp
<Response [200]>
>>> resp.status_code
200
>>> resp.text[:50]
'<!doctype html>\n<html>\n<head>\n <title>Example D'
Для демонстрации запроса JSON создадим сокращенную версию программы, которую вы увидите в конце данной главы. Вы предоставляете строку, и программа воспользуется поисковым API Internet Archive, чтобы пройтись по названиям миллиардов мультимедиа, сохраненным в этом архиве. Обратите внимание: в вызов requests.get(), показанный в примере 18.1, вам нужно передать только словарь params и библиотека requests обработает управляющие символы и создаст запрос.
Пример 18.1. ia.py
import json
import sys
import requests
def search(title):
url = ""
params = {"q": f"title:({title})",
"output": "json",
"fields": "identifier,title",
"rows": 50,
"page": 1,}
resp = requests.get(url, params=params)
return resp.json()
if __name__ == "__main__":
title = sys.argv[1]
data = search(title)
docs = data["response"]["docs"]
print(f"Found {len(docs)} items, showing first 10")
print("identifier\ttitle")
for row in docs[:10]:
print(row["identifier"], row["title"], sep="\t")
Сколько у них элементов, связанных с вендиго?
$ python ia.py wendigo
Found 24 items, showing first 10
identifier title
cd_wendigo_penny-sparrow Wendigo
Wendigo1 Wendigo 1
wendigo_ag_librivox The Wendigo
thewendigo10897gut The Wendigo
isbn_9780843944792 Wendigo mountain ; Death camp
jamendo-060508 Wendigo - Audio Leash
fav-lady_wendigo lady_wendigo Favorites
011bFearTheWendigo 011b Fear The Wendigo
CharmedChats112 Episode 112 - The Wendigo
jamendo-076964 Wendigo - Tomame o Dejame>
Первую колонку (идентификатор) можно использовать для того, чтобы просмотреть элемент на сайте archive.org. Вы узнаете, как это делается, в конце главы.
Веб-разработчики обнаружили, что Python хорошо подходит для написания веб-серверов и программ, работающих на серверной стороне. Это привело к появлению такого множества фреймворков, написанных на данном языке, что теперь уже становится трудно исследовать их все и сделать выбор, не говоря уже о принятии решения о том, какие из них обсудить в книге.
Веб-фреймворк предоставляет функции, позволяющие построить сайты, поэтому может решать большее количество задач, чем простой веб-сервер (HTTP). Вы встретитесь с функциями маршрутизации (URL к функции сервера), шаблонами (HTML с динамическими включениями), отладкой и др.
Я не буду говорить в этой книге обо всех фреймворках — рассмотрю лишь те, которые относительно просты в использовании и подходят для создания настоящих сайтов. Я также покажу, как запускать динамические части сайта на традиционном веб-сервере с помощью Python и других составляющих.
Вы можете запустить простейший веб-сервер, просто введя одну строку кода Python:
$ python -m
С помощью этой строки вы реализуете примитивный Python HTTP-сервер. Если не возникло никаких проблем, то вы увидите исходное сообщение о статусе:
Serving HTTP on 0.0.0.0 port 8000 ...
Запись 0.0.0.0 означает любой адрес TCP, поэтому веб-клиенты могут получать к нему доступ независимо от того, какой адрес имеет сервер. В главе 11 вы можете прочитать о некоторых низкоуровневых деталях TCP и других системах соединения в сеть.
Теперь вы можете запрашивать файлы, чьи пути относительны к вашему текущему каталогу, и они будут вам возвращены. Если вы введете в своем браузере строку , то должны увидеть список каталогов и сервер выведет на экран строки обращения к журналам, например такие:
127.0.0.1 - - [20/Feb/2013 22:02:37] "GET / HTTP/1.1" 200 -
Элементы localhost и 127.0.0.1 являются для TCP синонимами вашего локального компьютера, поэтому сработают независимо от того, подключены ли вы к Интернету. Вы можете интерпретировать эти строки следующим образом:
•127.0.0.1 — IP-адрес клиента;
• первый символ - — имя удаленного пользователя, если он присутствует;
• второй символ - — имя авторизующегося пользователя, если требуется;
• [20/Feb/201322:02:37] — дата и время доступа;
• "GET/HTTP/1.1" — команда, отправленная веб-серверу:
• метод HTTP (GET);
• запрошенный ресурс (/, верхний уровень);
• версия HTTP (HTTP/1.1);
• последнее число (200) — код статуса HTTP, возвращенный веб-сервером.
Щелкните на любом файле. Если ваш браузер может распознать его формат (HTML, PNG, GIF, JPEG и т.д.), то должен отобразить его, и сервер занесет этот запрос в журнал. Например, если в вашем текущем каталоге имеется файл oreilly.png, запрос должен вернуть изображение встревоженной зверушки, показанное на рис. 20.2, а в журнале должна появиться похожая запись:
127.0.0.1 - - [20/Feb/2013 22:03:48] "GET /oreilly.png HTTP/1.1" 200 -
При наличии в этом каталоге других файлов их названия должны появиться в списке. Можете щелкнуть на одном из файлов, чтобы загрузить его. Если ваш браузер сконфигурирован так, чтобы отображать формат данного файла, то вы увидите результат на экране. В противном случае браузер спросит, хотите ли вы загрузить и сохранить файл.
По умолчанию используется порт 8000, но вы можете указать любой другой:
$ python -m 9999
Вы должны увидеть следующее:
Serving HTTP on 0.0.0.0 port 9999 ...
Этот сервер, написанный только на Python, лучше всего подходит для быстрых тестов. Вы можете выключить его, убив его процесс нажатием Ctrl+C.
Вы не должны использовать этот простой сервер для загруженного производственного сайта. Традиционные веб-серверы, такие как Apache и NGINX, гораздо быстрее работают со статическими файлами. Кроме того, этот простой сервер не может взаимодействовать с динамическим содержимым, в отличие от более продвинутых серверов, принимающих дополнительные параметры.
Довольно быстро необходимость в простых файлах исчезает, и нам уже нужен сервер, который может запускать программы динамически. В первые годы существования Всемирной паутины общий интерфейс шлюза (Common Gateway Interface, CGI) был разработан для того, чтобы веб-серверы могли запускать внешние программы и возвращать результаты. CGI также обрабатывал получение входных аргументов от клиента, передавая их через сервер сторонним программам. Однако программы запускались заново при каждом обращении клиента. Масштабировать такие системы было трудно, поскольку даже у небольших программ время загрузки довольно велико.
Чтобы избежать задержки запуска, люди начали встраивать интерпретатор языка в веб-сервер. Apache запускал код на PHP внутри своего модуля mod_php, Perl — внутри модуля mod_perl и Python — внутри модуля mod_python. Далее код этих динамических языков мог быть выполнен внутри долгоиграющего процесса Apache, а не во внешних программах.
Альтернативный метод заключается в том, чтобы запускать динамический язык внутри отдельной долгоиграющей программы и заставить ее обмениваться данными с веб-сервером. Примерами таких программ являются FastCGI и SCGI.
Веб-разработка на Python совершила рывок с появлением Web Server Gateway Interface (WSGI) — универсального API между веб-приложениями и веб-серверами. Все веб-фреймворки и веб-серверы Python, показанные далее, используют WSGI. Обычно вам не нужно знать, как функционирует данный интерфейс (для этого многого и не потребуется), но знание основных принципов его функционирования может действительно помочь разработке. Такое соединение называется синхронным — за одним шагом следует другой.
Я уже несколько раз упоминал, что в Python начали появляться возможности асинхронного программирования, такие как async, await и asyncio. ASGI (Asynchronous Server Gateway Interface) — аналог WSGI, который использует все эти возможности. В приложении В вы познакомитесь с этой темой более подробно, а также увидите примеры новых веб-фреймворков, применяющих ASGI.
Лучшим WSGI-модулем apache (/) является mod_wsgi (). Он может запускать код, написанный на Python, внутри процесса Apache или в отдельном процессе, который обменивается данными с Apache. Если вы используете Linux или OS X, в вашей системе apache уже установлен. Для Windows вам придется устанавливать его самостоятельно ().
Наконец, установите предпочитаемый веб-фреймворк Python, основанный на WSGI. Попробуем использовать в наших примерах фреймворк bottle. Практически вся работа включает в себя конфигурирование Apache, что может оказаться довольно затруднительным.
Создайте тестовый файл и сохраните его как /var/ (пример 18.2).
Пример 18.2. home.wsgi
import bottle
application = bottle.default_app()
@bottle.route('/')
def home():
return "apache and wsgi, sitting in a tree"
На сей раз не вызывайте функцию run(), поскольку это запустит встроенный веб-сервер Python. Нам нужно присвоить некое значение переменной application, поскольку именно его будет проверять mod_wsgi при объединении веб-сервера и кода Python.
Если apache и его модуль mod_wsgi работают корректно, то нужно лишь соединить их с нашим сценарием Python. Надо добавить в файл одну строку, которая определяет сайт по умолчанию для этого сервера apache, но поиск данного файла сам по себе является задачей. Он может называться /etc/apache2/, или /etc/apache2/sites-available/default, или даже быть латинским названием чьей-то ручной саламандры.
Предположим, вы понимаете работу apache и нашли нужный файл. Добавьте эту строку в раздел <VirtualHost>, который управляет стандартным сайтом:
WSGIScriptAlias / /var/
Этот раздел должен выглядеть так:
<VirtualHost *:80>
DocumentRoot /var/
WSGIScriptAlias / /var/
<Directory /var/>
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
Запустите apache или, если он работал, перезапустите, с целью указать ему, что следует использовать новую конфигурацию. Перейдя в браузере по адресу /, вы должны увидеть эту строку:
apache and wsgi, sitting in a tree
Это запустит mod_wsgiво встроенном режиме как часть самого apache.
Вы также можете запустить его в режиме демона — как один или несколько процессов, отдельных от apache. Для этого добавьте две новые строки директив в ваш файл конфигурации apache:
WSGIDaemonProcess domain-name user=user-name group=group-name threads=25
WSGIProcessGroup domain-name
В предыдущем примере переменные user-name и group-name представляют собой имена пользователя и группы в операционной системе, а переменная domain-name — имя вашего интернет-домена. Минимальная конфигурация apache может выглядеть так:
<VirtualHost *:80>
DocumentRoot /var/
WSGIScriptAlias / /var/
WSGIDaemonProcess mydomain.com user=myuser group=mygroup threads=25
WSGIProcessGroup mydomain.com
<Directory /var/>
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
Веб-сервер NGINX (/) не имеет встроенного модуля Python. Вместо этого он обменивается данными с помощью отдельного сервера WSGI, такого как uWSGI или gUnicorn. Вместе они представляют собой очень быструю и удобную в конфигурации платформу для веб-разработки на Python.
Вы можете установить nginx с его официального сайта . Небольшая страница документации () предоставляет инструкции, позволяющие объединить Flask, NGINX и uWSGI.
Ниже перечислены некоторые независимые WSGI-серверы, написанные на Python, которые работают как apache или nginx и используют несколько процессов и/или потоков (см. раздел «Конкурентность» главы 15 на с. 308) для обработки одновременных запросов:
•uwsgi ();
• cherrypy (/);
•pylons (/).
А это серверы, основанные на событиях, которые пользуются одним процессом, но избегают блокирования любым одиночным запросом:
•tornado (/);
• gevent (/);
•gunicorn (/).
О событиях я подробнее рассказывал в разделе «Конкурентность» на с. 308.
Веб-серверы обрабатывают детали работы HTTP и WSGI, но вам нужно использовать веб-фреймворки, чтобы написать код на Python, который будет поддерживать сайт. Так что мы немного поговорим о фреймворках, а затем вернемся к альтернативным способам обслуживания сайтов, использующих их.
Написать сайт на Python можно с помощью множества веб-фреймворков (некоторые даже могут сказать, что их слишком много). Веб-фреймворк обрабатывает как минимум запросы клиента и ответы сервера. Большинство крупных веб-фреймворков могут решать такие задачи, как:
• обработка протокола HTTP;
• аутентификация (authn, или «кто ты?»);
• авторизация (authz, или «что ты можешь сделать?»);
• создание сессии;
• получение параметров;
• валидация параметров (обязательные/опциональные, тип, диапазон);
• обработка глаголов HTTP;
• маршрутизация (функции/классы);
• выдача статических файлов (HTML, JS, CSS, изображения);
• выдача динамических данных (базы данных, сервисы);
• возврат значений и статусов HTTP.
Опциональные возможности:
• создание шаблонов для бэкенда;
• соединение с базами данных, ORM;
• ограничение скорости;
• асинхронные задачи.
В следующих подразделах мы напишем пример, использующий два фреймворка (Bottle и Flask). Они являются синхронными. Далее поговорим об альтернативах, в частности о сайтах, работающих с базами данных. Вы можете найти подходящий фреймворк Python для любого сайта, который только представите.
Bottle состоит из одного файла Python, поэтому его довольно легко опробовать и развернуть. Bottle не является частью стандартной библиотеки Python, поэтому установите его с помощью следующей команды:
$ pip install bottle
Рассмотрим код, который запустит тестовый веб-сервер и вернет текстовую строку, когда ваш браузер обратится по URL /. Сохраните этот файл как bottle1.py (пример 18.3).
Пример 18.3. bottle1.py
from bottle import route, run
@route('/')
def home():
return "It isn't fancy, but it's my home page"
run(host='localhost', port=9999)
Bottle использует декоратор route, чтобы связать URL со следующей функцией; в этом примере / (главная страница) обрабатывается функцией home(). Запустите данный сценарий сервера с помощью следующей команды:
$ python bottle1.py
Обратившись по адресу , вы должны увидеть следующее:
It isn't fancy, but it's my home page
Функция run() запускает встроенный тестовый веб-сервер bottle. Вам не нужно использовать его в программах, написанных с помощью bottle, но это может оказаться полезным на первых порах разработки и тестирования.
Теперь вместо создания текста главной страницы в коде создадим отдельный HTML-файл, который называется index.html и содержит такую строку:
My <b>new</b> and <i>improved</i> home page!!!
Дайте bottle задание возвращать содержимое этого файла, когда запрашивается главная страница. Сохраните данный сценарий как bottle2.py (пример 18.4).
Пример 18.4. bottle2.py
from bottle import route, run, static_file
@route('/')
def main():
return static_file('index.html', root='.')
run(host='localhost', port=9999)
В вызове static_file() мы хотим получить файл index.html из каталога, указанного в root (в нашем случае в '.', текущем каталоге). Если код предыдущего примера все еще выполняется, то остановите его. Теперь запустите новый сервер:
$ python bottle2.py
Каждый раз, когда вы обращаетесь к странице /, вы должны видеть следующее:
My new and improved home page!!!
Добавим последний пример, который демонстрирует, как передавать аргументы в URL и использовать их. Конечно же, этот файл будет называться bottle3.py (пример 18.5).
Пример 18.5. bottle3.py
from bottle import route, run, static_file
@route('/')
def home():
return static_file('index.html', root='.')
@route('/echo/<thing>')
def echo(thing):
return "Say hello to my little friend: %s!" % thing
run(host='localhost', port=9999)
У нас появилась новая функция echo(), в которую мы хотим передавать строковый аргумент через URL. За это отвечает строка @route('/echo/<thing>') в предыдущем примере. Конструкция <thing> в маршруте означает: все, что находится в URL после /echo/, присваивается строковому аргументу thing, который передается функции echo. Чтобы увидеть развитие событий, остановите старый сервер, если он все еще работает, и запустите его с новым кодом:
$ python bottle3.py
Далее перейдите в браузере по ссылке . Вы должны увидеть следующее:
Say hello to my little friend: Mothra!
Оставьте bottle3.py работать еще на пару минут, чтобы мы могли попробовать что-либо еще. Вы проверяли работу этих примеров, вводя URL в браузер и глядя на отображаемые страницы. Вы также можете использовать клиентские библиотеки, такие как requests, чтобы они выполняли работу за вас. Сохраните этот код как bottle_test.py (пример 18.6).
Пример 18.6. bottle_test.py
import requests
resp = requests.get('')
if resp.status_code == 200 and \
resp.text == 'Say hello to my little friend: Mothra!':
print('It worked! That almost never happens!')
else:
print('Argh, got this:', resp.text)
Отлично! Теперь запустите этот код:
$ python bottle_test.py
В терминале вы должны увидеть следующее:
It worked! That almost never happens!
Перед вами небольшой пример юнит-теста. В главе 19 вы можете получить более подробную информацию о том, почему тесты — это хорошо и как написать их с помощью Python.
У фреймворка Bottle больше возможностей, чем я вам показал. В частности, при вызове функции run() можно попробовать добавить следующие аргументы:
•debug=True — создает страницу отладки, если вы получаете ошибку HTTP;
•reloader=True — перезагружает страницу в браузере, если вы измените хотя бы небольшой кусочек кода.
Все это хорошо задокументировано на сайте разработчика .
Bottle — это хороший фреймворк для того, чтобы начать работу. Но если вам нужно больше возможностей, то попробуйте Flask. Он был создан в 2010 году как первоапрельская шутка, но реакция энтузиастов вдохновила его автора, Армина Ронахера, сделать его настоящим фреймворком. Он назвал результат Flask («склянка»), обыгрывая название Bottle — «бутылка».
Flask почти так же прост в использовании, как и Bottle, но поддерживает множество расширений, которые могут оказаться полезными в профессиональной веб-разработке, например аутентификацию с помощью Facebook и интеграцию с базами данных. Это решение мне нравится больше других веб-фреймворков Python, поскольку в нем сбалансированы простота применения и богатый набор функций.
Пакет flask включает в себя библиотеку WSGI werkzeug и библиотеку шаблонов jinja2. Вы можете установить его с помощью терминала:
$ pip install flask
Переделаем наш последний пример с использованием фреймворка Flask. Однако для начала нужно внести несколько изменений.
• Во Flask каталог по умолчанию для статических файлов называется static, и URL для таких файлов тоже начинается со /static. Мы изменяем папку на '.' (текущий каталог) и префикс URL на '' (пустой), чтобы позволить URL / отображать файл index.html.
• В функции run() установка параметра debug=True активизирует также автоматическую перезагрузку, тогда как фреймворк bottle для отладки и перезагрузки использует отдельные аргументы.
Сохраните этот код в файл flask1.py (пример 18.7).
Пример 18.7. flask1.py
from flask import Flask
app = Flask(__name__, static_folder='.', static_url_path='')
@app.route('/')
def home():
return app.send_static_file('index.html')
@app.route('/echo/<thing>')
def echo(thing):
return "Say hello to my little friend: %s" % thing
app.run(port=9999, debug=True)
Далее запустите сервер из терминала или окна:
$ python flask1.py
Протестируйте главную страницу, введя в браузер следующий URL:
/
Вы должны увидеть следующее (как и в случае с bottle):
My new and improved home page!!!
Попробуйте обратиться к конечной точке /echo:
Вы должны увидеть следующее:
Say hello to my little friend: Godzilla
Есть еще одно преимущество установки параметра debug равным True при вызове метода run. Если в серверном коде генерируется исключение, то Flask возвращает особую отформатированную страницу, содержащую полезные сведения о том, что и где пошло не так. Даже больше: вы можете вводить команды с целью увидеть значения переменных в программе сервера.
Не устанавливайте параметр debug = True на производственных веб-серверах. Таким образом потенциальные злоумышленники получат слишком много информации о вашем сервере.
До сих пор примеры с использованием Flask повторяли то, что мы делали с помощью фреймворка Bottle. Что такого может делать Flask в отличие от Bottle? Flask содержит jinja2 — более широкую систему шаблонов. Рассмотрим небольшой пример одновременного применения jinja2 и Flask.
Создайте каталог templates и файл flask2.html внутри него (пример 18.8).
Пример 18.8. flask2.html
<html>
<head>
<title>Flask2 Example</title>
</head>
<body>
Say hello to my little friend: {{ thing }}
</body>
</html>
Далее мы напишем серверный код, который получает этот шаблон, заполняет значение аргумента thing, передаваемого нами, и отрисовывает его как HTML (я опущу функцию home() для экономии места). Сохраните этот файл под именем flask2.py (пример 18.9).
Пример 18.9. flask2.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/echo/<thing>')
def echo(thing):
return render_template('flask2.html', thing=thing)
app.run(port=9999, debug=True)
Аргумент thing=thing утверждает, что для передачи переменной с именем thing в шаблон эта переменная содержит значение строки thing.
Убедитесь, что файл flask1.py перестал работать, и запустите файл flask2.py:
$ python flask2.py
Теперь введите этот URL:
Вы должны увидеть следующее:
Say hello to my little friend: Gamera
Модифицируем наш пример и сохраним его в каталоге templates под именем flask3.html:
<html>
<head>
<title>Flask3 Example</title>
</head>
<body>
Say hello to my little friend: {{ thing }}.
Alas, it just destroyed {{ place }}!
</body>
</html>
Второй аргумент в URL, echo, вы можете передать множеством способов.
Передача аргумента как части пути URL. С помощью этого метода вы просто расширяете URL (сохраните код, показанный в примере 18.10, как flask3a.py):
Пример 18.10. flask3a.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/echo/<thing>/<place>')
def echo(thing, place):
return render_template('flask3.html', thing=thing, place=place)
app.run(port=9999, debug=True)
Как обычно, остановите предыдущий сценарий тестового сервера, если он еще работает, и затем запустите новый:
$ python flask3a.py
URL должен выглядеть так:
Вы должны увидеть следующее:
Say hello to my little friend: Rodan. Alas, it just destroyed McKeesport!
Или же вы можете передать аргументы как параметры команды GET, как это показано в примере 18.11; сохраните файл как flask3b.py.
Пример 18.11. flask3b.py
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route('/echo/')
def echo():
thing = request.args.get('thing')
place = request.args.get('place')
return render_template('flask3.html', thing=thing, place=place)
app.run(port=9999, debug=True)
Запустите новый сценарий сервера:
$ python flask3b.py
В этот раз используйте такой URL:
Вы должны увидеть следующее:
Say hello to my little friend: Gorgo. Alas, it just destroyed Wilmerding!
Когда команда GET используется в URL, любые аргументы должны передаваться в формате &ключ1=знач1&ключ2=знач2&...
Вы также можете использовать оператор словаря **, чтобы передать несколько аргументов в шаблон с помощью одного словаря (назовите файл flask3c.py), как показано в примере 18.12.
Пример 18.12. flask3c.py
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route('/echo/')
def echo():
kwargs = {}
kwargs['thing'] = request.args.get('thing')
kwargs['place'] = request.args.get('place')
return render_template('flask3.html', **kwargs)
app.run(port=9999, debug=True)
Элемент **kwargs действует как конструкция thing=thing,place=place. Используя этот словарь, можно сэкономить немного времени, если входных аргументов много.
У языка шаблонов jinja2 гораздо больше возможностей. Если вы работали на PHP, то увидите много общего.
Django (/) — очень популярный веб-фреймворк, написанный на Python, особенно для крупных сайтов. Его стоит изучить по многим причинам, среди которых регулярно появляющиеся требования опыта работы с Django в объявлениях о вакансиях. Он содержит код ORM (об ORM мы говорили в подразделе SQLAlchemy на с. 348), позволяющий создавать автоматические веб-страницы для типичных функций баз данных CRUD (создание, замена, обновление, удаление), которые я рассматривал в главе 16. Он также включает автоматические страницы администрирования для этих веб-страниц, но они предназначены для внутреннего использования среди программистов, а не размещения на публичных веб-страницах. Вам не обязательно использовать ORM именно для Django, если больше нравится применять что-то другое, например SQLAlchemy или прямые запросы SQL.
Вы можете сравнить фреймворки с помощью онлайн-таблицы, размещенной по адресу :
•fastapi (/) обрабатывает как синхронные (WSGI), так и асинхронные (ASGI) вызовы, использует подсказки типов и генерирует тестовые страницы. Очень хорошо задокументирован. Рекомендую;
• web2py (/) работает примерно с тем же, с чем и django, только немного в другом стиле;
• pyramid (/) появился из более раннего проекта pylons, с точки зрения функционала похож на django;
• turbogears (/) поддерживает ORM, множество баз данных и несколько языков шаблонов;
• wheezy.web () более молодой фреймворк, оптимизирован для повышения производительности. Недавние исследования показали, что он работает быстрее других ();
• molten (/) также использует подсказки типов, но поддерживает только WSGI;
• apistar (/) похож на fastapi, но это скорее инструмент для валидации API, чем веб-фреймворк;
•masonite (/) — аналог Ruby on Rails или Laravel, написанный на Python.
Интернет и базы данных — это неразлучная парочка: где одно, там и другое. В реальных приложениях Python в какой-то момент вам может понадобиться предоставить веб-интерфейс (сайт и/или API) реляционной базе данных.
Вы можете создать собственный интерфейс с помощью:
• веб-фреймворка, такого как Bottle или Flask;
• пакет для работы с БД, наподобие db-api или SQLAlchemy;
• драйвер для базы данных вроде pymysql.
Вместо этого вы также можете использовать один из пакетов, объединяющих Интернет и базы данных:
• connexion (/);
• datasette (/);
• sandman2 ();
• flask-restless (/).
Или можете использовать фреймворк со встроенной поддержкой баз данных, такой как Django.
Кроме того, ничто не заставляет вас выбирать именно реляционную базу данных. Если схема ваших данных может значительно изменяться — столбцы явно различаются в разных строках, — то вам стоит выбрать базу данных, не имеющую схемы. Это могут быть базы данных NoSQL, которые мы рассматривали в главе 16. Однажды я работал над сайтом, который изначально хранил данные в базе данных NoSQL, затем переключался на одну реляционную базу данных, затем на другую, потом на другую базу данных NoSQL и, наконец, возвращался к одной из реляционных.
Мы рассмотрели традиционные веб-клиенты и серверные приложения, потребляющие и генерирующие HTML-страницы. Интернет оказался мощным способом объединять приложения и данные во многих форматах, не только HTML.
Начнем с небольшого сюрприза. Запустите сессию Python в окне терминала и введите следующую строку:
>>> import antigravity
Эта строка скрыто вызывает модуль стандартной библиотеки webbrowser и перенаправляет ваш браузер по информативной ссылке Python.
Вы можете использовать этот модуль непосредственно. Данная программа загружает главную страницу сайта Python в ваш браузер:
>>> import webbrowser
>>> url = '/'
>>> webbrowser.open(url)
True
Этот код откроет ее в новом окне:
>>> webbrowser.open_new(url)
True
А этот — на новой вкладке, если ваш браузер поддерживает вкладки:
>>> webbrowser.open_new_tab('/')
True
Модуль webbrowser заставляет браузер делать всю работу.
Вместо вызова браузера, как это делает модуль webbrowser, модуль webview отображает страницу в собственном окне, используя интерфейс, нативный для вашей машины. Чтобы установить его в ОС Linux или macOS:
$ pip install pywebview[qt]
В Windows:
$ pip install pywebview[cef]
Обратитесь к инструкциям по установке (), если у вас возникли проблемы.
Рассмотрим пример, в котором я передал модулю официальный сайт правительства США, показывающий текущее время:
>>> import webview
>>> url = input("URL? ")
URL?
>>> webview.create_window(f"webview display of {url}", url)
На рис. 18.1 показан полученный мной результат.
Чтобы остановить программу, убейте окно отображения.
Рис. 18.1. Окно webview
Зачастую данные доступны только внутри веб-страниц. При желании получить к ним доступ вам нужно получить доступ к странице через браузер и прочитать ее. Если с момента вашего последнего визита авторы сайта внесли какие-нибудь изменения, то местоположение и стиль данных могли измениться.
Вместо того чтобы публиковать веб-страницы, вы можете предоставить доступ к данным через веб-интерфейс программирования приложений (Application Programming Interface, API). Клиенты получают доступ к вашему сервису, делая запросы к URL, и получают ответы, содержащие статус и данные. Вместо HTML-страниц данные имеют формат, который удобнее использовать в других программах, таких как JSON и XML (в главе 16 содержится более подробная информация о форматах).
Понятие «передача состояния представления» (Representational State Transfer, REST) было определено Роем Филдингом (Roy Fielding) в его докторской диссертации. Многие продукты имеют REST-интерфейс или интерфейс RESTful. На практике это часто означает, что они имеют веб-интерфейс — определения URL, предназначенные для доступа к веб-сервису.
Сервис RESTful использует глаголы HTTP определенными способами, описанными далее:
•HEAD получает информацию о ресурсе, но не его данные;
• GET получает данные ресурса с сервера. Это стандартный метод, используемый вашим браузером. GET не должен применяться для создания, изменения или удаления данных;
• POST создает новый ресурс;
• PUT заменяет существующий ресурс, создавая его, если его нет;
• PATCH частично обновляет ресурс;
•DELETE — этот глагол говорит сам за себя: DELETE удаляет. Мы за правдивость в рекламе!
Клиент RESTful также может запрашивать содержимое одного или нескольких типов с помощью заголовков запроса HTTP. Например, сложный сервис с интерфейсом REST может принимать и возвращать данные в строках JSON.
Иногда вам нужно получить немного больше информации — рейтинг фильма, цену акции или доступность продукта, — но требуемая информация доступна только на HTML-страницах, при этом окружена рекламой и посторонним контентом.
Вы можете извлечь необходимую информацию вручную, сделав следующее.
1. Введите URL в браузер.
2. Подождите, пока загрузится удаленная страница.
3. Просмотрите отображенную страницу на предмет необходимой информации.
4. Запишите ее где-нибудь.
5. Повторите процесс для связанных URL.
Однако гораздо более приятно автоматизировать некоторые из этих шагов. Программа, автоматически получающая данные из Сети, называется краулером или веб-пауком. После того как содержимое было получено с одного из удаленных веб-серверов, скрапер анализирует ее, чтобы найти иголку в стоге сена.
Если вам нужно мощное решение, объединяющее в себе возможности краулера и скрапера, то стоит загрузить Scrapy (/):
$ pip install scrapy
Эта команда установит сам модуль и программу scrapy, работающую в командной строке.
Scrapy — это фреймворк, а не модуль, в отличие от BeautifulSoup. Он имеет больше возможностей, но зачастую его трудно настроить. Чтобы узнать больше о Scrapy, прочтите Scrapy at a Glance () и руководство ().
Если у вас уже есть HTML-данные с сайта и вы просто хотите извлечь оттуда данные, то вам подойдет BeautifulSoup (). Анализировать HTML труднее, чем кажется. Это происходит потому, что большая часть HTML-кода на общедоступных веб-страницах технически некорректна: незакрытые теги, неправильная вложенность и прочие усложнения. Если вы пытаетесь написать свой HTML-анализатор с помощью регулярных выражений, которые мы рассматривали в разделе «Текстовые строки: регулярные выражения» на с. 253, то довольно скоро столкнетесь с подобным беспорядком.
Чтобы установить BeautifulSoup, введите следующую команду (не забудьте поставить в конце 4, иначе pip попробует установить более старую версию и, возможно, выдаст ошибку):
$ pip install beautifulsoup4
Теперь воспользуемся им для того, чтобы получить все ссылки с веб-страницы. HTML-элемент a — это ссылка, а href — ее атрибут, который представляет собой место назначения ссылки. В примере 18.13 мы определим функцию get_links(), которая делает грязную работу, и основную программу, получающую один или несколько URL как аргументы командной строки.
Пример 18.13. links.py
def get_links(url):
import requests
from bs4 import BeautifulSoup as soup
result = requests.get(url)
page = result.text
doc = soup(page)
links = [element.get('href') for element in doc.find_all('a')]
return links
if __name__ == '__main__':
import sys
for url in sys.argv[1:]:
print('Links in', url)
for num, link in enumerate(get_links(url), start=1):
print(num, link)
print()
Я сохранил эту программу под именем links.py, а затем запустил с помощью данной команды:
$ python links.py
Взгляните на первые несколько отображенных строк:
Links in /
1
2 /
3 /
4
5 /
6 /
7 javascript:void(0)
8 /
9
10
Кеннет Ритц, автор популярного пакета для работы с веб-клиентами requests, написал новую библиотеку для скрапинга, которая называется Requests-HTML (/) (для Python 3.6 и более новых версий).
Она получает страницу и обрабатывает ее элементы, поэтому вы, например, можете найти все ссылки, все содержимое или атрибуты любого элемента HTML.
Библиотека имеет простой дизайн, похожий на requests и другие пакеты этого автора. В конечном счете она может быть проще в использовании, чем BeautifulSoup или Scrapy.
Напишем полноценную программу.
Она будет искать видео с помощью API архива Internet Archive. Это один из немногих API, которые позволяют получить анонимный доступ, и он будет существовать и после того, как данная книга будет напечатана.
Большинство веб-API требуют от вас получить ключ API и предоставлять его всякий раз, когда вы хотите получить к нему доступ. Почему? Это «трагедия общин» — бесплатные ресурсы с анонимным доступом зачастую используются слишком много и неправильно. Вот почему у нас не может быть хороших сервисов.
Программа, показанная в примере 18.14, делает следующее:
• приглашает вас ввести часть названия фильма или видео;
• ищет элемент с таким названием в Internet Archive;
• возвращает список идентификаторов, имен и описаний;
• перечисляет их и просит вас выбрать один;
• отображает это видео в вашем браузере;
Сохраните данный файл под именем iamovies.py.
Функция search() использует библиотеку requests, чтобы получить доступ к URL и результаты и преобразовать их в формат JSON. Другие функции обрабатывают все остальное. Вы увидите использование включений списков, разбиения строк и других возможностей, которые были представлены в предыдущих главах. (Номера строк не являются частью исходного кода; они будут использованы в упражнениях, чтобы найти нужные фрагменты кода.)
Пример 18.14. iamovies.py
1 """Найдем видео в Internet Archive
2 по фрагменту названия и отобразим его."""
3
4 import sys
5 import webbrowser
6 import requests
7
8 def search(title):
9 """Возвращаем список кортежей, состоящих из трех элементов
10 (идентификатор, заголовок, описание), которые описывают видеоролики,
11 чьи заголовки частично соответствуют значению переменной title."""
12 search_url = ""
13 params = {
14 "q": "title:({}) AND mediatype:(movies)".format(title),
15 "fl": "identifier,title,description",
16 "output": "json",
17 "rows": 10,
18 "page": 1,
19 }
20 resp = requests.get(search_url, params=params)
21 data = resp.json()
22 docs = [(doc["identifier"], doc["title"], doc["description"])
23 for doc in data["response"]["docs"]]
24 return docs
25
26 def choose(docs):
27 """Выведем номер строки, заголовок и частичное описание для
28 каждого кортежа из списка :docs. Позволим пользователю выбрать
29 номер строки. Если он корректен, вернем первый элемент выбранного
30 кортежа ("идентификатор"). В противном случае вернем None."""
31 last = len(docs) - 1
32 for num, doc in enumerate(docs):
33 print(f"{num}: ({doc[1]}) {doc[2][:30]}...")
34 index = input(f"Which would you like to see (0 to {last})? ")
35 try:
36 return docs[int(index)][0]
37 except:
38 return None
39
40 def display(identifier):
41 """Отобразим видео из архива в браузере с помощью идентификатора """
42 details_url = ")
43 print("Loading", details_url)
44 webbrowser.open(details_url)
45
46 def main(title):
47 """Найдем все фильмы, чье название соответствует значению переменной
48 :title. Узнаем выбор пользователя и отобразим его в браузере."""
49 identifiers = search(title)
50 if identifiers:
51 identifier = choose(identifiers)
52 if identifier:
53 display(identifier)
54 else:
55 print("Nothing selected")
56 else:
57 print("Nothing found for", title)
58
59 if __name__ == "__main__":
60 main(sys.argv[1])
Вот что я получил, запустив эту программу и выполнив поиск для буквосочетания eegah:
$ python iamovies.py eegah
0: (Eegah) From IMDb : While driving thro...
1: (Eegah) This film has fallen into the ...
2: (Eegah) A caveman is discovered out in...
3: (Eegah (1962)) While driving through the dese...
4: (It's "Eegah" - Part 2) Wait till you see how this end...
5: (EEGAH trailer) The infamous modern-day cavema...
6: (It's "Eegah" - Part 1) Count Gore De Vol shares some ...
7: (Midnight Movie show: eegah) Arch Hall Jr...
Which would you like to see (0 to 7)? 2
Loading
Он отобразил страницу в моем браузере, она готова к запуску (рис. 18.2).
Рис. 18.2. Результат поиска фильма
Следующая глава чрезвычайно практична, в ней рассматриваются основы современной разработки на Python. Узнайте же, как стать хладнокровным питонщиком.
18.1. Если вы еще не установили Flask, то сделайте сейчас. Это также позволит установить werkzeug, jinja2 и, возможно, другие пакеты.
18.2. Создайте скелет сайта с помощью веб-сервера Flask. Убедитесь, что сервер начинает свою работу по адресу Localhost на стандартном порте 5000. Если ваш компьютер уже использует этот порт для чего-то еще, то воспользуйтесь другим портом.
18.3. Добавьте функцию home() для обработки запросов к главной странице. Пусть она возвращает строку It'salive!.
18.4. Создайте шаблон для jinja2, который называется home.html и содержит следующий контент:
<html>
<head>
<title>It's alive!</title>
<body>
I'm of course referring to {{thing}}, which is {{height}} feet tall and {{color}}.
</body>
</html>
18.5. Модифицируйте функцию home() вашего сервера, чтобы она использовала шаблон home.html. Передайте ей три параметра для команды GET: thing, height и color.
Компания, основанная Стивом Джобсом после его ухода из Apple.
Развеем старый миф. Сенатор (а впоследствии вице-президент) Эл Гор выступал в поддержку двухпартийного законодательства и сотрудничества, включая финансирование группы, создавшей Mosaic, что позволило значительно развить Интернет на раннем этапе. Он никогда не утверждал, что «изобрел Интернет»; эту фразу ему приписали его политические конкуренты, когда он баллотировался в президенты в 2000-м.
Если вы по какой-то причине не видите ее, то посетите сайт xkcd ().
Неприятный термин, если вы арахнофоб.
Если помните, я использовал еще один API для работы с архивами в основном примере главы 1.
В этом фильме Ричард Кил снялся в роли пещерного человека за много лет до того, как сыграл Челюсти в фильме о Джеймсе Бонде.