Привет, читатели.
Мой публикация навеяна этой — «eBay API: Первые шаги» и теоретически может стать её продолжением. Она пригодится тем, кто пытался начать работать с eBay API, не нашел где спросить, что делать и плюнул на это занятие. Или спросил, не дождался ответа и в итоге все равно плюнул.
На истину ни в какой инстанции не претендую и с радостью приму советы по преодолению багов, которые преодолеть так и не удалось.
Прежде всего, хочется оставить небольшую ремарку: начать работать с eBay API быстро — не получится. Катастрофическое количество недосказанностей и нестыковок в документации, недоработки и баги в самом механизме API, неявности и неоднозначности использования комбинаций условий запросов заставляют тестировать каждый шаг по отдельности. И тестировать неоднократно: то, что вчера прекрасно работало, сегодня будет возвращать ошибку, а завтра продолжит работать как ни в чем не бывало.
Впрочем, дабы не возводить напраслину на уважаемую компанию, давайте забудем про предыдущий абзац и все спишем на то, что я просто туп, глуп и недоразвит. И что у настоящего программиста все получится гораздо быстрее и правильнее.
Поэтому если у вас есть рабочий механизм парсинга страниц Ebay — подумайте еще раз, стоит ли переходить на цивилизованный способ получения данных или продолжить время от времени подстраивать парсер под подстоянно изменяющийся дизайн eBay. Тем более что с точки зрения просто покупателя никаких принципиально новых данных вы не получите, а вот например информацию о текущей скидке или обработку события «лот протух, перевыставляем» — потеряете.
Но если вам по долгу службы необходимо следить хотя бы за парой-тройкой тысяч товаров и периодически сверять цены — API вам бесспорно нужен. Мой небольшой проект отчаянно нуждался в автоматизации актуализации цен и добавления новых товаров, а изменения дизайна eBay в 2014 году стали столь часты, что я решил бросить курить перестать парсить, написать зачатки библиотеки по работе с eBay API и заодно начать на старости лет осваивать программирование.
Так как движок проекта написан на PHP, выбора у меня не было, а посему на нем и продолжим.
Начем с функции findItemsAdvanced, что в переводе означает «Найти много товаров по ключевому слову, с совершенно бесполезным результатом для жителей не-США». Затем рассмотрим (наверное уже в следующей статье) другие функции, которые превратят бесполезные ответы в полезные.
Вы уже получили Application Keys как это сказано здесь. Возьмите AppID из Production Keys — из-за того, что баги в Sandbox и Production разные, мы будем сразу работать с «боевой» версией — и подставьте его в нужное место заголовка. Как вы увидите дальше, заголовки в eBay API могут принимать самые разные формы, поэтому я буду приводить их отдельно в каждом конкретном случае.
<?php
/* ищет товары по ключевым словам ВО ВСЕХ store
// в качестве ключевых слов понимает слова, partnumber или itemID
// не понимает доставку за пределы США
*/
function findItemsAdvanced($request)
{
$headers = array(
'X-EBAY-SOA-SERVICE-NAME:FindingService',
'X-EBAY-SOA-OPERATION-NAME:findItemsAdvanced',
'X-EBAY-SOA-SERVICE-VERSION:1.12.0',
'X-EBAY-SOA-GLOBAL-ID:EBAY-US',
'X-EBAY-SOA-SECURITY-APPNAME:Ваш_AppID', // AppID будет жить здесь
"X-EBAY-API-REQUEST-ENCODING: XML",
'Content-Type: text/xml;charset=utf-8',
);
$endpoint = 'http://svcs.ebay.com/services/search/FindingService/v1?';
Остальные значения заголовков можно просто скопировать, они для одинаковы для всех.
Составим запрос.
Все мои запросы относятся к категории «Запчасти для мотоциклов» и ориентированы на тематчиеский раздел eBay USA — «EBAY-MOTORS». Найдите список подходящих вам разделов здесь, список категорий здесь и не забывайте подставлять их в свои запросы.
$xmlRequest = "<?xml version="1.0" encoding="utf-8"?>";
$xmlRequest .= "<findItemsAdvancedRequest xmlns='http://www.ebay.com/marketplace/search/v1/services'>";
$xmlRequest .= "<categoryId>10063</categoryId>"; // 10063 - запчасти для мотоциклов // 6028 - запчасти к транспорту вообще
$xmlRequest .= "<descriptionSearch>false</descriptionSearch>"; // иногда может найти полную ерунду, отключено
$xmlRequest .= "<keywords>".$request."</keywords>"; // самое главное: текст вашего запроса
$xmlRequest .= "<itemFilter><name>Condition</name><value>New</value></itemFilter>"; // состояние товара: новый, б/у и их варианты
$xmlRequest .= "<itemFilter><name>FeedbackScoreMin</name><value>3000</value></itemFilter>"; // отсекаем новичков
$xmlRequest .= "<itemFilter><name>ListingType</name><value>FixedPrice</value></itemFilter>"; // отсекаем аукционные торги
$xmlRequest .= "<itemFilter><name>AvailableTo</name><value>RU</value></itemFilter>";
// эта фича частенько не работает и пропускает товары, которые продавец на самом деле не отправляет в Россию. Сейчас таких продавцов стало намного больше, поэтому мы отстроимся от них в другом запросе еще раз. Чтобы гарантированно не попасть.
$xmlRequest .= "<itemFilter><name>PaymentMethod</name><value>PayPal</value></itemFilter>";
// в некоторых странах существуют платежные системы популярнее Paypal. Для нас они недоступны либо труднодоступны, поэтому лучше сразу отсечь продавцов-смельчаков, которые не принимают Paypal. Пусть eBay разбирается с ними сам.
$xmlRequest .= "<itemFilter><name>HideDuplicateItems</name><value>true</value></itemFilter>";
// нам же не нужны дубликаты в выдаче? Уберем их.
//$xmlRequest .= "<itemFilter><name>ExcludeSeller</name><value>storename</value></itemFilter>"; // на всякий случай: можно исключить вредных и нехороших продавцов из результатов выдачи. Даже если они прикидываются белыми и пушистыми.
$xmlRequest .= "<outputSelector>SellerInfo</outputSelector> // посмотрим что за продавцы попали в наши сети
Здесь хотелось бы подробнее остановиться на параметре descriptionSearch.
Description — это область описания лота в самом низу — под фотографиями, расчетом доставки итд. Теоретически продавец может написать там любую ерунду, которая попадет в наш результат и не будет соответствовать нашему запросу.
Например вы ищете слона, а некий продавец продает кота: в заголовке — кот, на фотографии — кот, но в описании сказано что-то вроде «Это огромный кот, настоящий слон!». Этот товар попадет в нашу выдачу, хотя на самом деле не будет содержать никакого слона. И цена за него будет соответствующая: как за кота.
Завершим подготовку запроса:
$session = curl_init($endpoint);
curl_setopt($session, CURLOPT_POST, true);
curl_setopt($session, CURLOPT_POSTFIELDS, $xmlRequest);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
$responseXML = curl_exec($session);
curl_close($session);
?>
Настало время отправить наш запрос…
<?php
header('X-Accel-Buffering: no');
ob_get_flush();
$responseXML = Ebay_shopping::findItemsAdvanced('cobra exhaust c90t');
// "cobra exhaust c90t" — это значит "глушитель марки Cobra для мотоцикла Suzuku Boulevard C90T". Если вы вставите в запрос "iPhone", то в этой категории вы не найдете ни одного айфона: вам будет нужно указать <categoryId>9355</categoryId>.
$responseXML = simplexml_load_string($responseXML);
print_r($responseXML);
?>
… и получить ответ:
SimpleXMLElement Object
(
[ack] => Success
[version] => 1.13.0
[timestamp] => 2014-11-24T18:01:37.836Z
[searchResult] => SimpleXMLElement Object
(
[@attributes] => Array
(
[count] => 11 // всего найдено 11 лотов, мы разберем один
)
[item] => Array
(
[0] => SimpleXMLElement Object
(
[itemId] => 360763537259
// номер лота, уникальный идентификатор товара. Его можно вставить в строку поиска на сайте eBay или передать другу вместо огромной ссылки, которую генерит eBay.
[title] => Cobra Speedster Exhaust with Powerport Long 2005 Suzuki C90T Boulevard
// название товара. Именно здесь ищется ваш $request.
[globalId] => EBAY-MOTOR // раздел eBay, к которому относится этот лот
[primaryCategory] => SimpleXMLElement Object
(
[categoryId] => 178001
[categoryName] => Other Exhaust Parts
)
[galleryURL] => http://thumbs4.ebaystatic.com/m/mbTIInAkeUhM9YV7mV7GtcQ/140.jpg
// маленькая и довольно бесполезная фотка товара. Мы научимся получать большие фотки позднее.
[viewItemURL] => http://www.ebay.com/itm/Cobra-Speedster-Exhaust-Powerport-Long-2005-Suzuki-C90T-Boulevard-/360763537259?pt=Motorcycles_Parts_Accessories
// по этому урлу будет доступен этот лот на сайте ebay
[paymentMethod] => Array
(
[0] => PayPal
[1] => VisaMC
[2] => AmEx
[3] => Discover
)
[autoPay] => false
[postalCode] => 46528
[location] => Goshen,IN,USA
[country] => US
[sellerInfo] => SimpleXMLElement Object
(
[sellerUserName] => motored_e
[feedbackScore] => 11025 // сюда попали только те, у кого FeedbackScoreMin больше 3000
[positiveFeedbackPercent] => 99.1 // ...а вот недобросовестных продавцов мы отфильтруем чуть позже. Здесь эта фича не работает
[feedbackRatingStar] => YellowShooting
[topRatedSeller] => false
)
[shippingInfo] => SimpleXMLElement Object
(
[shippingServiceCost] => 0.0
[shippingType] => FlatDomesticCalculatedInternational
[shipToLocations] => Worldwide
[expeditedShipping] => true
[oneDayShippingAvailable] => false
[handlingTime] => 1
)
[sellingStatus] => SimpleXMLElement Object
(
[currentPrice] => 521.51
[convertedCurrentPrice] => 521.51
[sellingState] => Active
[timeLeft] => P11DT9H1M57S
)
[listingInfo] => SimpleXMLElement Object
(
[bestOfferEnabled] => false
[buyItNowAvailable] => false
[startTime] => 2013-10-12T02:58:34.000Z
[endTime] => 2014-12-06T03:03:34.000Z
[listingType] => FixedPrice
[gift] => false
)
[returnsAccepted] => true
[condition] => SimpleXMLElement Object
(
[conditionId] => 1000 // хотелось бы в запросе указывать "1000" вместо "New"... но увы, не работает
[conditionDisplayName] => New
)
[isMultiVariationListing] => false
[topRatedListing] => false
)
)
)
[paginationOutput] => SimpleXMLElement Object
(
[pageNumber] => 1
[entriesPerPage] => 100
[totalPages] => 1
[totalEntries] => 11
)
[itemSearchURL] => http://www.ebay.com/sch/10063/i.html?_sasl=riderswarehouse&payment=PayPal&_fss=1&LH_SpecificSeller=1&LH_PayPal=1&_nkw=cobra+exhaust+c90t&LH_BIN=1&LH_ItemCondition=1&_saslop=2&LH_AvailTo=1&_fblo=3000&_fbsc=1&_fls=1&_incaucbin=0&_ipg=100&_os=S%7CD&_pgn=1&_saact=168&_sop=15
// этот урл будет в строке браузера, если мы будем искать наш $request на сайте ebay
)
Хотелось бы остановиться на некоторых моментах ответа.
[shipToLocations] => Worldwide
Эта стока на самом деле ни о чем не говорит. Если продавец вручную ввел вашу страну в черный список, и ни за какие деньги не собирается отправлять туда свои товары, то в этом месте ответа точно так же будет красоваться Worldwide.
[convertedCurrentPrice] => 521.51
EBay API может выдавать нам цены в той валюте, в которой их выставил продавец. На практике правильнее привести все цены в доллары, и этим занимается как раз эта строка.
[timeLeft] => P11DT9H1M57S
По истечению этого времени лот протухнет и будет перевыставлен (Item Relisted). Через некоторое врмя мы больше не сможем найти его по itemID.
[entriesPerPage] => 100
[totalPages] => 1
В eBay API существует глобальное ограничение в 100 лотов на страницу. Если по вашему запросу нашлось больше, вам придется делать несколько запросов и сшивать ответы воедино. В eBay API нет такой функции и мы напишем ее позже.
С полученным ответом вы уже можете что-нибудь сделать и наверняка у вас это получится лучше. Если эта статья будет интересна хаброчитателям, в следующей мы разберем, как его обрабатываю я и что из этого получается.
Также мы рассмотрим возможный алгоритм комбинирования нескольких функций eBay API «из жизни», скормим результат работы одной функции на вход другой, применим несколько фильтров и получим на выходе результат, достойный записи в вашу базу.
Удачи.
Автор: gluck59