功能介紹
- 實(shí)現(xiàn)提供<b>快遞單號(hào)</b>和<b>快遞公司</b>查詢快遞軌跡
- 實(shí)現(xiàn)<b>二維碼</b>和<b>條形碼</b>掃描查詢
- 實(shí)現(xiàn)對(duì)個(gè)人查詢記錄保存
- 實(shí)現(xiàn)市場常見的國內(nèi)與國際快遞電話的查詢與撥打
-
運(yùn)行截圖
運(yùn)行截圖
前期準(zhǔn)備
- 快遞api的選擇:
目前提供免費(fèi)的快遞api查詢的主要有快遞鳥股冗,快遞100等。在本文中掀亩,我選擇了快遞鳥作為我的快遞api查詢接口。主要原因是:快遞鳥對(duì)常見的快遞(順豐鳖敷,圓通丹泉,中通,韻達(dá)等)都提供免費(fèi)查詢馁菜,而快遞100則需要通過其他方式贡珊,自助定制性不強(qiáng)最爬。
具體注冊(cè)步驟可以參見官網(wǎng)教程。 - 快遞電話名稱信息的獲让挪怼:
在App的第三個(gè)View中爱致,實(shí)現(xiàn)了對(duì)常見快遞客服電話的查詢撥打,這里的信息通過網(wǎng)絡(luò)爬蟲獲取固歪。
快遞電話地址 - 爬蟲用到的工具:urllib2+BeautifulSoup
- 本文我這里為了圖省事蒜鸡,直接將上述網(wǎng)頁中的源代碼保存到代碼中存儲(chǔ)為html_doc進(jìn)行處理。
#-*- coding:utf-8 -*-
from bs4 import BeautifulSoup
html_doc="""這是上述網(wǎng)頁的源代碼"""
soup = BeautifulSoup(html_doc,"html.parser")
#用來存儲(chǔ)快遞名稱的數(shù)組
express = []
#用來存儲(chǔ)快遞電話號(hào)碼的數(shù)組
phoneNum = []
#快遞名稱都位于標(biāo)簽<h4>中
#去掉網(wǎng)頁中位于快遞名稱后的“電話”兩個(gè)字并保存在express中
for li in soup.find_all('h4'):
data = str(li.get_text()).replace("電話","")
express.append(data)
#快遞電話位于標(biāo)簽<b>中
for li in soup.find_all('b'):
data = li.get_text()
phoneNum.append(data)
expressLen = len(express)
#輸出到控制臺(tái)上
for i in range(expressLen):
print('express'+str(i)+',',end="")
- 處理中遇到的問題:
- 開始使用Python2.7牢裳,中文處理總出問題逢防,編碼解碼網(wǎng)上也查了很多資料,最后還是放棄專用了3.4蒲讯,一試就成功忘朝;
- Python2.7 和 Python3.4 可以在電腦上共存,由于mac os中某些系統(tǒng)程序會(huì)用到2.7判帮,因此將其保留局嘁。在控制臺(tái)中執(zhí)行<b>.py</b>程序時(shí)溉箕,只需要<b>Python3 xxx.py</b>即可。
程序?qū)崿F(xiàn)
代碼放在Github上了悦昵,歡迎folk肴茄,歡迎star
程序代碼
-
程序示意圖
iOS快遞模塊.png
程序界面
程序界面
- 查詢模塊:
- 用戶輸入:快遞單號(hào)(Required),快遞公司(Required)但指,快遞備注(Optional,用戶在快遞歷史模塊提供備注查看的作用)寡痰;
- 獲取用戶輸入的快遞單號(hào)和快遞公司信息,將請(qǐng)求的參數(shù)按照快遞鳥官方要求的格式處理(在這里會(huì)用到MD5加密以及Base64編碼)棋凳;
- 借助第三方庫:AFNetworking 拦坠,將第二步中處理好的數(shù)據(jù)POST給快遞鳥,快遞鳥返回Json數(shù)據(jù)剩岳;
- 處理返回的Json數(shù)據(jù)并且顯示物流信息贞滨;
- 用戶點(diǎn)擊“保存”按鈕,將本單的物流信息保存到plist文件當(dāng)中實(shí)現(xiàn)數(shù)據(jù)的持久化存儲(chǔ)拍棕。
- 用戶還可以使用二維碼或條形碼掃描功能晓铆,實(shí)現(xiàn)自動(dòng)識(shí)別快遞單號(hào)。
利用AFNetworking POST網(wǎng)絡(luò)參數(shù)
//網(wǎng)絡(luò)請(qǐng)求的部分代碼
-(IBAction)expressSearch:(id)sender {
//1.編寫上傳參數(shù)
//獲取快遞公司對(duì)應(yīng)的編號(hào)
NSString* shipperCode = shipperCodeUser;
//獲取快遞單號(hào)
NSString* logisticCode = self.expressNum.text;
//根據(jù)快遞鳥api請(qǐng)求的要求格式進(jìn)行數(shù)據(jù)的處理
//其中eBusinessID,appKey是快遞鳥api提供
//reqURL:請(qǐng)求的網(wǎng)址绰播,同樣是由快遞鳥api提供
NSString* requestData = [NSString stringWithFormat:@"{\"OrderCode\":\"\",\"ShipperCode\":\"%@\",\"LogisticCode\":\"%@\"}",shipperCode,logisticCode];
NSMutableDictionary* params = [[NSMutableDictionary alloc]init];
NSString* dataSignTmp = [[NSString alloc]initWithFormat:@"%@%@",requestData,appKey];
//這里使用到了MD5加密程序尤蒿,以及Base64編碼
NSString* dataSign = [[MD5 md5String:dataSignTmp] base64String];
[params setObject:[requestData stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:@"RequestData"];
[params setObject:eBusinessID forKey:@"EBusinessID"];
[params setObject:@"1002" forKey:@"RequestType"];
[params setObject:[dataSign stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:@"DataSign"];
[params setObject:@"2" forKey:@"DataType"];
//2.上傳參數(shù)并獲得返回值
AFHTTPSessionManager* manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager POST:reqURL parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"請(qǐng)求成功:%@",responseObject);
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:nil];
//3. 獲得網(wǎng)絡(luò)數(shù)據(jù)賦值給ExpressInfo對(duì)象
NSMutableArray* expressTraces = [[NSMutableArray alloc]init];
for (NSDictionary* traces in [json objectForKey:@"Traces"]) {
[expressTraces insertObject:traces atIndex:0];
}
NSString* shipperCode = [json objectForKey:@"ShipperCode"];
NSString* logisticCode = [json objectForKey:@"LogisticCode"];
NSString* expressForUser = self.expressForUser.text;
ExpressInfo* express = [[ExpressInfo alloc]initWitfShipperCode:shipperCode andlogisticCode:logisticCode andexpressForUser:expressForUser andexpressTraces:expressTraces];
//4. 傳遞數(shù)據(jù)給ExpresstracesViewController
//獲取的物流數(shù)據(jù)在ExpressTracesViewController中顯示
ExpressTracesViewController* expressTracesVC = [[ExpressTracesViewController alloc]init];
expressTracesVC.express = express;
self.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:expressTracesVC animated:YES];
self.hidesBottomBarWhenPushed = NO;
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"請(qǐng)求失敗:%@",error.description);
}];
}
向Plist中讀取并寫入數(shù)據(jù)
//保存數(shù)據(jù)到plist中的代碼
-(void)saveExpressTraces{
//1. 獲得文件路徑
NSString* path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString* fileName = [path stringByAppendingPathComponent:@"123.plist"];
//2. 獲得本單快遞信息
NSArray* array = [NSArray arrayWithObjects:shipperCode,logisticCode,expressForUser,expressTraces, nil];
//3. 獲取已經(jīng)保存的快遞信息
NSMutableArray* arrayCollection = [[NSMutableArray alloc]init];
NSArray *result = [ NSArray arrayWithContentsOfFile:fileName];
for (NSArray* obj in result) {
[arrayCollection addObject:obj];
}
//4. 將本單快遞信息追加到已有的信息中
[arrayCollection addObject:array];
//5. 存儲(chǔ)
[arrayCollection writeToFile:fileName atomically:YES];
//6. 提示框
UIAlertView* alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"保存成功" delegate:self cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
[alert show];
}
選擇iOS系統(tǒng)相冊(cè)圖片
//獲取相冊(cè)照片
// 1.判斷相冊(cè)是否可以打開
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) return;
// 2. 創(chuàng)建圖片選擇控制器
UIImagePickerController *ipc = [[UIImagePickerController alloc] init];
ipc.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
// 3.設(shè)置代理
ipc.delegate = self;
// 4.modal出這個(gè)控制器
[self presentViewController:ipc animated:YES completion:nil];
//實(shí)現(xiàn)相關(guān)協(xié)議
#pragma mark -------- UIImagePickerControllerDelegate---------
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
// 1.取出選中的圖片
UIImage *pickImage = info[UIImagePickerControllerOriginalImage];
NSData *imageData = UIImagePNGRepresentation(pickImage);
CIImage *ciImage = [CIImage imageWithData:imageData];
// 2.從選中的圖片中讀取二維碼數(shù)據(jù)
// 2.1創(chuàng)建一個(gè)探測器
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyLow}];
// 2.2利用探測器探測數(shù)據(jù)
NSArray *feature = [detector featuresInImage:ciImage];
// 2.3取出探測到的數(shù)據(jù)
for (CIQRCodeFeature *result in feature) {
NSLog(@"%@",result.messageString);
NSString *urlStr = result.messageString;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlStr]];
}
// 注意: 如果實(shí)現(xiàn)了該方法, 當(dāng)選中一張圖片時(shí)系統(tǒng)就不會(huì)自動(dòng)關(guān)閉相冊(cè)控制器
[picker dismissViewControllerAnimated:YES completion:nil];
}
利用攝像頭捕捉二維碼或者條形碼
#pragma mark --------AVCaptureMetadataOutputObjectsDelegate ---------
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
AVMetadataMachineReadableCodeObject *object = [metadataObjects lastObject];
if (object == nil) return;
// 只要掃描到結(jié)果就會(huì)調(diào)用
self.customLabel.text = object.stringValue;
NSDictionary* userInfo = [NSDictionary dictionaryWithObject:object.stringValue forKey:@"userInfo"];
[self clearLayers];
[self dismissViewControllerAnimated:YES completion:^{
[[NSNotificationCenter defaultCenter]postNotificationName:@"do" object:self userInfo:userInfo];
}];
/*
將secendView dismissViewControllerAnimated掉幅垮,然后自動(dòng)注冊(cè)一個(gè)名為do的通知
注冊(cè)了這個(gè)名為的通知,你就可以在任何.m文件里面通過以下代碼調(diào)用到了:
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(handleColorChange:)
name:@"do"
object:nil];
上面的代碼的意思就是尾组,先找到已經(jīng)注冊(cè)過的名為do的通知忙芒,然后再自動(dòng)調(diào)用handleColorChange去處理
*/
// 2.對(duì)掃描到的二維碼進(jìn)行描邊
AVMetadataMachineReadableCodeObject *obj = (AVMetadataMachineReadableCodeObject *)[self.previewLayer transformedMetadataObjectForMetadataObject:object];
[self drawLine:obj];
}
- 快遞歷史模塊:
- 獲取plist中的物流信息;
- 顯示到tableview中讳侨。
//獲取plist文件路徑
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
//得到123.plist文件
NSString *fileName = [path stringByAppendingPathComponent:@"123.plist"];
//獲取123.plist文件中的返回值
NSArray *result = [ NSArray arrayWithContentsOfFile:fileName];
//將返回值復(fù)制給(NSMutableArray* )expressHistory
expressHistory = [NSMutableArray arrayWithArray:result];
- 快遞電話模塊
- 新建一個(gè)快遞模型Class:ExpressPhoneNum.其中有兩個(gè)成員變量呵萨,快遞名稱和快遞電話;
- 將獲取到的快遞信息存儲(chǔ)為ExpressPhoneNum類型
- 將數(shù)據(jù)顯示到tableview
- 點(diǎn)擊某行實(shí)現(xiàn)撥打電話的功能
//點(diǎn)擊行打電話
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//獲取點(diǎn)擊的Section
NSArray* expressXX = self.expressArray[indexPath.section];
//獲取點(diǎn)擊的快遞信息
ExpressPhoneNum* expressXXX = expressXX[indexPath.row];
NSString* expressPhoneNumber = expressXXX.expressNum;
NSMutableString* str = [[NSMutableString alloc]initWithFormat:@"tel:%@",expressPhoneNumber];
//實(shí)現(xiàn)撥號(hào)功能
UIWebView* callWebView = [[UIWebView alloc]init];
[callWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:str]]];
[self.view addSubview:callWebView];
NSLog(@"打電話翱缈纭潮峦!");
}
下一步的工作
- 界面美化:程序當(dāng)中所有的控件都是系統(tǒng)默認(rèn)的,未做任何修改勇婴;
- 程序整體功能執(zhí)行沒問題忱嘹,但存在個(gè)別bug,有待優(yōu)化
參考資料
- Base64編碼:ekscrypto/Base64
- MD5 加密:MD5加密
- 二維碼掃描:JustKeepRunning/LXDScanQRCode