В этой публикации хотел бы поделиться опытом подбора контент-провайдера при поиске изображений и описать базовый интерфейс взаимодействия с одним из них на примере сервиса Flickr.
Так получилось, что при реализации одного из моих проектов понадобился поиск изображений по определенным ключевым словам. Поиск должен был быть реализован исходя из двух простых требований:
Я долго серфил интернеты в поисках подходящего контент-провайдера, предоставляющего JSON API. В итоге были отобраны 3 основных кандидата: Google Search, Flickr и Bing Image Search. Был еще один вариант подключения к старым бесплатным API от Google, которые уже лет 5 как в статусе deprecated, но все еще работают. Но этот вариант, увы, не подходил.
К сожалению, официальные Google Search API являются, во-первых, платными, во-вторых, в результате выдачи Google по некоторым запросам вылетают неприемлемые изображения. C Bing Image Search ситуация еще более плачевная, хотя доступ к API у них обходится и дешевле.
Чтобы не быть голословным, приведу сравнение выдачи всех трех провайдеров по провокационным запросам с применением максимальной фильтрации выдачи неприемлемого контента, да простят меня автолюбители.
У Google Search, на первый взгляд, все более или менее приемлемо, но если прокрутить страницу чуть ниже, местами встречаются очень печальные картинки. У Bing Image Search все еще гораздо более хардкорно. По понятным причинам скрины этого я приводить здесь не буду. Я проводил еще множество подобных сравнений, в том числе и по пикантным запросам, и во всех них однозначным лидером оказывался Flickr. Поэтому мой выбор пал именно на него. К тому же доступ к их API является бесплатным. Единственным жирным минусом были совсем уж скудные результаты поиска на русском языке. Забегая вперед, скажу, что решено это было на бекэнде – был поднят простенький сервис, переводящий русский текст на английский. Таким образом запрос к Flickr состоял из двух частей: запрос перевода у нашего сервера, отправка полученного результата на Flickr.
Прежде всего нам необходимо получить API key работы с Flickr. Для этого перейдем по ссылке (только для некоммерческого использования)
После заполнения формы вам будут предоставлены Key и Secret:
![](https://habrastorage.org/r/w1560/files/ecc/2b2/67d/ecc2b267dec54aaeabcfe25cb88fed90.png)
Для начала определимся, каким образом вообще выглядит запрос к API:
Нам будут интересны два метода: flickr.photos.search и flickr.interestingness.getList.
Метод запроса flickr.interestingness.getList возвращает нам список самых популярных изображений. Изображения не застаиваются и периодически обновляются (по моим наблюдениям раз в пол часа — час).
Метод запроса flickr.photos.search будет использоваться непосредственно для поиска по ключевым словам.
Более подробно про каждый из параметров запроса вы можете почитать у них в официальной документации.
Для реализации общения с сервисом был реализован следующий класс:
Как видно из кода выше, класс унаследован от класса Model, в котором в свою очередь реализованы примитивные методы общения с сервером (GET, POST, PUT, DELETE запросы). Метод fetch реализует http-метод GET и принимает в качестве первого аргумента параметры запроса, в качестве второго – различные флаги обработки данных и т.д., в качестве третьего – callback интерфейс.
Метод deserialize, как понятно из названия, десериализует полученные данные. Так как Flickr возвращает JSON, обернутый вот в такую бяку: jsonFlickrApi(), то перед десериализацией JSON-объекта регуляркой отбрасываем все лишнее. По остальному, я думаю, тут все понятно.
Теперь реализуем метод, в который непосредственно будет передаваться строка запроса.
Тут, если query равен null, то делаем запрос методом flickr.interestingness.getList, иначе — flickr.photos.search c текстом query.
И, напоследок, результат работы.
Так получилось, что при реализации одного из моих проектов понадобился поиск изображений по определенным ключевым словам. Поиск должен был быть реализован исходя из двух простых требований:
- быть бесплатным;
- быть хорошо фильтрованным, чтобы не нарваться на проблемы с Apple (такая же задача стояла и для версии приложения под iOS).
Я долго серфил интернеты в поисках подходящего контент-провайдера, предоставляющего JSON API. В итоге были отобраны 3 основных кандидата: Google Search, Flickr и Bing Image Search. Был еще один вариант подключения к старым бесплатным API от Google, которые уже лет 5 как в статусе deprecated, но все еще работают. Но этот вариант, увы, не подходил.
К сожалению, официальные Google Search API являются, во-первых, платными, во-вторых, в результате выдачи Google по некоторым запросам вылетают неприемлемые изображения. C Bing Image Search ситуация еще более плачевная, хотя доступ к API у них обходится и дешевле.
Чтобы не быть голословным, приведу сравнение выдачи всех трех провайдеров по провокационным запросам с применением максимальной фильтрации выдачи неприемлемого контента, да простят меня автолюбители.
Выдача Google![](https://habrastorage.org/r/w1560/files/cd5/79d/2c5/cd579d2c59d045f2b0b23075fb5bd8b2.png)
![](https://habrastorage.org/files/cd5/79d/2c5/cd579d2c59d045f2b0b23075fb5bd8b2.png)
Выдача Bing![](https://habrastorage.org/r/w1560/files/bcb/719/769/bcb7197693fd4348ab79c5f20bea5db4.png)
![](https://habrastorage.org/files/bcb/719/769/bcb7197693fd4348ab79c5f20bea5db4.png)
Выдача Flickr![](https://habrastorage.org/r/w1560/files/1e5/d02/e1d/1e5d02e1d8ef4e3f8c5bb5bbf67894f3.png)
![](https://habrastorage.org/files/1e5/d02/e1d/1e5d02e1d8ef4e3f8c5bb5bbf67894f3.png)
У Google Search, на первый взгляд, все более или менее приемлемо, но если прокрутить страницу чуть ниже, местами встречаются очень печальные картинки. У Bing Image Search все еще гораздо более хардкорно. По понятным причинам скрины этого я приводить здесь не буду. Я проводил еще множество подобных сравнений, в том числе и по пикантным запросам, и во всех них однозначным лидером оказывался Flickr. Поэтому мой выбор пал именно на него. К тому же доступ к их API является бесплатным. Единственным жирным минусом были совсем уж скудные результаты поиска на русском языке. Забегая вперед, скажу, что решено это было на бекэнде – был поднят простенький сервис, переводящий русский текст на английский. Таким образом запрос к Flickr состоял из двух частей: запрос перевода у нашего сервера, отправка полученного результата на Flickr.
Регистрация приложения
Прежде всего нам необходимо получить API key работы с Flickr. Для этого перейдем по ссылке (только для некоммерческого использования)
После заполнения формы вам будут предоставлены Key и Secret:
![](https://habrastorage.org/files/ecc/2b2/67d/ecc2b267dec54aaeabcfe25cb88fed90.png)
Интерфейс запросов к Flickr
Для начала определимся, каким образом вообще выглядит запрос к API:
https://api.flickr.com/services/rest/?safe_search=safe&api_key=XXX&sort=relevance&method=flickr.interestingness.getList&per_page=50&media=photos&extras=url_sq,url_t,url_s,url_q,url_m,url_n,url_z,url_c,url_l,url_o&license=1,2,3,4,5,6&format=json
Нам будут интересны два метода: flickr.photos.search и flickr.interestingness.getList.
Метод запроса flickr.interestingness.getList возвращает нам список самых популярных изображений. Изображения не застаиваются и периодически обновляются (по моим наблюдениям раз в пол часа — час).
Метод запроса flickr.photos.search будет использоваться непосредственно для поиска по ключевым словам.
Более подробно про каждый из параметров запроса вы можете почитать у них в официальной документации.
Пример реализации
Для реализации общения с сервисом был реализован следующий класс:
public class Flickr extends Model {
public interface FlickrModelResponseHandler {
public void onSuccess(ArrayList<HashMap<String, String>> responseArray);
public void onFailure(String error);
}
public Flickr(Context context) {
super(context);
setUrl("/services/rest/");
}
public void fetch (Map<String, String> inData, Map<String, Object> options, final FlickrModelResponseHandler handler) {
HashMap<String, String> fetchedParams = new HashMap(inData);
fetchedParams.put("api_key", "XXX");
fetchedParams.put("extras", "url_sq,url_t,url_s,url_q,url_m,url_n,url_z,url_c,url_l,url_o");
fetchedParams.put("format", "json");
fetchedParams.put("per_page", "50");
fetchedParams.put("safe_search", "safe");
fetchedParams.put("content_type", "1");
fetchedParams.put("media", "photos");
fetchedParams.put("sort", "relevance");
fetchedParams.put("license", "1,2,3,4,5,6");
super.fetch(fetchedParams, new HashMap<String, Object>() {{
put("host", "https://api.flickr.com");
}}, new ModelResponseHandler() {
@Override
public void onSuccess(Map<String, Object> responseDict) {
ArrayList<HashMap<String, String>> photos = ((HashMap<String, ArrayList<HashMap<String, String>>>)(responseDict.get("photos"))).get("photo");
if (handler != null)
handler.onSuccess(photos);
}
@Override
public void onFailure(String error) {
if (handler != null)
handler.onFailure(error);
}
});
}
@Override
protected JSONObject deserialize(String responseString) {
Pattern p = Pattern.compile(".*?\\((.*)\\)$");
Matcher m = p.matcher(responseString);
String json = null;
if (m.matches()) {
json = m.group(1);
}
JSONObject response = null;
try {
response = new JSONObject(json);
} catch (JSONException e) {
e.printStackTrace();
}
return response;
}
}
Как видно из кода выше, класс унаследован от класса Model, в котором в свою очередь реализованы примитивные методы общения с сервером (GET, POST, PUT, DELETE запросы). Метод fetch реализует http-метод GET и принимает в качестве первого аргумента параметры запроса, в качестве второго – различные флаги обработки данных и т.д., в качестве третьего – callback интерфейс.
Метод deserialize, как понятно из названия, десериализует полученные данные. Так как Flickr возвращает JSON, обернутый вот в такую бяку: jsonFlickrApi(), то перед десериализацией JSON-объекта регуляркой отбрасываем все лишнее. По остальному, я думаю, тут все понятно.
Теперь реализуем метод, в который непосредственно будет передаваться строка запроса.
private void searchImages(String query) {
HashMap<String, String> params = new HashMap<>();
flickr = new Flickr(ImageSearchActivity.this);
if (query != null) {
params.put("method", "flickr.photos.search");
params.put("text", query);
} else {
params.put("method", "flickr.interestingness.getList");
}
flickr.fetch(params, null, new Flickr.FlickrModelResponseHandler() {
@Override
public void onSuccess(ArrayList<HashMap<String, String>> responseArray) {
adapter.setArray(responseArray);
}
@Override
public void onFailure(String error) {
adapter.setArray(new ArrayList<HashMap<String, String>>());
}
});
}
Тут, если query равен null, то делаем запрос методом flickr.interestingness.getList, иначе — flickr.photos.search c текстом query.
И, напоследок, результат работы.
Осторожно, 10-ти метровая gif-ка![](https://habrastorage.org/files/c11/fba/0cb/c11fba0cb96d4d4fa9ec22b2b538ec63.gif)
![](https://habrastorage.org/files/c11/fba/0cb/c11fba0cb96d4d4fa9ec22b2b538ec63.gif)