SpringBoot實現(xiàn)根據(jù)手機號獲取歸屬地
最近在做公司需求時,甲方一會提出根據(jù)IP獲取所在地,一會有提出根據(jù)手機號獲取手機號所在地。真的是:需求時時變,累死程序猿。甲方才是爸爸。
之前已經(jīng)實現(xiàn)過根據(jù)IP獲取所在地的幾種方式,大家可以參考我之前寫的文章下:《SpringBoot通過ip獲取歸屬地的幾種方式》。
那么今天我們來看看根據(jù)手機號有哪些方式可以獲取歸屬地呢?
廢話不多說,開擼!
1、基于libphonenumber
libphonenumber:是谷歌提供的一款用于解析、格式化和校驗國際手機號碼的軟件庫。它提供了三個包,分別對應(yīng)不同的功能。
libphonenumber
:用于校驗手機號的正確性,提供了:getNumberType,isNumberMatch ,getExampleNumber 等方法。
carrier
:用于獲取手機號的供應(yīng)商。通過初始化PhoneNumberToCarrierMapper ,調(diào)用getNameForNumber可獲取運營商信息。
geocoder
:用于獲取手機號的歸屬地。通過初始化PhoneNumberOfflineGeocoder ,調(diào)用getDescriptionForNumber方法可獲取手機歸屬地。
下面我們來說說具體實現(xiàn)。
引入包:
<dependency> <groupId>com.googlecode.libphonenumber</groupId> <artifactId>libphonenumber</artifactId> <version>8.13.26</version> </dependency> <dependency> <groupId>com.googlecode.libphonenumber</groupId> <artifactId>carrier</artifactId> <version>1.210</version> </dependency> <dependency> <groupId>com.googlecode.libphonenumber</groupId> <artifactId>geocoder</artifactId> <version>2.220</version> </dependency>
1.1 編寫工具
引入libphonenumber所有包后,我們編寫一個工具類,實現(xiàn)手機校驗,獲取供應(yīng)商,歸屬地等信息。
/** * @author: jiangjs * @description: 基于google的libphonenumber將手機號轉(zhuǎn)成地區(qū)及供應(yīng)商信息 * @date: 2023/11/30 14:33 **/ public class PhoneToRegionUtil { /** * 手機號基本工具類 */ private final static PhoneNumberUtil PHONE_NUMBER_UTIL = PhoneNumberUtil.getInstance(); /** * 運營商 */ private final static PhoneNumberToCarrierMapper CARRIER_MAPPER = PhoneNumberToCarrierMapper.getInstance(); /** * */ private final static PhoneNumberOfflineGeocoder GEO_CODER = PhoneNumberOfflineGeocoder.getInstance(); /** * 驗證當(dāng)前手機號是否有效 * @param phone 手機號 * @return 校驗結(jié)果 */ public static boolean isValidNumber(String phone){ return PHONE_NUMBER_UTIL.isValidNumber(getPhoneNumber(phone)); } /** * 獲取手機號運營商 * @param phone 手機號 * @return 運營商 */ public static String getPhoneCarrier(String phone){ return isValidNumber(phone) ? CARRIER_MAPPER.getNameForNumber(getPhoneNumber(phone), Locale.CHINA) : ""; } /** * 獲取手機號歸屬地 * @param phone 手機號 * @return 歸屬地 */ public static String getRegionInfoByPhone(String phone){ return isValidNumber(phone) ? GEO_CODER.getDescriptionForNumber(getPhoneNumber(phone),Locale.CHINESE) : ""; } /** * 生成PhoneNumber * @param phone 手機號 * @return PhoneNumber */ private static Phonenumber.PhoneNumber getPhoneNumber(String phone){ Phonenumber.PhoneNumber phoneNumber = new Phonenumber.PhoneNumber(); phoneNumber.setCountryCode(86); phoneNumber.setNationalNumber(Long.parseLong(phone)); return phoneNumber; } /** * 獲取手機號的歸屬信息:運營商,歸屬地 * @param phone 手機號 * @return 歸屬信息 */ public static JSONObject getPhoneAffiliationInfo(String phone){ JSONObject affiliation = new JSONObject(); affiliation.put("phone",phone); affiliation.put("carrier",getPhoneCarrier(phone)); affiliation.put("region",getRegionInfoByPhone(phone)); return affiliation; } }
其中,getPhoneNumber創(chuàng)建每個手機號的Phonenumber.PhoneNumber,供其他接口調(diào)用。同時在調(diào)用運營商等接口時先進(jìn)行手機號的校驗。
在上面的接口中,我們會發(fā)現(xiàn)創(chuàng)建Phonenumber.PhoneNumber時,會使用setCountryCode方法去設(shè)置所在國家的電話區(qū)號,我們有時候復(fù)制手機號會發(fā)現(xiàn)前面是86,而86就是代表我們國家。每個國家有每個國家電話代號,其他國家代號,小伙伴們可以參考國際電信聯(lián)盟根據(jù) E.164 標(biāo)準(zhǔn) 分配給各國或特殊行政區(qū)的代碼。
1.2 獲取歸屬地
已經(jīng)封裝了工具類,那么接下來我們就測試一下,用手機號試試能不能獲取歸屬信息。
我們直接在Controller層中編寫接口:
@GetMapping("/getPhoneAffiliationInfo.do/{phone}") public JsonResult<?> getPhoneAffiliationInfo(@PathVariable("phone") String phone){ return JsonResult.success(PhoneToRegionUtil.getPhoneAffiliationInfo(phone)); }
在瀏覽器中輸入地址,添加號碼:
通過測試,引用谷歌提供的包,可以解決我們的需求。
哈哈,可以不用加班.......
2、基于CSV文件
雖然引入谷歌的可以搞定需求了,但是作為程序員總要想想還有沒有其他方式實現(xiàn)?這不又找一種方式。哈哈
其實我們的手機號是有規(guī)律可循的:
1、前3位:前三位的數(shù)字,其實代表的是運營商。不同的運營商會提供不同的號段。比如:我的手機號是135開頭就是移動提供的。移動除了提供135號段外,還有其他各種號段,如134,137等;聯(lián)通則提供了:130,131等號段;電信呢,提供了133,153等號段。
2、前7位:前7位則是可以確定手機號的歸屬地,例如:我的手機號前7位是1350154,則可以確定是廣東省廣州市。
既然我們知道了手機號的一些規(guī)律,那么如果有一份這樣的文檔,我們是不是就可以基于這份文檔進(jìn)行歸屬地的查詢呢?
還真有這樣的一份文檔,我在網(wǎng)上找到一份4年前的CSV文檔。如圖:
既然有這份文檔那我們就好實現(xiàn)了該功能。
2.1 讀取CSV文件
只所以寫讀取CSV文件,是因為讀取到這些信息后,想怎么查詢就由我們自己說了算了。可以將數(shù)據(jù)存儲到數(shù)據(jù)庫查詢,也可以放在redis中查詢。下面我們基于redis的查詢來實現(xiàn)歸屬功能。
將CSV文件讀取到redis中。
/** * @author: jiangjs * @description: 服務(wù)啟動后,加載數(shù)據(jù)到緩存 * @date: 2023/12/9 15:32 **/ @ConditionalOnProperty(havingValue = "true",value = "phoneToRegion.enabled") @Component public class ReadRegionToRedisStart implements CommandLineRunner { private final static String REGION_CSV_PATH = "classpath:/static/region/phonetmp.csv"; private final static String PHONE_REGION_KEY = "country_phone_region_info"; @Resource private ResourceLoader resourceLoader; @Resource private RedisTemplate<String,Object> redisTemplate; @Override public void run(String... args) { long size = redisTemplate.opsForHash().size(PHONE_REGION_KEY); if (size <= 0){ try (InputStream ism = resourceLoader.getResource(REGION_CSV_PATH).getInputStream()){ Assert.notNull(ism,"讀取手機號信息文件為空"); BufferedReader reader = new BufferedReader(new InputStreamReader(ism)); String line = reader.readLine(); while (StringUtils.isNoneBlank(line)){ String[] lineVal = line.split(","); RegionVo regionVo = new RegionVo(); regionVo.setPhonePrefix(lineVal[0]).setProvince(lineVal[1]).setCity(lineVal[2]).setCarrier(lineVal[3]); redisTemplate.opsForHash().put(PHONE_REGION_KEY,lineVal[0],regionVo); line = reader.readLine(); } }catch (Exception e){ e.printStackTrace(); throw new RuntimeException("獲取手機號信息報錯"); } } } }
我們在系統(tǒng)啟動后,自動將CSV數(shù)據(jù)加載到redis中,當(dāng)然通過@ConditionalOnProperty可以來自行決定要不要加載到內(nèi)存中。不知道@ConditionalOnProperty注解使用的小伙伴去我的主頁可以找到這篇文章來了解。
2.2 創(chuàng)建工具
數(shù)據(jù)被加載到內(nèi)存后,那么我們就可以編寫工具類來進(jìn)行獲取手機歸屬地。
/** * @author: jiangjs * @description: 讀取CSV文件,根據(jù)手機號前7位進(jìn)行匹配 * @date: 2023/11/30 14:54 **/ @Component public class PhoneToRegionCsvUtil { private final static String PHONE_REGION_KEY = "country_phone_region_info"; @Resource private RedisTemplate<String,Object> redisTemplate; /** * 根據(jù)手機號獲取手機歸屬地 * @param phone 手機號 * @return 歸屬地信息 */ public RegionVo getPhoneToRegion(String phone){ String prefix = StringUtils.substring(phone, 0, 7); Object region = redisTemplate.opsForHash().get(PHONE_REGION_KEY, prefix); return Objects.isNull(region) ? new RegionVo() : (RegionVo) region; } }
2.3 獲取歸屬地
有了工具類,那我們來測試一下。
直接在Controller層中編寫接口:
@Resource private PhoneToRegionCsvUtil phoneToRegionCsvUtil; @GetMapping("/getPhoneGeoInfoByCsv.do/{phone}") public JsonResult<?> getPhoneGeoInfoByCsv(@PathVariable("phone") String phone){ return JsonResult.success(phoneToRegionCsvUtil.getPhoneToRegion(phone)); }
瀏覽器中訪問:
至此我們也可以正常的獲取到手機號的歸屬地。
3、頁面抓取
頁面抓取這種方式,跟我之前的《SpringBoot通過ip獲取歸屬地的幾種方式》中的頁面抓取方式是一樣的,在這就不跟大家詳細(xì)介紹了。
總結(jié)
文中介紹了三種方式進(jìn)行手機號查詢歸屬地的方式。
第一種:基于谷歌提供的國際解析包,引入后不用額外引入其他的東西,只需要寫工具類即可,查詢速度也比較快。
第二種:基于CSV文件的,不用額外引入具體的包,但是要引入CSV文件,大小在12M多,當(dāng)然也可以將文件放在磁盤里,這樣不用擔(dān)心部署包過大。如果是基于內(nèi)存查詢的話,則需要依賴redis,增加了難度。
第三種:這個就不推薦了,畢竟依賴于第三方,如果服務(wù)掛了的話就沒法使用了。如果用戶量大的話,很可能會被第三方......,大家都懂的。
我在應(yīng)用就是使用了第一種方式。
以上就是SpringBoot實現(xiàn)根據(jù)手機號獲取歸屬地的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot手機號獲取歸屬地的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot返回前端Long類型字段丟失精度問題及解決方案
Java服務(wù)端返回Long整型數(shù)據(jù)給前端,JS會自動轉(zhuǎn)換為Number類型,本文主要介紹了SpringBoot返回前端Long類型字段丟失精度問題及解決方案,感興趣的可以了解一下2024-03-03多線程-lock與lockInterruptibly的區(qū)別及說明
文章主要討論了Java中ReentrantLock的lock和lockInterruptibly方法的區(qū)別,以及AQS中的雙向鏈表設(shè)計,lock方法不響應(yīng)中斷,而lockInterruptibly方法會響應(yīng)中斷,AQS的雙向鏈表設(shè)計使得線程管理更加高效和靈活,適用于高并發(fā)場景2025-02-02