nodejs數據處理實踐之ip匹配


問題描述

對於一份ip數據,我們希望使用開放API來獲得ip地址對應的地理地址。

ip查詢API選擇

互聯網上開放的ip查詢API有很多,但是,它們各有優缺點。筆者在短暫的實踐中了解了一下兩個API。

  1. 淘寶IP地址庫
  2. 百度普通IP定位API

這兩個API都是免費開放。淘寶的API的訪問限制是10qps,百度API的訪問限制是6000/min。百度對於未認證用戶一天的訪問限制次數是10萬次(認證后可以提高配額)。

通過少量數據的比對,淘寶的api可以返回縣地址,而百度的api基本沒有返回縣地址。但是百度的api訪問速度,並發量都比淘寶要大的多。考慮到淘寶api的統計數據中,縣數據的覆蓋率也只有6.72%。所以我們選擇百度的api。

讀者可以查看上述的兩個鏈接,進一步了解判斷,根據自己的情況做出決定。

API接口了解

http://api.map.baidu.com/location/ip?ak=yourak&ip=targetIp

百度的api存在兩種認證模式,一種是ip白名單,一種是sn校驗算。不過筆者在nodejs下發起請求時,一直沒有辦法算對sn值,在網絡上也沒有找到相應的資料,最后轉而使用ip白名單模式,其實對於數據處理的應用,使用ip白名單模式就可以了。

設置ip白名單后就可以直接發起API請求了。

只有IP白名單內的服務器才能成功發起調用
格式: 202.198.16.3,202.198.0.0/16 填寫IP地址或IP前綴網段,英文半角逗號分隔
如果不想對IP做任何限制,請設置為0.0.0.0/0

請求的格式如上,詳細請直接查看鏈接。

代碼

代碼的細節就不多討論,讀者可以直接閱讀源代碼來了解。這里提及一下使用到的npm包以及使用該包的原因和相應的技術文檔,

  1. 數據處理在nodejs下必然包含很多異步操作比如文件讀寫網絡請求等。所以我們使用了流程控制庫co
    如果你不了解co請參看這個鏈接
    Generator 函數的異步應用
    你也可以使用其它流程控制庫。
  2. co-parallelasyncco只能讓異步的流程同步化。但是一些細節的控制還是要依賴其它一些庫。筆者在這里沒有過多調研。簡單查閱資料后,使用co-parallel來做並發控制,使用async.retry在請求失敗的時候重試。
  3. superagent http請求庫

下面是源代碼。



const bluebird = require('bluebird')
const fs = bluebird.promisifyAll(require('fs'))
const co = require('co')
const os = require('os')
const superagent = require('superagent')
const parallel = require('co-parallel')
const async = require('async')
// 計數連接失敗的個數
let failCount = 0

//百度IP地址庫
const urltaoip = 'http://api.map.baidu.com/location/ip?ak=yourak&ip='
// 輸入文件名字
let inputFileName = process.argv[2] || 'xxx.csv'
// 輸出文件名
let outputFileName = process.argv[3] || 'result.csv'
// 輸出文件流
const writer = fs.createWriteStream(outputFileName)

// 把supsuperagent 包裝為promise 供co使用
// 使用async.retry 在發生錯誤的情況下重試5次
function getUrlPromise(url) {
    return new Promise((resolve, reject) => {
        async.retry(5, (cb) => {
            superagent.get(url)
                .end((err, res) => {
                    if (err) {
                        cb(err)
                    } else {
                        cb(null, res)
                    }
                })
        }, (err, res) => {
            if (err) {
                resolve('{}') // 重連5次后,仍然錯誤, 也不要拋出錯誤,避免程序終止
            } else {
                resolve(res.text)
            }
        })
    })
}




co(function* () {
    // 讀取文件
    let content = yield fs.readFileAsync(inputFileName, 'utf-8')
    // 分割得到行
    let lineArr = content.split(os.EOL)
    // 從行中得到ip
    let ipArr = []
    for(line of lineArr) {
        let itemArr = line.split(',') // 確保ip在最后一列
        let ip = itemArr.pop()
        ipArr.push(ip)
    }

    // 構造請求列表
    let reqs = ipArr.map(function* (ip) {
        
        let url = `${urltaoip}${ip}`
        console.log(url)
        return yield getUrlPromise(url)
    })
    // 並發不超過100 來請求, 百度限制 一分鍾 6000個並發請求
    let res = yield parallel(reqs, 100)



    // 輸出結果
    for(let i = 0; i < res.length; i++) {
        let ipObjStr = res[i]
        let ipObj = JSON.parse(ipObjStr)
        let address =ipObj && ipObj.content && ipObj.content.address_detail
        
        if (!address) {
            failCount++;
            address = {
                province: '獲取失敗',
                city: '獲取失敗',
                district: '獲取失敗'
            }
        }
        console.log(address.province)
        console.log(address.city)
        console.log(address.district)
        
        writer.write(lineArr[i])
        writer.write(`,${address.province || '無數據'},${address.city || '無數據'},${address.district || '無數據'}${os.EOL}`)
    }
    
    

    

})
.catch(err => {
    console.log(err)
}) 
.then(() => {
    console.log('失敗個數' + failCount)
})




參考文獻:

  1. 淘寶IP地址庫
  2. 百度普通IP定位API
  3. NodeJS實現批量查詢地理位置經緯度接口
  4. Generator 函數的異步應用
  5. async.retry


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM