Использование реальных данных – API сайта eBay

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

Получение ключа разработчика

Процедура доступа к API сайта eBay состоит из нескольких шагов, но она относительно проста и автоматизирована. Хорошее описание можно найти в кратком руководстве для начинающих по адресу http:// developer.ebay.com/quickstartguide.

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

•          Ключ разработчика.

•          Ключ приложения.

•          Ключ сертификата.

•          Маркер аутентификации (очень длинный).

Создайте новый файл ebaypredict.py и включите в него следующий код, в котором импортированы необходимые модули и прописаны полученные вами ключи:

import httplib

from xml.dom.minidom import parse, parseString, Node

devKey = ‘ключ разработчика’ appKey = ‘ключ приложения’ certKey = ‘ключ сертификата’ userToken = ‘маркер’ serverUrl = ‘api.ebay.com’

Официального интерфейса к API сайта eBay на языке Python не существует, но есть стандартный API на языке XML, реализованный в библиотеках httplib и minidom. В этом разделе нам потребуется только два вызова API сайта eBay: GetSearchResults и GetItem, но большую часть приведенного кода можно использовать и для других вызовов. Дополнительную информацию о структуре API можно найти в документации по адресу http://developer.ebay.com/DevZone/XML/docs/WebHelp/index.htm.

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

Получив ключи, необходимо подготовить соединение для работы с API сайта eBay. От вас требуется передать несколько HTTP-заголовков, в которых заданы ключи и указано, какую функцию API вы собираетесь вызывать. Для этой цели включите в файл ebaypredict.py функцию getHeaders – она получает имя функции API и возвращает словарь заголовков, которые предстоит передать библиотеке httplib: def getHeaders(apicall,siteID="0",compatabilityLevel = "433"): headers = {"X-EBAY-API-COMPATIBILITY-LEVEL": compatabilityLevel, "X-EBAY-API-DEV-NAME": devKey, "X-EBAY-API-APP-NAME": appKey, "X-EBAY-API-CERT-NAME": certKey, "X-EBAY-API-CALL-NAME": apicall, "X-EBAY-API-SITEID": siteID, "Content-Type": "text/xml"} return headers

Помимо заголовков, API сайта eBay требует XML-документ с параметрами запроса, а в ответ возвращает XML-документ, который можно разобрать с помощью функции parseString из библиотеки minidom. Функция отправки запроса sendrequest открывает соединение с сервером, посылает XML-документ с параметрами и разбирает результат. Добавьте ее в файл ebaypredict.py: def sendRequest(apicall,xmlparameters):

connection = httplib.HTTPSConnection(serverUrl) connection.request("POST", ‘/ws/api.dll’, xmlparameters,

getHeaders(apicall)) response = connection.getresponse( ) if response.status != 200:

print "Ошибка при отправке запроса:" + response.reason else:

data = response.read( ) connection.close( ) return data

Эти функции годятся для выполнения любого вызова API сайта eBay. Но для конкретных вызовов необходимо по-разному генерировать XML-запрос и интерпретировать разобранный результат. Поскольку разбор DOM – утомительное занятие, нам потребуется простой вспомогательный метод getSingleValue, который ищет указанный узел и возвращает его содержимое: def getSingleValue(node,tag):

nl=node.getElementsByTagName(tag) if len(nl)>0: tagNode=nl[0]

if tagNode.hasChildNodes( ):

return tagNode.firstChild.nodeValue return ‘-1’

Выполнение поиска

Для выполнения поиска нужно лишь задать параметры в формате XML для вызова функции API GetSearchResults и передать их определенной выше функции sendrequest. Параметры записываются в таком виде:

<GetSearchResultsRequest xmlns="urn:ebay:apis:eBLBaseComponents">

<RequesterCredentials><eBayAuthToken>tofcen</eBayAuthToken>

</RequesterCredentials>

<parameterT>vaJue</parameterT>

<paramieter2>value</paramieter2>

</GetSearchResultsRequest>

Этой функции API можно передать десятки параметров, но в данном примере мы ограничимся только двумя:

Query

Строка, содержащая поисковые слова. Параметр используется точно так же, как если бы вы набрали запрос на начальной странице сайта eBay.

CategoryID

Числовое значение, определяющее категорию, в которой вы ищете. На сайте eBay определена огромная иерархия категорий, которую можно запросить, вызвав функцию API GetCategories. Это можно сделать автономно или в сочетании с параметром Query. Функция doSearch принимает два параметра и выполняет поиск. Она возвращает список идентификаторов товаров (они нам понадобятся для вызова функции GetItem) вместе с их описаниями и текущими ценами. Добавьте эту функцию в файл ebaypredict.py: def doSearch(query,categoryID=None,page=1): xml = "<?xml version=’1.0′ encoding=’utf-8′?>"+\

"<GetSearchResultsRequest xmlns=\"urn:ebay:apis:eBLBaseComponents\">"+\ "<RequesterCredentials><eBayAuthToken>" +\ userToken +\

"</eBayAuthToken></RequesterCredentials>" + \ "<Pagination>"+\ "<EntriesPerPage>200</EntriesPerPage>"+\ "<PageNumber>"+str(page)+"</PageNumber>"+\ "</Pagination>"+\ "<Query>" + query + "</Query>" if categoryID!=None:

xml+="<CategoryID>"+str(categoryID)+"</CategoryID>" xml+="</GetSearchResultsRequest>"

data=sendRequest(‘GetSearchResults’,xml) response = parseString(data)

itemNodes = response.getElementsByTagName(‘Item’); results = [] for item in itemNodes: itemId=getSingleValue(item,’ItemID’) itemTitle=getSingleValue(item,’Title’) itemPrice=getSingleValue(item,’CurrentPrice’) itemEnds=getSingleValue(item,’EndTime’) results.append((itemId,itemTitle,itemPrice,itemEnds)) return results

Чтобы воспользоваться параметром category, понадобится также функция для получения иерархии категорий. Вызов API в данном случае очень прост, но XML-документ со всеми данными категории огромен, загружается долго и разбирать его трудно. Поэтому ограничьтесь какой-нибудь категорией верхнего уровня.

Функция getCategory получает строку и идентификатор родительской категории и возвращает все ее подкатегории, в названии которых присутствует заданная строка. Если идентификатор родителя опущен, то возвращается просто список всех категорий верхнего уровня. Добавьте эту функцию в файл ebaypredict.py:

def getCategory(query=”,parentID=None,siteID=’0′): lquery=query.lower( )

xml = "<?xml version=’1.0′ encoding=’utf-8′?>"+\

"<GetCategoriesRequest xmlns=\"urn:ebay:apis:eBLBaseComponents\">"+\ "<RequesterCredentials><eBayAuthToken>" +\ userToken +\

"</eBayAuthToken></RequesterCredentials>"+\ "<DetailLevel>ReturnAll</DetailLevel>"+\ "<ViewAllNodes>true</ViewAllNodes>"+\ "<CategorySiteID>"+siteID+"</CategorySiteID>" if parentID==None:

xml+="<LevelLimit>1</LevelLimit>" else:

xml+="<CategoryParent>"+str(parentID)+"</CategoryParent>" xml += "</GetCategoriesRequest>" data=sendRequest(‘GetCategories’,xml) categoryList=parseString(data)

catNodes=categoryList.getElementsByTagName(‘Category’) for node in catNodes: catid=getSingleValue(node,’CategoryID’) name=getSingleValue(node,’CategoryName’) if name.lower( ).find(lquery)!=-1: print catid,name

Протестируйте ее в интерактивном сеансе: >>> import ebaypredict >>> laptops=ebaypredict.doSearch(‘laptop’) >>> laptops[0:10]

[(u’110075464522′, u’Apple iBook G3 12" 500MHZ Laptop , 30 GB HD ‘, u’299.99′, u’2007-01-11T03:16:14.000Z’), (u’150078866214′, u’512MB PC2700 DDR Memory 333MHz 200-Pin Laptop SODIMM’, u’49.99′,u’2007-01-11T03:16:27.000Z’), (u’120067807006′, u’LAPTOP USB / PS2 OPTICAL MOUSE 800 DPI SHIP FROM USA’, u ‘4.99’, u’2007-01-11T03:17:00.000Z’),

Похоже, что поиск по слову laptop возвращает и все аксессуары, так или иначе относящиеся к лэптопам (портативным компьютерам). К счастью, можно поискать в категории «Laptops, Notebooks» (Лэптопы, Ноутбуки), чтобы ограничиться лишь собственно лэптопами. Сначала следует запросить список категорий верхнего уровня, найти в категории «Computers and Networking» (Компьютеры и Построение сети) подкатегорию «Laptops, Notebooks» (Лэптопы, Ноутбуки), получить ее идентификатор и затем искать по слову laptop в нужной категории. >>> ebaypredict.getCategory(‘computers’) 58058 Computers & Networking

>>> ebaypredict.getCategory(‘laptops’,parentID=58058)

25447 Apple Laptops, Notebooks

31533 Drives for Laptops 51148 Laptops, Notebooks…

>>> laptops=ebaypredict.doSearch(‘laptop’,categoryID=51148) >>> laptops[0:10]

[(u’150078867562′, u’PANASONIC TOUGHBOOK Back-Lit KeyBoard 4 CF-27 CF-28′, u’49.95′, u’2007-01-11T03:19:49.000Z’),

(u’270075898309′, u’mini small PANASONIC CFM33 CF M33 THOUGHBOOK ! libretto’, u’171.0′, u’2007-01-11T03:19:59.000Z’),

(u’170067141814′, u’Sony VAIO "PCG-GT1" Picturebook Tablet Laptop MINT ‘, u’760.0′, u’2007-01-11T03:20:06.000Z’),…

Во время работы над этой книгой у категории «Laptops, Notebooks» (Лэптопы, Ноутбуки) был идентификатор 51148. Как видите, ограничив поиск этой категорией, мы смогли устранить многие не относящиеся к делу результаты, которые возвращает поиск по одному лишь слову laptop. В результате получился набор данных, гораздо более пригодный для построения ценовой модели.

Получение подробной информации о товаре

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

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

getItem:

def getItem(itemID): xml = "<?xml version=’1.0′ encoding=’utf-8′?>"+\

"<GetItemRequest xmlns=\"urn:ebay:apis:eBLBaseComponents\">"+\ "<RequesterCredentials><eBayAuthToken>" +\ userToken +\

"</eBayAuthToken></RequesterCredentials>" + \ "<ItemID>" + str(itemID) + "</ItemID>"+\ "<DetailLevel>ItemReturnAttributes</DetailLevel>"+\ "</GetItemRequest>" data=sendRequest(‘GetItem’,xml) result={}

response=parseString(data)

result[‘title’]=getSingleValue(response,’Title’)

sellingStatusNode = response.getElementsByTagName(‘SellingStatus’)[0]; result[‘price’]=getSingleValue(sellingStatusNode,’CurrentPrice’) result[‘bids’]=getSingleValue(sellingStatusNode,’BidCount’) seller = response.getElementsByTagName(‘Seller’) result[‘feedback’] = getSingleValue(seller[0],’FeedbackScore’) attributeSet=response.getElementsByTagName(‘Attribute’); attributes={} for att in attributeSet: attID=att.attributes.getNamedItem(‘attributeID’).nodeValue attValue=getSingleValue(att,’ValueLiteral’) attributes[attID]=attValue result[‘attributes’]=attributes return result

Эта функция получает с помощью sendrequest XML-документ, содержащий описание товара, а затем извлекает из него интересующие нас данные. Поскольку набор атрибутов для каждого товара свой, то все они возвращаются в виде словаря. Протестируйте функцию на результатах предыдущего поиска: >>> reload(ebaypredict) >>> ebaypredict.getItem(laptops[7][0])

{‘attributes’: {u’13’: u’Windows XP’, u’12’: u’512′, u’14’: u’Compaq’, u’3805′: u’Exchange’, u’3804′: u’14 Days’,

u’41’: u’-‘, u’26445′: u’DVD+/-RW’, u’25710′: u’80.0′, u’26443′: u’AMD Turion 64′, u’26444′: u’1800′, u’26446′: u’15’, u’10244′: u’-‘}, ‘price’: u’515.0′, ‘bids’: u’28’, ‘feedback’: u’2797′, ‘title’: u’COMPAQ V5210US 15.4" AMD Turion 64 80GB Laptop Notebook’}

По-видимому, атрибут 26444 представляет тактовую частоту процессора, 26446 – размер экрана, 12 – объем оперативной памяти, а 25710 – емкость жесткого диска. Если добавить сюда еще рейтинг продавца, количество заявок и начальную цену, то мы получим интересный набор данных для прогнозирования цен.

Построение предсказателя цен

Чтобы воспользоваться разработанным в этой статье механизмом прогнозирования цен, нам потребуется получить описания товаров с сайта eBay и преобразовать их в списки чисел, которые можно передать как наборы данных функции перекрестного контроля. Для этого функция makeLaptopDataset сначала вызывает doSearch, чтобы получить список лэптопов, а затем для каждого выполняет индивидуальный запрос. Извлекая описанные в предыдущем разделе атрибуты, эта функция создает список чисел, пригодный для прогнозирования, и помещает данные в структуру, необходимую для работы алгоритма kNN. Добавьте функцию makeLaptopDataset в файл ebaypredict.py:

def makeLaptopDataset( ):

searchResults=doSearch(‘laptop’,categoryID=51148) result=[]

for r in searchResults: item=getItem(r[0]) att=item[‘attributes’] try:

data=(float(att[’12’]),float(att[‘26444’]),

float(att[‘26446’]),float(att[‘25710’]), float(item[‘feedback’])

)

entry={‘input’:data,’result’:float(item[‘price’])} result.append(entry) except:

print item[‘title’]+’ failed’ return result

Эта функция игнорирует товары, у которых нет необходимых атрибутов. Для получения и обработки результатов потребуется некоторое время, но в итоге у вас окажется интересный набор данных с реальными ценами и атрибутами. Вызовите функцию в интерактивном сеансе: >>> reload(ebaypredict)

<module ‘ebaypredict’ from ‘ebaypredict.py’> >>> set1=ebaypredict.makeLaptopDataset( )

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

>>> numpredict.knnestimate(set1,(512,1000,14,40,1000))

667.89999999999998

>>> numpredict.knnestimate(set1,(1024,1000,14,40,1000))

858.42599999999982

>>> numpredict.knnestimate(set1,(1024,1000,14,60,0))

482.02600000000001

>>> numpredict.knnestimate(set1,(1024,2000,14,60,1000))

1066.8

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

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

3 коммент. »

 
  • Мария says:

    Добрый день! Читала Вашу статью. С подобным материалом я уже знакома из книги Тоби Сегарана “Программируем коллективный разум”. Получилось ли у вас реализовать все описанные алгоритмы для eBay? Есть ли у вас исходники для ценового предсказателя применительно к eBay? Если можно пришлите мне их. Мой mail: mariya_88@mail.ru. Интересная тематика для статьи!

  • Toma says:

    Материал для статьи черпался из указной Вами книги. Пока не реализовал, к сожалению…

  • Мария says:

    Плохо! А я вот попыталась все это реализовать для eBay. У меня работает только простой knn-алгоритм, данные считываются, а вот оценить вероятности, построить графики и взвешенный алгоритм не получаются. Кстати, есть недостаток в этом коде: данные о лэптопах считываются разные, включая и те, у которых цена 0,01 usd, следовательно, если выбирать количество соседей равное 1, то прогноз будет неверным. Наверно, нужно использовать только завершенные аукционы, Bye it now и выбирать только новые компьютеры. Если попытаетесь реализовать, напишите, мне интересна эта тематика!

 

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100