реклама
Бургер менюБургер меню

Кеннет Рейтц – Автостопом по Python (страница 60)

18

Используйте метод requests.get, чтобы получить веб-страницу с данными, преобразуйте их с помощью модуля html и сохраните результат в дереве:

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

Мы используем свойство page.content, а не page.text, поскольку метод html.fromstring() неявно ожидает получить объект типа bytes.

Теперь дерево содержит весь файл HTML и имеет удобную структуру. Мы можем пойти двумя путями: использовать XPath (http://lxml.de/xpathxslt.html) или CSSSelect (http://lxml.de/cssselect.html). Оба этих способа стандартные для указания пути с помощью дерева HTML, они определены и поддерживаются World Wide Web Consortium (W3C) и реализованы как модули в lxml. В этом примере мы используем XPath. Руководство по XPath (http://www.w3schools.com/xsl/xpath_intro.asp) поможет вам начать работу.

Существуют различные инструменты для получения XPath элементов изнутри вашего браузера вроде Firebug for Firefox или Chrome Inspector. Если используете Chrome, щелкните правой кнопкой мыши на элементе, выберите пункт меню Inspect element (Инспектировать элемент), подсветите код, снова щелкните правой кнопкой и выберите Copy XPath (Скопировать XPath).

После небольшого анализа мы видим, что данные на нашей странице содержатся в двух элементах: div (с заголовком buyer-name) и span (имеющий класс item-price):

<div title="buyer-name">Carson Busses</div>

<span class="item-price">$29.95</span>

Зная это, мы можем создать корректный запрос XPath и использовать lxml-функцию xpath, как показано в примере:

# Это создаст список покупателей:

buyers = tree.xpath('//div[@title="buyer-name"]/text()')

# Это создаст список цен

prices = tree.xpath('//span[@class="item-price"]/text()')

Посмотрим, что получилось:

>>> print('Buyers: ', buyers)

Buyers: ['Carson Busses', 'Earl E. Byrd', 'Patty Cakes',

'Derri Anne Connecticut', 'Moe Dess', 'Leda Doggslife', 'Dan Druff',

'Al Fresco', 'Ido Hoe', 'Howie Kisses', 'Len Lease', 'Phil Meup',

'Ira Pent', 'Ben D. Rules', 'Ave Sectomy', 'Gary Shattire',

'Bobbi Soks', 'Sheila Takya', 'Rose Tattoo', 'Moe Tell']

>>>

>>> print('Prices: ', prices)

Prices: ['$29.95', '$8.37', '$15.26', '$19.25', '$19.25',

'$13.99', '$31.57', '$8.49', '$14.47', '$15.86', '$11.11',

'$15.98', '$16.27', '$7.50', '$50.85', '$14.26', '$5.68',

'$15.00', '$114.07', '$10.09']

Сериализация данных

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

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

Pickle

Нативный модуль сериализации данных для Python называется Pickle (https://docs.python.org/2/library/pickle.html). Рассмотрим пример его использования:

import pickle

# Пример словаря

grades = { 'Alice': 89, 'Bob': 72, 'Charles': 87 }

# Используем дампы для преобразования объекта в сериализованную строку

serial_grades = pickle.dumps(grades)

# Используем loads для десериализации строки в объект

received_grades = pickle.loads(serial_grades)

Функции, методы, классы и эфемерные объекты вроде конвейеров сериализовать нельзя.

В соответствии с документацией к Pickle «модуль pickle небезопасно использовать для ошибочных или вредоносных данных. Никогда не десериализуйте данные, полученные из недостоверных источников».

Межъязыковая сериализация

Если вы ищете модуль сериализации данных, который поддерживает несколько языков, подойдут Protobuf от Google (https://developers.google.com/protocol-buffers/docs/pythontutorial) и Avro от Apache (https://avro.apache.org/docs/1.7.6/gettingstartedpython.html).

В стандартной библиотеке имеется библиотека xdrlib (https://docs.python.org/3/library/xdrlib.html), позволяющая упаковывать и распаковывать данные в формате External Data Representation (XDR) (https://en.wikipedia.org/wiki/External_Data_Representation) от компании Sun. Этот формат не зависит от операционной системы и протокола передачи данных. Он работает на гораздо более низком уровне, нежели предыдущие варианты, и просто выполняет конкатенацию упакованных байтов, поэтому и клиент, и сервер должны знать тип и порядок упаковки. Рассмотрим пример сервера, получающего данные в формате XDR:

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

Мы должны знать заранее, что получаем данные типа unsigned int.

В этой строке считываем остальную часть сообщения…

…а в этой сбрасываем распаковщик, чтобы он начал работать с новыми данными.

Мы должны знать заранее, что получаем одну строку, а затем одно число с плавающей точкой.

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

Сначала упакуем все данные, подлежащие отправке.

Далее отдельно упакуем длину сообщения…

…и добавим ее ко всему сообщению.

Сжатие

Стандартная библиотека Python поддерживает сжатие и декомпрессию данных с использованием алгоритмов zlib, gzip, bzip2 и lzma, а также создание архивов ZIP и TAR. Для того чтобы поместить в ZIP-архив данные, сериализованные с помощью Pickle, сделайте следующее:

import pickle

import gzip

data = "my very big object"

# Для запаковки и сериализации:

with gzip.open('spam.zip', 'wb') as my_zip:

····pickle.dump(data, my_zip)

# Для распаковки и десериализации:

with gzip.open('spam.zip', 'rb') as my_zip:

····unpickled_data = pickle.load(my_zip)

Протокол буфера