Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Предупреждение
30 октября 2020 г. API поиска Bing перемещены из служб ИИ Azure в службы поиска Bing. Эта документация предоставляется только для справки. С актуальной документацией можно ознакомиться в документации по API поиска Bing. Инструкции по созданию ресурсов Azure для Поиска Bing приведены в статье Создание ресурса для Поиска Bing с помощью Azure Marketplace.
API поиска новостей Bing позволяет выполнять поиск в Интернете и получать результаты типа новостей, относящихся к поисковому запросу. В этом руководстве мы создадим одностраничное веб-приложение, использующее API поиска новостей Bing для отображения результатов поиска на странице. Приложение включает компоненты HTML, CSS и JavaScript. Исходный код для этого примера доступен на GitHub.
Замечание
Заголовки JSON и HTTP в нижней части страницы при щелчке отображают ответ JSON и сведения о HTTP-запросе. Эти сведения могут быть полезны при изучении службы.
На примере учебного приложения показано, как выполнить такие задачи:
- Выполнение вызова API поиска новостей Bing в JavaScript
- Передача параметров поиска в API поиска новостей Bing
- Отображение результатов поиска новостей из четырех категорий: любой тип, бизнес, здоровье или политика, с 24 часов, прошлой недели, месяца или всего доступного времени
- Пролистать результаты поиска
- Обработка идентификатора клиента Bing и ключа подписки API
- Обработка ошибок, которые могут возникнуть
Страница учебника полностью самодостаточна; Она не использует внешние фреймворки, таблицы стилей или файлы изображений. Он использует только широко поддерживаемые функции языка JavaScript и работает с текущими версиями всех основных веб-браузеров.
Предпосылки
Чтобы следовать вместе с руководством, вам потребуются ключи подписки для API поиска Bing. Если у вас нет этих данных, вам потребуется создать их:
- подписка Azure — создайте бесплатную учетную запись.
- После того как получите подписку Azure, создайте ресурс поиска Bing на портале Azure, чтобы получить ключ и конечную точку. После развертывания щелкните Перейти к ресурсу.
Компоненты приложения
Как и любое одностраничное веб-приложение, это учебное приложение включает в себя три части:
- HTML — определяет структуру и содержимое страницы
- CSS — определяет внешний вид страницы
- JavaScript — определяет поведение страницы
Большая часть HTML и CSS является обычным, поэтому учебник не обсуждает его. HTML-код содержит форму поиска, в которой пользователь вводит запрос и выбирает параметры поиска. Форма подключена к JavaScript, которая фактически выполняет поиск с помощью onsubmit атрибута тега <form> :
<form name="bing" onsubmit="return newBingNewsSearch(this)">
Обработчик onsubmit возвращает false, который предотвращает отправку формы на сервер. Код JavaScript выполняет работу по сбору необходимых сведений из формы и выполнению поиска.
HTML также содержит деления (теги HTML-<div>), в которых отображаются результаты поиска.
Управление ключом подписки
Чтобы избежать необходимости включать ключ подписки API поиска Bing в код, мы используем постоянное хранилище браузера для хранения ключа. Перед сохранением ключа мы запрашиваем ключ пользователя. Если ключ позже будет отклонён API, мы отменим хранимый ключ, чтобы пользователю снова предложили его ввести.
Мы определяем функции storeValue и retrieveValue, использующие объект localStorage (не все браузеры поддерживают его) или файл cookie. Функция getSubscriptionKey() использует эти функции для хранения и извлечения ключа пользователя. Вы можете использовать глобальную конечную точку ниже или конечную точку пользовательского поддомена , отображаемую на портале Azure для ресурса.
// Cookie names for data we store
API_KEY_COOKIE = "bing-search-api-key";
CLIENT_ID_COOKIE = "bing-search-client-id";
// Bing Search API endpoint
BING_ENDPOINT = "https://api.cognitive.microsoft.com/bing/v7.0/news";
// ... omitted definitions of storeValue() and retrieveValue()
// Browsers differ in their support for persistent storage by
// local HTML files. See the source code for browser-specific
// options.
// Get 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>onsubmit вызывает функцию bingWebSearch для возврата результатов поиска.
bingWebSearch использует getSubscriptionKey() для проверки подлинности каждого запроса. Как показано в предыдущем определении, getSubscriptionKey запрашивает пользователю ключ, если ключ не введен. Затем ключ сохраняется для продолжения использования приложением.
<form name="bing" onsubmit="this.offset.value = 0; return bingWebSearch(this.query.value,
bingSearchOptions(this), getSubscriptionKey())">
Выбор параметров поиска
На следующем рисунке показаны текстовое поле запроса и параметры, определяющие поиск новостей о финансировании учебного заведения.
Html-форма содержит элементы со следующими именами:
| Элемент | Описание |
|---|---|
where |
Раскрывающееся меню для выбора рынка (расположения и языка), используемого для поиска. |
query |
Текстовое поле для ввода условий поиска. |
category |
Флажки для выдвижения конкретных типов результатов. Повышение здоровья, например, увеличивает рейтинг новостей здравоохранения. |
when |
Раскрывающееся меню для дополнительного ограничения поиска до последнего дня, недели или месяца. |
safe |
Флажок, указывающий, следует ли использовать функцию безопасного поиска Bing для исключения "взрослого" контента. |
count |
Скрытое поле. Количество результатов поиска, возвращаемых по каждому запросу. Измените значение, чтобы отобразить меньше или больше результатов на страницу. |
offset |
Скрытое поле. Смещение первого результата поиска в запросе; используется для разбиения по страницам. Он сбрасывается на 0 при новом запросе. |
Замечание
Поиск в Интернете Bing предлагает другие параметры запроса. Мы используем только несколько из них.
// 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);
for (var i = 0; i < form.category.length; i++) {
if (form.category[i].checked) {
category = form.category[i].value;
break;
}
}
if (category.valueOf() != "all".valueOf()) {
options.push("category=" + category);
}
options.push("count=" + form.count.value);
options.push("offset=" + form.offset.value);
return options.join("&");
}
Например, параметр SafeSearch в фактическом вызове API может быть strict, moderate, или off, причем значение по умолчанию — moderate. Однако наша форма использует флажок, который имеет только два состояния. Код JavaScript преобразует этот параметр strict в или off (moderate не используется).
Выполнение запроса
Учитывая запрос, строку параметров и ключ API, BingNewsSearch функция использует XMLHttpRequest объект для выполнения запроса к конечной точке поиска новостей Bing.
// perform a search given query, options string, and API key
function bingNewsSearch(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();
if (category.valueOf() != "all".valueOf()) {
var queryurl = BING_ENDPOINT + "/search?" + "?q=" + encodeURIComponent(query) + "&" + options;
}
else
{
if (query){
var queryurl = BING_ENDPOINT + "?q=" + encodeURIComponent(query) + "&" + options;
}
else {
var queryurl = BING_ENDPOINT + "?" + 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-запроса JavaScript вызывает load обработчик событий, handleBingResponse() функцию для обработки успешного HTTP-запроса GET к API.
// 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 === "News") {
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"));
}
}
Это важно
Успешный HTTP-запрос не обязательно означает, что сам поиск произошёл успешно. Если ошибка возникает в операции поиска, API поиска новостей Bing возвращает код состояния HTTP, отличный от 200, и содержит сведения об ошибке в ответе JSON. Кроме того, если запрос был ограничен скоростью, API возвращает пустой ответ.
Большая часть кода в обеих предыдущих функциях посвящена обработке ошибок. Ошибки могут возникать на следующих этапах:
| Этап | Потенциальные ошибки | Обрабатывается |
|---|---|---|
| Создание объекта запроса JavaScript | Недопустимый URL-адрес. | блок try/catch |
| Выполнение запроса | Сетевые ошибки, прерванные подключения | обработчики событий error и abort |
| Выполнение поиска | Недопустимый запрос, недопустимый JSON, ограничения скорости | тесты в обработчике событий load |
Ошибки обрабатываются путем вызова renderErrorMessage() со сведениями об ошибке. Если ответ проходит полный набор тестов на ошибки, мы используем renderSearchResults(), чтобы отобразить результаты поиска на странице.
Отображение результатов поиска
Основная функция отображения результатов поиска — renderSearchResults(). Эта функция принимает JSON, возвращенный службой поиска новостей Bing, и отображает результаты новостей и связанные поиски, если таковые есть.
// render the search results given the parsed JSON response
function renderSearchResults(results) {
// add Prev / Next links with result count
var pagingLinks = renderPagingLinks(results);
showDiv("paging1", pagingLinks);
showDiv("paging2", pagingLinks);
showDiv("results", renderResults(results.value));
if (results.relatedSearches)
showDiv("sidebar", renderRelatedItems(results.relatedSearches));
}
Основные результаты поиска возвращаются в качестве объекта верхнего уровня value в ответе JSON. Мы передаем их в нашу функцию renderResults(), которая перебирает их и вызывает отдельную функцию для отрисовки каждого элемента в HTML. Полученный HTML возвращается в renderSearchResults(), где он вставляется в results раздел на странице.
function renderResults(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.news(items[i], i, len));
}
return html.join("\n\n");
}
API поиска новостей Bing возвращает до четырех различных типов связанных результатов, каждый из которых имеет собственный объект верхнего уровня. В их число входят:
| Отношение | Описание |
|---|---|
pivotSuggestions |
Запросы, в которых исходное ключевое слово заменяется другим. Например, если вы ищете "красные цветы", ключевым словом может быть "красные", а предложением-заменой может быть "желтые цветы". |
queryExpansions |
Запросы, сужающие исходный поиск, добавляя дополнительные термины. Например, если вы ищете "Microsoft Surface", расширение запроса может быть "Microsoft Surface Pro". |
relatedSearches |
Запросы, которые также были введены другими пользователями, которые ввели исходный поиск. Например, если вы ищете «гора Рейнир», связанный поиск может быть «г. Рейнир». Сент-Хеленс". |
similarTerms |
Запросы, аналогичные исходному поиску. Например, если вы ищете "школы", аналогичный термин может быть "образование". |
Как было показано ранее в renderSearchResults(), мы показываем только предложения relatedItems и помещаем полученные ссылки на боковой панели страницы.
Элементы результата рендеринга
В коде JavaScript объект searchItemRenderersсодержит отрисовщики: функции, которые создают HTML для каждого типа результата поиска.
searchItemRenderers = {
news: function(item) { ... },
webPages: function (item) { ... },
images: function(item, index, count) { ... },
relatedSearches: function(item) { ... }
}
Функция отрисовщика может принимать следующие параметры:
| Параметр | Описание |
|---|---|
item |
Объект JavaScript, содержащий свойства элемента, например URL-адрес и его описание. |
index |
Индекс элемента результата в коллекции. |
count |
Количество элементов в коллекции результатов поиска. |
Параметры index и count можно использовать для числа результатов, создания специального HTML-кода для начала или конца коллекции, вставки разрывов строк после определенного количества элементов и т. д. Если отрисовщик не нуждается в этой функции, он не должен принимать эти два параметра.
Отрисовщик news показан в следующем фрагменте JavaScript:
// render news story
news: function (item) {
var html = [];
html.push("<p class='news'>");
if (item.image) {
width = 60;
height = Math.round(width * item.image.thumbnail.height / item.image.thumbnail.width);
html.push("<img src='" + item.image.thumbnail.contentUrl +
"&h=" + height + "&w=" + width + "' width=" + width + " height=" + height + ">");
}
html.push("<a href='" + item.url + "'>" + item.name + "</a>");
if (item.category) html.push(" - " + item.category);
if (item.contractualRules) { // MUST display source attributions
html.push(" (");
var rules = [];
for (var i = 0; i < item.contractualRules.length; i++)
rules.push(item.contractualRules[i].text);
html.push(rules.join(", "));
html.push(")");
}
html.push(" (" + getHost(item.url) + ")");
html.push("<br>" + item.description);
return html.join("");
},
Функция рендеринга новостей:
- Создает тег абзаца, назначает его классу
newsи отправляет его в html-массив. - Вычисляет размер эскиза изображения (ширина фиксирована в 60 пикселей, высота вычисляется пропорционально).
- Создает тег HTML-
<img>для отображения эскиза изображения. - Создает теги HTML-
<a>, которые связываются с изображением и страницей, содержащей ее. - Создает описание, отображающее сведения о изображении и сайте, на котором он находится.
Размер эскиза используется как в теге <img>, так и в полях h и w в URL-адресе эскиза. Затем служба эскизов Bing предоставляет эскиз точно этого размера.
Сохранение идентификатора клиента
Ответы от API поиска Bing могут включать заголовок X-MSEdge-ClientID, который должен отправляться обратно в API с последовательными запросами. Если используются несколько API-интерфейсов поиска Bing, при возможности следует использовать один и тот же идентификатор клиента.
Предоставление заголовка X-MSEdge-ClientID позволяет API Bing связать все поисковые запросы пользователя, что имеет два важных преимущества.
Во-первых, он позволяет поисковой системе Bing применять прошлый контекст к поиску результатов, которые лучше удовлетворяют пользователю. Если пользователь ранее искал термины, связанные с парусным спортом, например, более поздний поиск "узлов" может предпочтительно возвращать информацию о узлах, используемых в парусном спорте.
Во-вторых, Bing может случайным образом выбрать пользователей для взаимодействия с новыми функциями, прежде чем они становятся широко доступными. Предоставление одного и того же идентификатора клиента с каждым запросом гарантирует, что пользователи, которые видят эту функцию, всегда видят ее. Без идентификатора клиента пользователь может увидеть, что функция появится и исчезнет, казалось бы, случайным образом в результатах поиска.
Политики безопасности браузера (CORS) могут предотвратить доступность заголовка X-MSEdge-ClientID в JavaScript. Это ограничение возникает, когда ответ поиска имеет другой источник от страницы, запрашивающей ее. В рабочей среде следует решить эту политику, размещая серверный скрипт, который вызывает API в том же домене, что и веб-страница. Так как скрипт имеет тот же источник, что и веб-страница, X-MSEdge-ClientID заголовок будет доступен для JavaScript.
Замечание
В рабочем веб-приложении необходимо выполнить запрос на стороне сервера. В противном случае ключ API поиска Bing должен быть включен на веб-страницу, где он доступен любому, кто просматривает источник. Вам выставляется счет за все использование, связанное с ключом подписки API, включая запросы, сделанные несанкционированными сторонами, поэтому важно не разглашать ключ.
В целях разработки можно сделать запрос API Bing для поиска в Интернете с помощью прокси-сервера CORS. Ответ от такого прокси-сервера содержит заголовок Access-Control-Expose-Headers, который обеспечивает доступ к заголовкам ответов и предоставляет их для JavaScript.
Легко установить прокси-сервер CORS, чтобы разрешить нашему учебному приложению обращаться к заголовку идентификатора клиента. Во-первых, если у вас его еще нет, установите Node.js. Затем выполните следующую команду в командном окне:
npm install -g cors-proxy-server
Затем измените конечную точку для веб-поиска Bing в файле HTML на следующую:
http://localhost:9090/https://api.cognitive.microsoft.com/bing/v7.0/search
Наконец, запустите прокси-сервер CORS с помощью следующей команды:
cors-proxy-server
Оставьте окно командной строки открытым при использовании приложения учебника; закрытие окна останавливает прокси-сервер. В разделе расширенных заголовков HTTP под результатами поиска теперь можно увидеть заголовок X-MSEdge-ClientID (среди прочего) и убедиться, что он одинаковый для каждого запроса.