從 Search API 看蘋果如何構建應用內搜索

關於應用內搜索,目前有兩個較為成熟的方案,iOS Search API 和 Google Firebase App Indexing。限於篇幅,分成了兩篇,這一篇關於 Apple,後一篇關於Google。

在2015年的WWDC 大會上,蘋果推出了全新的iOS Search API,一度讓『應用內搜索』這個話題成為熱門。如今一年過去,不少應用也適配了Search API,我們不妨借這個機會深入了解什麼是應用內搜索。

應用內搜索指的是能夠搜索應用內的內容。和網頁內容不一樣,應用內容往往沒有統一的URI,搜索引擎也就無法準確定位。其次,點擊搜索結果後要調起應用載入內容。所以,應用內搜索的第一步是建立唯一對應的索引,第二步是讓搜索引擎知道該調起哪一個應用。

iOS Search API是第一個系統性解決應用內搜索的方案,在這套方案里,應用內容被抽象成兩大類:私密內容(Priviate)和公開內容(Public),與之對應的是兩類索引:On-Device Index和Cloud Index。值得注意的是,索引才有on-device(本地)和cloud(雲端)之分,內容本身無所謂在線還是雲端,因為私密內容也可以上傳至雲端。

為了解決私密內容和公開內容的索引問題,蘋果推出了iOS Search API,包含3個子API,分別是:

  • NSUserActivity API: 搜索看過的內容(ViewedApp Content)

  • CoreSpotlight API: 搜索任意內容(AnyApp Content)

  • Web Markup API: 搜索網頁上的應用內容(AppContent on Web)

NSUserActivity API

NSUserActivity API 最早於iOS 8引入,應用於新功能Handoff:

藉助「連續互通」功能,您可以在 iOS 設備和 Mac 之間無縫轉換,還能將這些設備協同使用。 --support.apple.com/zh-cn

為了實現Handoff,應用本身需要利用NSUserActivity API捕捉當前頁面的元數據,另一個設備收到並解析這些元數據就可以『從您離開的位置重新開始』。

在iOS 9中,蘋果擴展了這個API的能力,讓它成為搜索的一部分。第三方開發者可以利用這個API捕捉當前頁面的元數據,包括但不限於:

  1. title:頁面標題

  2. keywords:關鍵詞。開發者可以隨意添加關鍵詞嗎?蘋果雖然『允許』這種行為,但很可能會降低搜索結果的排序

  3. contentAttributeSet:描述該頁面的欄位,如縮略圖、詳情等。開發者可以使用系統自帶的欄位(詳情點這裡CSSearchableItemAttributeSet Class Reference),也可以自定義欄位。

  4. webpageURL(可不填):假如這個詳情頁有對應的 Web 頁面,就可以附上。

舉個例子,目前知乎、京東的iOS客戶端都調用了這個API,我們可以iOS Spotlight里搜索到在知乎瀏覽過的問題、在京東瀏覽過的商品詳情頁。

用戶的搜索詞只要命中了 title / keywords / contentAttributeSet 等任一個,都可以搜出結果(各位不妨一試),但搜索結果的排序由系統決定。

NSUserActivity API默認只能檢索用戶看過的頁面,默認生成的是on-device index。也就是說,如果我沒有安裝京東,沒有在京東打開過《白夜行》的詳情頁,那麼我就搜索不到結果。

但是,在這個API中還有1個屬性:eligibleForPublicIndexing(指是否允許將其設為公開頁面)。假設京東在調用API時,將這個屬性設置為『True』。那隻要有足夠多的用戶(蘋果雲端決定多少為『足夠多』)打開過《白夜行》這個頁面,即便我沒有打開過,也同樣能搜索到。這個特性就比較適合用來索引近期熱門的公共內容,比如最近熱賣圖書、熱映電影、熱門微博等等,可惜我還沒找到用該特性的應用??

這個 API 還有一些特性,比如是否支持Handoff(eligibleForHandoff),是否設置索引的過期時間(expirationDate)等等,詳情可以閱讀官方文檔NSUserActivity Class Reference。

CoreSpotlight API

前面提到了NSUserActivity API 默認只會檢索用戶看過的頁面,但如果開發者想讓自己app內的所有內容都能被檢索呢(不局限於用戶看過)?蘋果為此準備了CoreSpotlight API。

Core Spotlight API 理論上能索引應用內的所有內容,但生成的是on-device index,即只有本機才能檢索到,並且它沒有類似eligibleForPublicIndexing的特性,是專為私密內容而設的API。

下面以豆瓣的電影內容為例說明如何調用該API:

  1. 創建N個CSSearchableItem,用來一一對應N部電影。每部電影都是一個item。

  2. 利用CSSearchableItemAttributeSet(也就是NSUserActivity API提到的那個)來給這N個CSSearchableItem逐一添加足夠多的描述,如名稱、主演、評分等。

  3. 把這些CSSearchableItem添加到on-deviceindex中。

如此一來,就能在iOS的spotlight中搜索到這些電影了(如下圖),效果看上去和NSUserActivity API,但這個API不需要用戶曾經看過才能被搜索到。

(如果沒猜錯的話)豆瓣就是用這個API來索引大部分的電影電視劇內容,但有意思的是,豆瓣並沒有提供全量電影的搜索,也猜不透是哪一類電影能被搜到╮(╯▽╰)╭… 也許是豆瓣的主動過濾?也許是蘋果限制了CSSearchableItem的數目上限?有興趣的讀者可以嘗試在官方文檔Core Spotlight Framework 找答案(反正我沒找到答案??)

Web Markup API

調用NSUserActivity API和Core Spotlight API生成的索引都有一個共同點,即只有安裝應用後才能搜索到應用內容。假如你的內容大部分都基於web(如豆瓣的電影、知乎的問答),並且希望用戶在沒有安裝應用的時候也能搜索到這些內容呢?

蘋果為此準備了第三個API:Web Markup API。開發者調用API,允許蘋果的爬蟲抓取自己的網頁內容,聲明網頁內容和應用的關聯性,那麼就有機會讓自己的內容出現在iOS Spotlight 和 Safari的搜索結果中。

同樣以豆瓣為例,假設我們希望全量圖書、電影都能被所有用戶搜索到,有以下幾個步驟:

1.允許蘋果的爬蟲索引豆瓣的網頁內容

步驟很簡單,在提交應用給App Store 的時候,聲明哪些網頁可以被爬蟲索引就行了。具體的步驟參考官方文檔Making Your Website Discoverable by Apple。

2.確認你的網頁源代碼包含 Mobile Deep Link

在解釋Mobile Deep Link之前,先簡要解釋什麼是Deep Link 和 Deep Linking:

Deep Link 是一個指向特定內容的鏈接,比如twitter.com/elonmusk 是指向Elon Musk Twitter主頁的鏈接。

Deep Linking 是一個過程,指點擊 DeepLink 調起應用打開詳情頁的過程。如點擊上述鏈接直接打開Twitter,並載入Elon Musk主頁的過程:

所謂的Mobile Deep Link 是指用在移動操作系統的Deep Link,但不一定以http/https 開頭,比如在iOS里,『mailto:frank@wwdc.com』就是一條 Mobile DeepLink, 點擊之後會調起郵件應用,並填上收件人frank@wwdc.com.

不同於web端的Deep Link都有統一的標準,移動應用的Mobile Deep Link標準不盡相同,所以豆瓣的開發者需要在網頁源代碼中寫入自己支持的Mobile Deep Link,聲明這個網頁該由豆瓣App打開。爬蟲拿到這個信息後,就會告訴iOS『請用豆瓣客戶端打開這個鏈接』。

目前Apple 支持的Mobile Deep Link 有4種:

  • UniversalLinks

  • Custom URL schemes

  • Facebook AppLinks

  • Twitter Cards.

這4種Mobile Deep Link 協議所做的事情都類似,主要是告訴爬蟲2件事情:

  1. 這個網頁由哪個應用打開

  2. 打開的內容是什麼

其中 UniversalLinks 是 Apple 在iOS 9 推出的協議,也是Apple 強烈推薦開發者使用的協議。之所以稱其為 『Universal』(通用),是因為使用這個協議之後,一條普通的HTTP/HTTPS 鏈接,在iOS上既能調起應用,也能由 Safari 打開網頁。Twitter已經適配了Universal Links,所以在系統任意地方(如備忘錄、郵件)點擊 twitter.com/elonmusk 這樣的HTTP鏈接,都能調起應用,或者打開網頁(如下圖)。

支持Universal Links 的方法很簡單,細節就不再此贅述了,詳情可以閱讀官方文檔 Support Universal Links 或者觀看 WWDC 視頻介紹Seamless Linking to Your App.

3.使用標註語言讓網頁內容結構化

這不是非做不可的一步,但蘋果強烈推薦開發者這麼做,原因是在未標註的情況下,網頁爬蟲只能分辨出『網頁標題』和『網頁內容』,這也是為什麼 Google 和百度的搜索結果往往只有標題和摘要的原因。

Google的搜索結果

但如果網站的開發者使用了 schema.org Microdata 和 Open Graph 之類的標註語言,爬蟲就能知道『網頁內容』裡面,每一個欄位所代表的含義了。

舉例來說,一段沒有結構化標註的HTML代碼是這樣的:

<div> <h1> 白夜行</h1> <span>東野圭吾 </span> <span>9.1 </span> <img src="白夜行.jpg" /></div>

對於這段HTML代碼,爬蟲不知道這是一本書,也不知道原來『東野圭吾』和『9.1』代表的是作者和評分。但如果使用了 schema.org Microdata 的標註語言,這一段HTML代碼就會變成這樣:

<div itemscope itemtype ="http://schema.org/Book"> <h1 itemprop="name"> 白夜行</h1> <span itemprop="author"> 東野圭吾</span> <span itemprop="ratingValue"> 9.1</span> <img src= "白夜行.jpg" itemprop="contentUrl" /></div>

加粗的部分是標註語言,用來告訴爬蟲這個欄位是書名(name)、作者(author)和評分(ratingValue),iOS拿到這些結構化數據之後,就可以呈現更好的樣式,蘋果稱其為『 Rich Results 』。

值得一提的是,schema.org 和Open Graph 這兩種標記語言都是開源項目,並逐漸成為業內統一的標準,Google、微軟、蘋果、Pinterest都已經支持這兩種語言並從中受惠。

如果以上3步都做了,用戶就可以在iOS搜索到豆瓣全量的圖書。假如用戶安裝了豆瓣,點擊搜索結果會直接打開豆瓣,否則會用 Safari 載入網頁,效果圖如下:

結語

至此為止,iOS Search API已介紹完畢。儘管他們很強大,但它們的分工並沒有那麼明確,某些內容可以同時使用這3個API來索引。儘管官方也給了一些例子供參考ExampleImplementations,但這種模稜兩可容易讓開發者困惑。

例如前面提到的NSUserActivity API和Core Spotlight API默認都生成on-device index,只有在安裝應用後本機才能檢索到這些內容,按理說更適合用於索引私密內容,如郵件、相冊里的照片、Evernote的筆記等。但我們前面也看到了,知乎、京東、豆瓣用這兩個API索引的卻是公共內容。這會帶來兩個問題:

  1. NSUserActivity API要求用戶看過的頁面才被索引,但既然是看過的內容,那被再次搜索的概率就不高。

  2. Core Spotlight API 生成的是on-deviceindex(本地索引),用它來索引海量的圖書、電影、商品,似乎不是一個合理的做法。若做不到全量索引,那用戶再次使用搜索的可能性也不高。

所以對於知乎、豆瓣的公共內容來說,使用Web Markup API會更合適,適配成本也許相對較大。當然,如果豆瓣因為各種原因不願意使用web markup API,也可以:

  1. 使用NSUserActivity API,設置為eligibleForPublicIndexing。保證用戶能夠搜索看過的內容和近期熱門內容

  2. 同時使用Core Spotlight API,用來檢索那些綜合評分高的電影。

但睱不掩瑜,這是一整套完善的方案。同時蘋果的領頭羊作用也將惠及整個業界:越多開發者願意支持iOS Search API,就有更多的應用支持Deep Linking,有更多的網頁加入了結構化的標註語言,這些都能讓第三方應用、甚至是Android、Windows等第三方平台受益。讓我們拭目以待。

最後,你可能會疑惑,用戶真的需要應用內搜索嗎?在這個問題上蘋果著墨不多,他提到的理由是,如今用戶86%的時間花在了應用(Apps),僅14%的時間花在網頁(Web),因此發現並檢索應用內容顯得至關重要(詳見WWDC2015 Introducing Search APIs)。論據是沒錯,但這個推論值得我們再三推敲,留待各位思考。

歡迎各位指出文中的錯誤?? 如果能解答其中的疑問就更好了。

附:

前文提到了我猜測知乎使用的是NSUserActivity API,豆瓣使用的是CoreSpotlight。下面簡單說說是怎麼判斷的。

先看知乎:

  1. 在不安裝知乎的情況下,不會有任何知乎的搜索結果。說明不是Web Markup API

  2. 安裝知乎後,在不瀏覽內容的情況下,也沒有搜索結果,說明不是CoreSpotlight

  3. 瀏覽某一問題,再用標題、正文中的關鍵詞去搜索(如pokemon),有搜索結果,說明是NSUserActivity API

再看豆瓣:

  1. 首先明確豆瓣的電影能被搜到,圖書並不能,所以僅使用電影名作關鍵詞。

  2. 在不安裝豆瓣的情況下,不會有任何豆瓣的搜索結果。說明不是Web Markup API

  3. 安裝豆瓣後,在不瀏覽內容的情況下,個別有搜索結果,說明可能是CoreSpotlight API,或者是設置為eligibleForPublicIndexing的NSUserActivity API(即看過的人足夠多)

  4. 瀏覽某一電影(如《寒戰2》),發現不能搜索到它,說明不是NSUserActivity API

  5. 猜測是使用了CoreSpotlight API,但為什麼只有部分電影能被搜到就不得而知了。可能是豆瓣主動過濾,也可能是被iOS限制了on-device index的數目。

同樣的方法,可以猜測京東、大眾點評調用的是NSUserActivity API,貓眼、高德地圖調用的是CoreSpotlight API。目前還沒發現使用Web Markup API的應用……

關於應用內搜索,蘋果給出了自己的方案,Google 也有自己的思考,諸如Google Now 和 Now on tap 便是相關的產品體現。下一篇打算好好介紹Google 的 App Indexing 方案 :)


推薦閱讀:

查詢詞-站點相關性計算
互聯網搜索入門
記一次ElasticSearch集群災難恢復
這不是文本框,是搜索框
玩轉知乎,我只看這一篇。

TAG:iOS | 应用内搜索 | 搜索 |