現(xiàn)在我們知道如何搜索一個區(qū)域,其他的部分就簡單多了。
如何知道搜索哪個區(qū)域
在數(shù)據(jù)查詢的時候,把查詢區(qū)域發(fā)送到一個消息隊列里 (AWS SQS),然后 search worker 不斷地從隊列里讀取需要搜索的區(qū)域。
如何得到每個區(qū)域里需要搜索的坐標點
Google S2 是一個基于 希爾伯特曲線 的區(qū)域分割方式。它可以將一個區(qū)域分割成相同大小的區(qū)域并且用一個 unique id 來表示。我們可以用 Google S2 將區(qū)域分割成 100m * 100m 的小正方形,然后取其中心作為用戶位置,發(fā)送給服務器。
希爾伯特例子
圖中的黑線是一個 hilbert curve,而圖中的每一個黑線的轉折點就是一個搜索點。

系統(tǒng)優(yōu)化
在實現(xiàn)了最簡單的數(shù)據(jù)采集層之后,就已經(jīng)可以在網(wǎng)頁端看到小精靈了。但是還有一些問題需要優(yōu)化解決:
如果每個查詢地區(qū)都進行搜索,搜索服務器的壓力會比查詢服務器大數(shù)十倍,而許多區(qū)域是沒必要短時間內多次搜索的。
如果使用同一個賬號進行搜索,非常容易被封號。
如果每次搜索都需要先登錄服務器,會大大增加搜索延遲。
讓我們一個個問題來解決:
如何減少搜索次數(shù)
這個問題的關鍵就在于減少相同地區(qū)的重復搜索。借用 Data Deduplication 的思想,我們可以用 時間 + 地點 來作為 Deduplication Key,如果區(qū)域 A 在 x 秒內搜索過了,就跳過這個區(qū)域。
在這里,我用 redis 來儲存搜索記錄,原因有兩個:
Redis 的 setex 功能可以自動添加 ttl (Time to live),在一定時間之后,記錄會自動消失。
Redis 作為一種 in memory cache,對于這類不需要高可靠性的數(shù)據(jù),可以提供很好的查詢速度。
具體實現(xiàn):

如何避免同一個賬戶過于頻繁地訪問 Pokemon Go 服務器
答案很簡單。使用多個賬號( > 10000 個)。那么如何注冊并使用多個賬號呢?
由于 Pokemon Go 官網(wǎng)的 注冊 不需要驗證碼,我用 selenium 寫了一個批量注冊機,自動填寫注冊信息。
注冊郵箱需要驗證之后才能使用,如何批量得到郵箱地址呢?
我申請了一個域名,設置通用郵件轉發(fā)。這樣所有在那個域名下的郵箱地址都會轉發(fā)到我指定的一個郵箱了。我只需要注冊一個郵箱,就可以映射到所有在我域名下的郵箱了。大部分的域名提供商都有郵件轉發(fā)功能。
怎樣批量激活賬戶呢?
設置郵箱的 Pop3,并且寫一個小腳本,定期輪詢新郵件,并且點擊激活地址。在激活之后,儲存用戶名密碼到數(shù)據(jù)庫。
如何降低由于登錄服務器引起的搜索延遲
在 tejado/pgoapi 中,為了模擬用戶的行為,使用 API 之前,都會進行登錄行為。整個流程分為 3 個部分:
從 oauth 服務器獲取 account token。這里的 token 既可能是 Pokemon Trainer Club 的,也可能是 Google 的。
從 Load Balance 服務器 ( http:// pgorelease.nianticlabs.com /plfe ) 獲取實際的 rpc 服務器地址以及 access token。
使用第二部的 access token 簽名 rpc 請求,發(fā)送到 rpc 服務器。
在第一步跟第二步獲得的 token 都是可以重復使用的,oauth token 有效時間大概是 3 小時,access token 有效時間大概是 30 分鐘。我在每次成功登陸之后把 access token 和 rpc 服務器地址存到數(shù)據(jù)庫中,這樣就可以在不同的 Scan worker 之間使用了。
在優(yōu)化了上面三點之后,每條數(shù)據(jù)爬取的速度就快了一倍。
Pokemon Map 的災難
這個地圖做起來其實并不難,所以有很多同質性的地圖網(wǎng)站,最有名的大概就是 pokevision 。在 2016/07/30,Niantic (Pokemon Go的制作公司)將大部分云服務提供商的 ip 地址都封禁了,包括 AWS, Azure, DigitalOcean 等等。由于大部分的 Pokemon Map 都托管在云服務上,這就導致了幾乎所有的 Pokemon Map 都無法使用了。比如這篇報道 。
同樣,我的 Pokemon Map 也無法獲取數(shù)據(jù)了,所有的 rpc 請求都返回了 nginx 403 錯誤請求。 為了解決這個問題,我們就需要更改發(fā)送請求的ip地址,最簡單的方式就是使用一個 proxy 轉發(fā)所有的通信流量。具體實現(xiàn)也很簡單,在 Scan Worker 服務器上跑一個 ssh 鏈接即可: