去年年底,公司需要建个本地的手机号码归属地库,因为有点时间,又爱好python,就主动说来搞定这个。活接下来了,那么怎么实施呢?分为四步:
- 所有手机号码的获取
- 归属地的查询来源
- 请求数据的组装,返回数据的解析
- 有用数据的落地
一:号码的获取
国内手机号码十亿级别的,茫茫多的号码,一个一个获取肯定是不现实的,想想数据表得多大?
那怎么办呢?通过百度百科确认,要查归属地,只要根据前七位就ok了。那么数据只要 ‘151 5220 XXXX’这样就可以了,这样数据量可以压缩一万倍!然后搜索到目前国内三大运营商下面的手机号段分别有:
电信:
133、153、180、181、189、177、173、149
联通:
130、131、132、155、156、145、185、186、176、175
移动:
1340-1348、135、136、137、138、139、150、151、152、157、158、159、182、183、184、187、188、147、178
共37个号码段。所以我们只要37万条数据就ok了,而不是37亿条!
二:归属地的查询来源
国内靠谱点的手机号码归属地查询网站是?
国内主要的有ip138,114百事通,手机在线等,有些是收费的,有些返回整个html,综合下来:
适合我的是手机在线v.shouji.com,可免费且返回数据量很小!
三:请求数据的组装,返回数据的解析
然后在该网站上输入手机号码查询归属地,并开启charles抓包。
通过抓包分析,请求为:
http://v.showji.com/Locating/showji.com2016234999234.aspx?m=13900008888&output=json&callback=querycallback×tamp=1493796438586
其中m为手机号码,output为数据格式json,callback为返回动作,timestamp为时间戳,so只要替换其中的m,就能获取新的手机归属地了!
通过抓包分析,响应为:
querycallback({
"Mobile": "13900008888",
"QueryResult": "True",
"TO": "中国移动",
"Corp": "中国移动",
"Province": "新疆",
"City": "乌鲁木齐",
"AreaCode": "0991",
"PostCode": "830000",
"VNO": "",
"Card": ""
});
在querycallback()里面是一个json格式的数据包。对应有手机号、运营商、省份、城市等有用信息,Nice啊!这就是我需要的。
四:有用数据的落地
重点是落地,落到哪里去,最好的最便捷的还是使用python直接支持的sqlite3啦,轻量快捷!
五:代码实现
#coding:utf-8
import sys
import urllib2
import sqlite3
import json
import time
import re
class SQLITETool:
def __init__(self,databaseName):
self.databaseName = databaseName
self.create_db()
def create_db(self):
conn = sqlite3.connect(self.databaseName)
conn.close();
def execute_table(self,sql):
conn = sqlite3.connect(self.databaseName);
cursor = conn.cursor();
try:
cursor.execute(sql)
except Exception, e:
print(Exception,":",e)
finally:
cursor.close()
conn.commit()
conn.close()
class PhoneInfoSpider:
def __init__(self,databaseName,phoneSections):
self.phoneSections = phoneSections
self.sqlTool = SQLITETool(databaseName)
def phoneInfoHandler(self,jsonData):
mobile = jsonData['Mobile'];
corp = jsonData['Corp'];
province = jsonData['Province'];
city = jsonData['City'];
try:
sql = 'insert into phone_info_table (mobile, corp, province, city) values(\'{0}\',\'{1}\',\'{2}\',\'{3}\')'.format(mobile,corp,province,city);
self.sqlTool.execute_table(sql)
except Exception,e:
print(Exception,":",e)
def requestPhoneInfo(self,phoneNum):
print(phoneNum);
try:
#因为有20次/min的ip限制,所以sleep 3s
time.sleep(3);
response = urllib2.urlopen('http://v.showji.com/Locating/showji.com2016234999234.aspx?m={0}&output=json&callback=querycallback×tamp=1484546664567'.format(phoneNum))
resStr = response.read()
jsonStr = re.search(r'querycallback\((.*?)\);',resStr,re.S).group(1)
jsonData = json.loads(jsonStr)
self.phoneInfoHandler(jsonData)
except Exception,e:
print(Exception,":",e)
def requestAllSections(self):
#last用于接上次异常退出前的号码
last = 0
#自动生成手机号码,后四位补0
for head in self.phoneSections:
for i in range(last,10000):
middle = str(i).zfill(4)
phoneNum = head+middle+"0000"
self.requestPhoneInfo(phoneNum)
last = 0
if __name__ == '__main__':
reload(sys);
sys.setdefaultencoding('utf-8');
#134,135 '136','137','138','139','150','151','152',133','153','180','181','189','177',173','149','182','183','184','178'
#'157','158','159','187','188','147', '130','131','132','155','156','185','186','145','176'
#要爬的号码段
yys = ['153','180','181','189','177','173','149','182','183','184','178'];
spider = PhoneInfoSpider('phoneInfo.db',yys)
sql = 'CREATE TABLE phone_info_table (mobile varchar(11) primary key,corp varchar(32),province varchar(16), city varchar(32));'
spider.sqlTool.execute_table(sql)
spider.requestAllSections()