Привет, хабравчане.
Публикация навеяна этой статьей — «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 могут принимать самые разные формы, поэтому я буду приводить их отдельно в каждом конкретном случае.
Остальные значения заголовков можно просто скопировать, они для одинаковы для всех.
Составим запрос.
Все мои запросы относятся к категории «Запчасти для мотоциклов» и ориентированы на тематчиеский раздел eBay USA — «EBAY-MOTORS». Найдите список подходящих вам разделов здесь, список категорий здесь и не забывайте подставлять их в свои запросы.
Здесь хотелось бы подробнее остановиться на параметре descriptionSearch.
Description — это область описания лота в самом низу — под фотографиями, расчетом доставки итд. Теоретически продавец может написать там любую ерунду, которая попадет в наш результат и не будет соответствовать нашему запросу.
Например вы ищете слона, а некий продавец продает кота: в заголовке — кот, на фотографии — кот, но в описании сказано что-то вроде «Это огромный кот, настоящий слон!». Этот товар попадет в нашу выдачу, хотя на самом деле не будет содержать никакого слона. И цена за него будет соответствующая: как за кота.
Завершим подготовку запроса:
Настало время отправить наш запрос…
… и получить ответ:
Хотелось бы остановиться на некоторых моментах ответа.
[shipToLocations] => Worldwide
Эта стока на самом деле ни о чем не говорит. Если продавец вручную ввел вашу страну в черный список, и ни за какие деньги не собирается отправлять туда свои товары, то в этом месте ответа точно так же будет красоваться Worldwide.
[convertedCurrentPrice] => 521.51
EBay API может выдавать нам цены в той валюте, в которой их выставил продавец. На практике правильнее привести все цены в доллары, и этим занимается как раз эта строка.
[timeLeft] => P11DT9H1M57S
По истечению этого времени лот протухнет и будет перевыставлен (Item Relisted). Через некоторое врмя мы больше не сможем найти его по itemID.
[entriesPerPage] => 100
[totalPages] => 1
В eBay API существует глобальное ограничение в 100 лотов на страницу. Если по вашему запросу нашлось больше, вам придется делать несколько запросов и сшивать ответы воедино. В eBay API нет такой функции и мы напишем ее позже.
С полученным ответом вы уже можете что-нибудь сделать и наверняка у вас это получится лучше. Если эта статья будет интересна хаброчитателям, в следующей мы разберем, как его обрабатываю я и что из этого получается.
Также мы рассмотрим возможный алгоритм комбинирования нескольких функций eBay API «из жизни», скормим результат работы одной функции на вход другой, применим несколько фильтров и получим на выходе результат, достойный записи в вашу базу.
Удачи.
Публикация навеяна этой статьей — «eBay API: Первые шаги» и теоретически может стать её продолжением. Она пригодится тем, кто пытался начать работать с eBay API, не нашел где спросить, что делать и плюнул на это занятие. Или спросил, не дождался ответа и в итоге все равно плюнул.
На истину ни в какой инстанции не претендую и с радостью приму советы по преодолению багов, которые преодолеть так и не удалось.

Прежде всего, хочется оставить небольшую ремарку: начать работать с eBay API быстро — не получится. Катастрофическое количество недосказанностей и нестыковок в документации, недоработки и баги в самом механизме API, неявности и неоднозначности использования комбинаций условий запросов заставляют тестировать каждый шаг по отдельности. И тестировать неоднократно: то, что вчера прекрасно работало, сегодня будет возвращать ошибку, а завтра продолжит работать как ни в чем не бывало.
Впрочем, дабы не возводить напраслину на уважаемую компанию, давайте забудем про предыдущий абзац и все спишем на то, что я просто туп, глуп и недоразвит. И что у настоящего программиста все получится гораздо быстрее и правильнее.
Поэтому если у вас есть рабочий механизм парсинга страниц Ebay — подумайте еще раз, стоит ли переходить на цивилизованный способ получения данных или продолжить время от времени подстраивать парсер под подстоянно изменяющийся дизайн eBay. Тем более что с точки зрения просто покупателя никаких принципиально новых данных вы не получите, а вот например информацию о текущей скидке или обработку события «лот протух, перевыставляем» — потеряете.
Но если вам по долгу службы необходимо следить хотя бы за парой-тройкой тысяч товаров и периодически сверять цены — API вам бесспорно нужен. Мой небольшой проект отчаянно нуждался в автоматизации актуализации цен и добавления новых товаров, а изменения дизайна eBay в 2014 году стали столь часты, что я решил
Так как движок проекта написан на 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 «из жизни», скормим результат работы одной функции на вход другой, применим несколько фильтров и получим на выходе результат, достойный записи в вашу базу.
Удачи.
