Öğretici: Bing Resim Arama API’sini kullanarak tek sayfalı uygulama oluşturma
Uyarı
30 Ekim 2020'de Bing Arama API'leri Azure yapay zeka hizmetlerinden Bing Arama Hizmetlerine taşındı. Bu belgeler yalnızca başvuru için sağlanır. Güncelleştirilmiş belgeler için Bing arama API'sinin belgelerine bakın. Bing araması için yeni Azure kaynakları oluşturma yönergeleri için bkz. Azure Market aracılığıyla Bing Arama kaynağı oluşturma.
Bing Resim Arama API’si, web’de yüksek kaliteli, alakalı görüntüleri aramanıza olanak sağlar. API’ye arama sorguları gönderebilen ve sonuçları web sayfasında görüntüleyebilen tek sayfalı bir web uygulaması derlemek için bu öğreticiyi kullanın. Bu öğretici, Bing Web Araması’nın ilgili öğreticisine benzerdir.
Öğretici uygulaması şunları gösterir:
- JavaScript’te Bing Resim Arama API’si çağrısı gerçekleştirme
- Arama seçeneklerini kullanarak arama sonuçlarını iyileştirme
- Arama sonuçlarını görüntüleme ve arama sonuçları sayfalarında gezinme
- API abonelik anahtarı ve Bing istemci kimliği isteme ve işleme.
Önkoşullar
- Node.js'in en son sürümü.
- Node.js için Express.js çerçevesi. Kaynak kodun yükleme yönergeleri GitHub örnek benioku dosyasında bulunabilir.
Kullanıcı abonelik anahtarlarını yönetme ve depolama
Bu uygulama, API abonelik anahtarlarını depolamak için web tarayıcılarının kalıcı depolama alanını kullanır. Herhangi bir anahtar depolanmıyorsa web sayfası, kullanıcıdan anahtarını belirtmesini ve daha sonra kullanmak üzere anahtarını depolamasını ister. Anahtar daha sonra API tarafından reddedilirse uygulama bunu depolama alanından kaldırır. Bu örnek genel uç noktayı kullanır. Kaynağınız için Azure portal görüntülenen özel alt etki alanı uç noktasını da kullanabilirsiniz.
localStorage
nesnesini (tarayıcı destekliyorsa) veya bir tanımlama bilgisini kullanmak için storeValue
ve retrieveValue
işlevlerini tanımlayın.
// Cookie names for data being stored
API_KEY_COOKIE = "bing-search-api-key";
CLIENT_ID_COOKIE = "bing-search-client-id";
// The Bing Image Search API endpoint
BING_ENDPOINT = "https://api.cognitive.microsoft.com/bing/v7.0/images/search";
try { //Try to use localStorage first
localStorage.getItem;
window.retrieveValue = function (name) {
return localStorage.getItem(name) || "";
}
window.storeValue = function(name, value) {
localStorage.setItem(name, value);
}
} catch (e) {
//If the browser doesn't support localStorage, try a cookie
window.retrieveValue = function (name) {
var cookies = document.cookie.split(";");
for (var i = 0; i < cookies.length; i++) {
var keyvalue = cookies[i].split("=");
if (keyvalue[0].trim() === name) return keyvalue[1];
}
return "";
}
window.storeValue = function (name, value) {
var expiry = new Date();
expiry.setFullYear(expiry.getFullYear() + 1);
document.cookie = name + "=" + value.trim() + "; expires=" + expiry.toUTCString();
}
}
getSubscriptionKey()
işlevi, retrieveValue
öğesini kullanarak önceden depolanan bir anahtarı almaya çalışır. Biri bulunamazsa, kullanıcıdan anahtarını belirtmesini ve storeValue
öğesini kullanarak depolamasını ister.
// Get the stored API subscription key, or prompt if it's not found
function getSubscriptionKey() {
var key = retrieveValue(API_KEY_COOKIE);
while (key.length !== 32) {
key = prompt("Enter Bing Search API subscription key:", "").trim();
}
// always set the cookie in order to update the expiration date
storeValue(API_KEY_COOKIE, key);
return key;
}
HTML <form>
etiketi onsubmit
arama sonuçlarını döndürmek için bingWebSearch
işlevini çağırır.
bingWebSearch
, her sorgunun kimliğini doğrulamak için getSubscriptionKey
kullanır. Önceki tanımda gösterildiği gibi, anahtar girilmediyse getSubscriptionKey
kullanıcıdan anahtarı ister. Ardından anahtar uygulama tarafından sürekli kullanılmak üzere depolanır.
<form name="bing" onsubmit="this.offset.value = 0; return bingWebSearch(this.query.value,
bingSearchOptions(this), getSubscriptionKey())">
Arama istekleri gönderme
Bu uygulama, newBingImageSearch()
çağrısı yapmak için onsubmit
özniteliğini kullanarak başlangıçta kullanıcı arama istekleri göndermek için HTML <form>
öğesini kullanır.
<form name="bing" onsubmit="return newBingImageSearch(this)">
Varsayılan olarak onsubmit
işleyicisi, formun gönderilmesini engelleyen false
değerini döndürür.
Arama seçeneklerini belirleme
Bing Resim Arama API’si, arama sonuçlarını daraltmak ve filtrelemek için birçok filtre sorgusu parametresi sunar. Bu uygulamadaki HTML formu, aşağıdaki parametre seçeneklerini kullanır ve görüntüler:
Seçenek | Açıklama |
---|---|
where |
Aramada kullanılan pazarı (konum ve dil) seçmek için açılan menü. |
query |
Arama terimlerinin girildiği metin alanı. |
aspect |
Bulunan görüntülerin oranlarını seçmek için kullanılan radyo düğmeleri: kabaca kare, geniş veya uzun. |
color |
|
when |
Aramayı isteğe bağlı olarak en son günle, haftayla veya ayla sınırlamak için açılan menü. |
safe |
"Yetişkinlere yönelik" sonuçları filtrelemek için Bing Güvenli Arama özelliğinin kullanılıp kullanılmayacağını belirten onay kutusu. |
count |
Gizli alan. Her istekte döndürülecek arama sonuçlarının sayısı. Sayfada daha az veya daha fazla sonuç görüntülemek için bunu değiştirin. |
offset |
Gizli alan. İstekteki ilk arama sonucunun göreli konumu; sayfalama için kullanılır. Yeni istekte 0 değerine sıfırlanır. |
nextoffset |
Gizli alan. Arama sonucu alındıktan sonra bu alan, yanıttaki nextOffset değerine ayarlanır. Bu alan kullanıldığında, ardışık sayfalarda sonuçların çakışması engellenir. |
stack |
Gizli alan. Önceki sayfalara geri dönmek için, arama sonuçlarının önceki sayfalarının göreli konumlarının JSON olarak kodlanmış bir listesi. |
bingSearchOptions()
işlevi, bu seçenekleri kısmi bir sorgu dizesi olarak biçimlendirir ve bu dize de uygulamanın API isteklerinde kullanılabilir.
// Build query options from the HTML form
function bingSearchOptions(form) {
var options = [];
options.push("mkt=" + form.where.value);
options.push("SafeSearch=" + (form.safe.checked ? "strict" : "off"));
if (form.when.value.length) options.push("freshness=" + form.when.value);
var aspect = "all";
for (var i = 0; i < form.aspect.length; i++) {
if (form.aspect[i].checked) {
aspect = form.aspect[i].value;
break;
}
}
options.push("aspect=" + aspect);
if (form.color.value) options.push("color=" + form.color.value);
options.push("count=" + form.count.value);
options.push("offset=" + form.offset.value);
return options.join("&");
}
İsteği gerçekleştirme
BingImageSearch()
işlevi, arama sorgusunu, seçenekler dizesini ve API anahtarını kullanarak, Bing Resim Arama uç noktasına istekte bulunmak için bir XMLHttpRequest nesnesi kullanır.
// perform a search given query, options string, and API key
function bingImageSearch(query, options, key) {
// scroll to top of window
window.scrollTo(0, 0);
if (!query.trim().length) return false; // empty query, do nothing
showDiv("noresults", "Working. Please wait.");
hideDivs("results", "related", "_json", "_http", "paging1", "paging2", "error");
var request = new XMLHttpRequest();
var queryurl = BING_ENDPOINT + "?q=" + encodeURIComponent(query) + "&" + options;
// open the request
try {
request.open("GET", queryurl);
}
catch (e) {
renderErrorMessage("Bad request (invalid URL)\n" + queryurl);
return false;
}
// add request headers
request.setRequestHeader("Ocp-Apim-Subscription-Key", key);
request.setRequestHeader("Accept", "application/json");
var clientid = retrieveValue(CLIENT_ID_COOKIE);
if (clientid) request.setRequestHeader("X-MSEdge-ClientID", clientid);
// event handler for successful response
request.addEventListener("load", handleBingResponse);
// event handler for erorrs
request.addEventListener("error", function() {
renderErrorMessage("Error completing request");
});
// event handler for aborted request
request.addEventListener("abort", function() {
renderErrorMessage("Request aborted");
});
// send the request
request.send();
return false;
}
HTTP isteği başarıyla tamamlanınca JavaScript, başarılı bir HTTP GET isteğini işlemek için "yük" olay işleyicisini (handleBingResponse()
) çağırır.
// handle Bing search request results
function handleBingResponse() {
hideDivs("noresults");
var json = this.responseText.trim();
var jsobj = {};
// try to parse JSON results
try {
if (json.length) jsobj = JSON.parse(json);
} catch(e) {
renderErrorMessage("Invalid JSON response");
}
// show raw JSON and HTTP request
showDiv("json", preFormat(JSON.stringify(jsobj, null, 2)));
showDiv("http", preFormat("GET " + this.responseURL + "\n\nStatus: " + this.status + " " +
this.statusText + "\n" + this.getAllResponseHeaders()));
// if HTTP response is 200 OK, try to render search results
if (this.status === 200) {
var clientid = this.getResponseHeader("X-MSEdge-ClientID");
if (clientid) retrieveValue(CLIENT_ID_COOKIE, clientid);
if (json.length) {
if (jsobj._type === "Images") {
if (jsobj.nextOffset) document.forms.bing.nextoffset.value = jsobj.nextOffset;
renderSearchResults(jsobj);
} else {
renderErrorMessage("No search results in JSON response");
}
} else {
renderErrorMessage("Empty response (are you sending too many requests too quickly?)");
}
}
// Any other HTTP response is an error
else {
// 401 is unauthorized; force re-prompt for API key for next request
if (this.status === 401) invalidateSubscriptionKey();
// some error responses don't have a top-level errors object, so gin one up
var errors = jsobj.errors || [jsobj];
var errmsg = [];
// display HTTP status code
errmsg.push("HTTP Status " + this.status + " " + this.statusText + "\n");
// add all fields from all error responses
for (var i = 0; i < errors.length; i++) {
if (i) errmsg.push("\n");
for (var k in errors[i]) errmsg.push(k + ": " + errors[i][k]);
}
// also display Bing Trace ID if it isn't blocked by CORS
var traceid = this.getResponseHeader("BingAPIs-TraceId");
if (traceid) errmsg.push("\nTrace ID " + traceid);
// and display the error message
renderErrorMessage(errmsg.join("\n"));
}
}
Önemli
Başarılı HTTP istekleri, başarısız arama bilgilerini içerebilir. Arama işlemi sırasında bir hata oluşursa, Bing Resim Arama API’si, JSON yanıtında hata bilgilerini ve 200 olmayan bir HTTP durum kodunu döndürür. Buna ek olarak, istekte hız sınırlaması varsa API boş yanıt döndürür.
Arama sonuçlarını görüntüleme
Arama sonuçları, renderSearchResults()
işlevi tarafından görüntülenir. BU işlev, Bing Resim Arama hizmeti tarafından döndürülen JSON’ı alır ve döndürülen görüntülerde ve ilgili aramalarda uygun bir işlenen işlevi çağırır.
function renderSearchResults(results) {
// add Prev / Next links with result count
var pagingLinks = renderPagingLinks(results);
showDiv("paging1", pagingLinks);
showDiv("paging2", pagingLinks);
showDiv("results", renderImageResults(results.value));
if (results.relatedSearches)
showDiv("sidebar", renderRelatedItems(results.relatedSearches));
}
Görüntü arama sonuçları, JSON yanıtında üst düzey value
nesnesinde bulunur. Bunlar renderImageResults()
öğesine geçirilir ve burada sonuçlar aracılığıyla yineleme yapıp her bir öğeyi HTML’e dönüştürür.
function renderImageResults(items) {
var len = items.length;
var html = [];
if (!len) {
showDiv("noresults", "No results.");
hideDivs("paging1", "paging2");
return "";
}
for (var i = 0; i < len; i++) {
html.push(searchItemRenderers.images(items[i], i, len));
}
return html.join("\n\n");
}
Bing Resim Arama API’si, kullanıcıların arama deneyimlerine yol göstermesi için dört tür arama önerisi döndürebilir; bunların her biri kendi üst düzey nesnesinde yer alır:
Öneri | Description |
---|---|
pivotSuggestions |
Özgün aramadaki asıl sözcüğü başka bir sözcükle değiştiren sorgular. Örneğin, "kırmızı çiçekler" araması yaparsanız pivot sözcüğü "kırmızı" ve pivot öneri de "sarı çiçekler" olabilir. |
queryExpansions |
Daha fazla terim ekleyerek özgün aramayı daraltan sorgular. Örneğin, "Microsoft Surface" araması yaparsanız genişletilmiş sorgu "Microsoft Surface Pro" olabilir. |
relatedSearches |
Özgün aramayı giren diğer kullanıcılar tarafından da girilmiş olan sorgular. Örneğin, "Mount Rainier" araması yaparsanız, ilgili arama "Mt. Saint Helens" olabilir. |
similarTerms |
Özgün aramayla benzer anlamı olan sorgular. Örneğin, "yavru kedi" araması yaparsanız benzer bir terim de "şirin" olabilir. |
Bu uygulama yalnızca relatedItems
önerilerini işler ve sonuçta elde edilen bağlantıları sayfanın kenar çubuğuna yerleştirir.
Arama sonuçlarını işleme
Bu uygulamada searchItemRenderers
nesnesi, her arama sonucu türü için HTML oluşturan işleyici işlevlerini içerir.
searchItemRenderers = {
images: function(item, index, count) { ... },
relatedSearches: function(item) { ... }
}
Bu işleyici işlevleri aşağıdaki parametreleri kabul eder:
Parametre | Açıklama |
---|---|
item |
Öğenin özelliklerini, örneğin URL'sini ve açıklamasını içeren JavaScript nesnesi. |
index |
Kendi koleksiyonu içindeki arama öğesinin dizini. |
count |
Arama sonucu öğesinin koleksiyonundaki öğelerin sayısı. |
index
ve count
parametreleri, sonuçları numaralandırmak, koleksiyonlara yönelik HTML oluşturmak ve içeriği düzenlemek için kullanılır. Özellikle:
- Görüntünün küçük resim boyutunu hesaplar (genişlik, minimum 120 piksel olacak şekilde değişiklik gösterir, yükseklik ise 90 piksel’e sabittir).
- Resmin küçük resmini görüntülemek için HTML
<img>
etiketini oluşturur. - Görüntüye ve bu görüntüyü içeren sayfaya bağlanan HTML
<a>
etiketlerini oluşturur. - Resim ve bu resmin bulunduğu site hakkındaki bilgileri görüntüleyen bir açıklama oluşturur.
images: function (item, index, count) {
var height = 120;
var width = Math.max(Math.round(height * item.thumbnail.width / item.thumbnail.height), 120);
var html = [];
if (index === 0) html.push("<p class='images'>");
var title = escape(item.name) + "\n" + getHost(item.hostPageDisplayUrl);
html.push("<p class='images' style='max-width: " + width + "px'>");
html.push("<img src='"+ item.thumbnailUrl + "&h=" + height + "&w=" + width +
"' height=" + height + " width=" + width + "'>");
html.push("<br>");
html.push("<nobr><a href='" + item.contentUrl + "'>Image</a> - ");
html.push("<a href='" + item.hostPageUrl + "'>Page</a></nobr><br>");
html.push(title.replace("\n", " (").replace(/([a-z0-9])\.([a-z0-9])/g, "$1.<wbr>$2") + ")</p>");
return html.join("");
}, // relatedSearches renderer omitted
Küçük resim görüntüsünün height
ve width
öğesi, hem <img>
etiketinde hem de küçük resim URL’sindeki h
ve w
alanlarında kullanılır. Böylece Bing’in tam olarak o boyutta bir küçük resim döndürmesi sağlanır.
Kalıcı istemci kimliği
Bing arama API'lerinden gelen yanıtlar, başarılı isteklerle birlikte API'ye geri gönderilmesi gereken bir X-MSEdge-ClientID
üst bilgisi içerir. Birden çok Bing Arama API'si kullanılıyorsa, mümkün olduğunca bu API'lerin tümünde aynı istemci kimliği kullanılmalıdır.
Böylelikle X-MSEdge-ClientID
üst bilgisi sayesinde Bing API’leri kullanıcının tüm aramalarını ilişkilendirebilir. Bunun önemli avantajı vardır
İlk olarak, Bing arama alt yapısının geçmiş bağlamı aramalara uygulayarak kullanıcıya daha uygun sonuçlar bulabilmesini sağlar. Kullanıcı daha önce yelkencilikle ilgili terim aramaları yaptıysa, daha sonra yapılan "düğümler" araması tercihen yelkencilikte kullanılan düğümler hakkında bilgi döndürebilir.
İkincisi, Bing yeni özellikleri geniş ölçekte kullanıma sunmadan önce bunları denemesi için rastgele kullanıcılar seçebilir. Her istekle birlikte aynı istemci kimliğinin sağlanması, özelliği görmesi tercih edilen kullanıcıların bunu sürekli görebilmesini sağlar. İstemci kimliği olmadan, özellik kullanıcıya arama sonuçlarında rastgele gösterilebilir ve gösterilmeyebilir.
Tarayıcı güvenlik ilkeleri (CORS) X-MSEdge-ClientID
üst bilgisinin JavaScript'in kullanımına sunulmasını engelleyebilir. Bu sınırlama, arama sonucunun kaynağı istekte bulunan sayfadan farklı olduğunda ortaya çıkar. Üretim ortamında, Web sayfasıyla aynı etki alanında API çağrısı yapan bir sunucu tarafı betiği barındırarak bu ilkeye uymalısınız. Betiğin kaynağı Web sayfasıyla aynı olduğundan, X-MSEdge-ClientID
üst bilgisi JavaScript'in kullanımına sunulur.
Not
Üretim ortamındaki bir Web uygulamasında, isteği sunucu tarafından gerçekleştirmeniz gerekir. Aksi takdirde, Bing Arama API'si anahtarınızın Web sayfasına eklenmesi gerekir ve bu durumda kaynağı görüntüleyen herkes tarafından görülebilir. API abonelik anahtarınız altında gerçekleştirilen tüm kullanım, yetkisiz tarafların yaptığı istekler bile size faturalandırılır; dolayısıyla anahtarınızı açıklamamanız önemlidir.
Geliştirme amacıyla, Bing Web Araması API’si isteğini CORS ara sunucusu aracılığıyla yapabilirsiniz. Böyle bir proxy'den gelen yanıt, yanıt üst bilgilerine izin veren ve Bunları JavaScript için kullanılabilir hale getiren bir Access-Control-Expose-Headers
üst bilgi içerir.
Öğretici uygulamamızın istemci kimliği üst bilgisine erişebilmesi için CORS ara sunucusu kolayca yüklenebilir. İlk olarak, henüz yüklemediyseniz Node.js'yi yükleyin. Ardından komut penceresinde aşağıdaki komutu yürütün:
npm install -g cors-proxy-server
Sonra, HTML dosyasındaki Bing Web Araması uç noktasını şöyle değiştirin:
http://localhost:9090/https://api.cognitive.microsoft.com/bing/v7.0/search
Son olarak, aşağıdaki komutla CORS ara sunucusunu başlatın:
cors-proxy-server
Öğretici uygulamasını kullanırken komut penceresini açık bırakın; pencere kapatılırsa ara sunucu durdurulur. Arama sonuçlarının altındaki genişletilebilir HTTP Üst Bilgileri bölümünde artık X-MSEdge-ClientID
üst bilgisini (diğerleriyle birlikte) görebilir ve bunun her istekte aynı olduğunu doğrulayabilirsiniz.