2020-02-17-Demo之iOS使用CoreData

Demo之iOS使用CoreData

准备

Xcode 添加 Coredata.framework 依赖库
确认是否存在 xxxCoreData.xcdatamodeld , 没有就新建一个, 在AppDelegate里可能会新出现一些代码
打开 xxxCoreData.xcdatamodeld 确认以下
ENTITIES里添加xxxEntity(也就是类obj),并添加属性及属性类型
CONFIGURATIONS里确认Entity及Class是否存在上一行的那个xxxEntity,其abstract不勾选(Demo用不上)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#import <CoreData/CoreData.h>
#import "AppDelegate.h"
#import "xxxCoreData+CoreDataModel.h"
#import "xxxEntity+CoreDataClass.h"
#import "xxxEntity+CoreDataProperties.h"
<UIApplicationDelegate>
@property (retain, strong) NSPersistentContainer *persistentContainer;
/** NSManagedObjectContext */
@property(nonatomic, strong)NSManagedObjectContext *managedObjectContext;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self initCoreData];
[self insert];
[self tapSearchBtn];
}
// 懒加载
- (NSManagedObjectContext *)managedObjectContext {
// 因为在AppDelegate中已经实现过了,所以在这里是从AppDelegate中去获取
if (!_managedObjectContext) {
// 获取appDelegate对象
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
_managedObjectContext = appDelegate.persistentContainer.viewContext;
}
return _managedObjectContext;
}
-(void)initCoreData {
NSError *error; //Path to sqlite file.
NSString *path = [NSHomeDirectory() stringByAppendingString:@"/Documents/xxxEntity.sqlite"];
NSURL *url = [NSURL fileURLWithPath:path]; //init the model
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil]; //Establish the persistent store coordinator
NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel];
if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error])
{
NSLog(@"Error %@",[error localizedDescription]);
}else{
self.managedObjectContext = [[NSManagedObjectContext alloc ] initWithConcurrencyType:NSMainQueueConcurrencyType];
[self.managedObjectContext setPersistentStoreCoordinator:persistentStoreCoordinator];
}
}
- (xxxEntity *)insert {
NSError *error = nil;
if (self.managedObjectContext == nil) { NSLog(@"managedObjectContext is nil"); return nil; }
xxxEntity * hfile = [NSEntityDescription insertNewObjectForEntityForName:@"xxxEntity" inManagedObjectContext:self.managedObjectContext];
hfile.fileversion = @"version1";
hfile.filename = [NSString stringWithFormat:@"%u", arc4random() % 1000];
hfile.filepath = @"path1";
if ([self.managedObjectContext hasChanges] && ![self.managedObjectContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
return nil;
} else {
NSLog(@"Unresolved error %@", @"插入数据成功");
}
return hfile;
}
- (NSArray *)search {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"xxxEntity"];
// 执行获取操作,获取所有Student托管对象
NSError *error = nil;
NSArray<xxxEntity *> *hfileArray = [self.managedObjectContext executeFetchRequest:request error:&error];
if (error) {
NSLog(@"%@", [NSString stringWithFormat:@"CoreData Error:%@",error.description]);
} else {
NSLog(@"查找数据成功 %@", NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES));
for (xxxEntity *hfile in hfileArray) {
NSLog(@"%@ %@ %@", hfile.fileversion, hfile.filename, hfile.filepath);
}
}
return hfileArray;
}
// 获取数据的请求对象,指明对实体进行查询操作
- (void)tapSearchBtn {
NSArray *sts = [self search];
if (sts.count == 0) {
return;
}
// 遍历
//[self.dataSources removeAllObjects];
[sts enumerateObjectsUsingBlock:^(xxxEntity * obj, NSUInteger idx, BOOL *stop) {
//[self.dataSources addObject:obj];
NSLog(@"%@", obj);
}];
//[self.tableView reloadData];
}

以下代码是xcode直接生成在AppDelegate里的,不需要重复添加,列在这里只是备个份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#pragma mark - Core Data stack

@synthesize persistentContainer = _persistentContainer;

- (NSPersistentContainer *)persistentContainer {
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
@synchronized (self) {
if (_persistentContainer == nil) {
_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"xxxCoreData"];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
if (error != nil) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}];
}
}

return _persistentContainer;
}

#pragma mark - Core Data Saving support

- (void)saveContext {
NSManagedObjectContext *context = self.persistentContainer.viewContext;
NSError *error = nil;
if ([context hasChanges] && ![context save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}

2020-02-15-AFNetworking添加SecurityPolicy解决上传文件时https请求Status Code 200(正常)Request failed unacceptable content-type问题

使用 https://github.com/AFNetworking/AFNetworking 上传文件时https请求Status Code 200(正常)Request failed unacceptable content-type问题
需要在原代码中加入以下codeblock来解决
最终代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer= [AFHTTPRequestSerializer serializer];
//往下 2020.1.14更新此codeblock
//添加securityPolicy
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
securityPolicy.validatesDomainName = NO;
securityPolicy.allowInvalidCertificates = YES;
manager.securityPolicy = securityPolicy;
//需要请求前面加上定制化的配置
manager.responseSerializer.acceptableContentTypes= [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html", nil];
//往上 2020.1.14更新此codeblock
NSDictionary *dict = @{@"key":@"id111111",@"type":@"file4what"};
NSMutableData *data = [NSMutableData data];
NSMutableData *fileData = [NSMutableData dataWithContentsOfFile:@"filePath"];
NSString *spaceString1 = @"\n\n---------------------------------------------------------1---------------------------------------------------------\n\n";
[data appendData:[spaceString1 dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:fileData];
NSString *spaceString2 = @"\n\n---------------------------------------------------------split line---------------------------------------------------------\n\n";
[data appendData:[spaceString2 dataUsingEncoding:NSUTF8StringEncoding]];
// 在网络开发中,上传文件时,是文件不允许被覆盖,文件重名
// 要解决此问题,
// 可以在上传时使用当前的系统事件作为文件名
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
// 设置时间格式
formatter.dateFormat = @"yyyyMMddHHmmss";
NSString *str = [formatter stringFromDate:[NSDate date]];
NSString *fileName = [NSString stringWithFormat:@"abc%@.log", str];
[manager POST:@"https://www.xxx.com/app/upload.php" parameters:dict constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
//上传
/*
此方法参数
1. 要上传的[二进制数据]
2. 对应网站上[upload.php中]处理文件的[字段"Filedata"]
3. 要保存在服务器上的[文件名]
4. 上传文件的[mimeType]
*/
[formData appendPartWithFileData:data name:@"Filedata" fileName:fileName mimeType:@"text/html"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
NSLog(@"%f",1.0 * uploadProgress.completedUnitCount / uploadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"上传日志成功 %@", responseObject);
//返回json
NSDictionary *jsonDic = responseObject;
//OK了开始处理
int ret = [jsonDic[@"code"] intValue];
if (ret == 0) {
NSString *xxx = jsonDic[@"xxx"];
//......
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"上传日志失败 %@", error);
}];

参考链接
https://www.jianshu.com/p/3bc4243e4c05
https://github.com/AFNetworking/AFNetworking

2020-02-13-Scrapy爬虫Demo_抓取出版社2020年春季学期使用教科书

(有删减)

源头及原因

http://xxx.com/jc/
出版社2020年春季学期使用教科书,方便在家自主学习。所以下载下来本地随时使用

Scrapy介绍

Scrapy is an application framework for crawling web sites and extracting structured data which can be used for a wide range of useful applications, like data mining, information processing or historical archival.

Scrapy安装

https://doc.scrapy.org/en/latest/intro/overview.html

爬虫创建及添加

创建   
scrapy startproject jiaocai  
生成爬虫  
cd jiaocai  
scrapy genspider jiaocai2020 http://xxx.com/jc  

修改 jiaocai/spiders/jiaocai2020.py 文件内容,如下:

# -*- coding: utf-8 -*-  
# Usage 使用方法: scrapy crawl jiaocai2020 -o xxx.csv  
# //img//@data-src' 图片类似于这样的 <img data-src='http://xxxx/images/2018.jpg'> in start_urls  
# //input//@data-src' 图片类似于这样的 <input data-src='http://xxxx/images/2018.jpg' type='image'> in start_urls  
# 因为考虑反爬虫的问题,建议URL地址已抓出来后,直接将所有地址放到迅雷里打包下载  

import scrapy  
from scrapy import Request  

class Jiaocai2020Spider(scrapy.Spider):  
    name = 'jiaocai2020'  
    # allowed_domains = ['http://xxx.com']  
    start_urls = ['http://xxx.com/jc/']  

    def parse(self, response):  
        print (response.url)  
        # for quote1 in response.xpath('//a/@href').extract():  
        # <li class="fl"><a href="./ywjygjkcjc/xdjc/" target="_blank">教科书</a></li>  
        for quote1 in response.xpath('//li[@class="fl"]//a/@href').extract():  
            # href="./ywjygjkcjc/xxdfjsys/"  
            ur11 = "http://xxx.com/jc/" + quote1[2:]  
            print (ur11)  
            # 'http://xxx.com/jc/zzwhjc/zzjyghjsys/',  
            yield scrapy.Request(ur11, callback=self.calbak, meta=None)  
            # yield scrapy.Request(ur11, callback=self.calbak, meta={'ur11': ur11})  


    def calbak(self, response):  
        print (response.url)  
        # kkp = response.meta['ur11']  
        # print (kkp)  
        # if "error" in response.body:  
        #     self.logger.error("error1r")  
        #     return  
        # <a href="./202002/P020200210281142511137.pdf" target="_blank" title="下载" class="btn_type_dl">下载</a>  
        for quote2 in response.xpath('//a[@class="btn_type_dl"]/@href').extract():  
            ur12 = response.url + quote2[2:]  
            # http://xxx.com/jc/zzwhjc/zzjyghjsys/202002/P020200211707082058148.pdf  
            print (ur12)  
            # 加 -o 参数时导出到文件时,会用到  
            yield {  
                'url' : ur12  
            }  

            # (直接下载图片到当前目录中,反爬严格的,可能会无法下载下来)  
            #  因为考虑反爬虫的问题,建议URL地址已抓出来后,直接将所有地址放到迅雷里打包下载  
            # if quote:  
            #     filename = os.path.basename(quote)  
            #     print filename  
            #     filepath=os.path.join("/Users/xxx/Desktop",filename)  
            #     urllib.urlretrieve(quote,filepath)  

            # 这个注释掉,需要时再用  
            # yield scrapy.Request(quote)  

使用

Usage 使用方法: scrapy crawl jiaocai2020 -o xxx.csv

一些注意事项

1, 因为考虑反爬虫的问题,建议URL地址已抓出来后,直接将所有地址放到迅雷里打包下载
2, Scrapy爬虫出现Forbidden by robots.txt
关闭scrapy自带的ROBOTSTXT_OBEY功能,在setting找到这个变量,设置为False即可解决
ROBOTSTXT_OBEY = False
3, Settings.py文件的一些修改项

USER_AGENT = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50"  
ROBOTSTXT_OBEY = False  
CONCURRENT_REQUESTS = 1  
DOWNLOAD_DELAY = 1  
DEFAULT_REQUEST_HEADERS = {  
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',  
  'Accept-Language': 'en',
}  
AUTOTHROTTLE_START_DELAY = 5  
AUTOTHROTTLE_MAX_DELAY = 60  

创建 main.py 自动化执行 (未完成)

创建 main.py 自动化执行, 点击运行即可爬数据, 文件放在项目根目录(即scrapy.cfg所在目录)
main.py代码如下

import scrapy  
from scrapy.cmdline import execute  
import os,sys  
sys.path.append(os.path.dirname(os.path.basename(__file__))) #注意,是爬虫名,不是项目名  
execute(['scrapy','crawl','jiaocai2020'])  

# from scrapy import cmdline  
# cmdline.execute("scrapy crawl jiaocai2020".split())   

参考链

https://www.jianshu.com/p/8d353c7cf606
https://zhuanlan.zhihu.com/p/32458936

2020-02-10-howto如何使用漂逸算法及边改iOS的swift语言Struct式的Framework边调App

howto如何使用漂逸算法及边改iOS的swift语言Struct式的Framework边调App

Transform coordinate between earth(WGS-84) and mars in china(GCJ-02).
火星坐标:我国自己加了飘逸搞的一套加密坐标 还原了飘逸算法

usage

1
2
3
import HameToolsFramework
let location = LocationTransform.gcj2wgs(gcjLat: 30.581237, gcjLng: 104.065644)
print(location)

Framework code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import Foundation
/**
* Struct transform coordinate between earth(WGS-84) and mars in china(GCJ-02).
*/
public struct LocationTransform {
static let EARTH_R: Double = 6378137.0
static func isOutOfChina(lat: Double, lng: Double) -> Bool {
if lng < 72.004 || lng > 137.8347 {
return true
}
if lat < 0.8293 || lat > 55.8271 {
return true
}
return false
}
static func transform(x: Double, y: Double) -> (lat: Double, lng: Double) {
let xy = x * y
let absX = sqrt(fabs(x))
let xPi = x * Double.pi
let yPi = y * Double.pi
let d = 20.0 * sin(6.0 * xPi) + 20.0 * sin(2.0 * xPi)
var lat = d
var lng = d
lat += 20.0 * sin(yPi) + 40.0 * sin(yPi / 3.0)
lng += 20.0 * sin(xPi) + 40.0 * sin(xPi / 3.0)
lat += 160.0 * sin(yPi / 12.0) + 320 * sin(yPi / 30.0)
lng += 150.0 * sin(xPi / 12.0) + 300 * sin(xPi / 30.0)
lat *= 2.0 / 3.0
lng *= 2.0 / 3.0
lat += -100 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * xy + 0.2 * absX
lng += 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * xy + 0.1 * absX
return (lat, lng)
}
static func delta(lat: Double, lng: Double) -> (dLat: Double, dLng: Double) {
let ee = 0.00669342162296594323
let radLat = lat / 180.0 * Double.pi
var magic = sin(radLat)
magic = 1 - ee * magic * magic
let sqrtMagic = sqrt(magic)
var (dLat, dLng) = transform(x: lng - 105.0, y: lat - 35.0)
dLat = (dLat * 180.0) / ((EARTH_R * (1 - ee)) / (magic * sqrtMagic) * Double.pi)
dLng = (dLng * 180.0) / (EARTH_R / sqrtMagic * cos(radLat) * Double.pi)
return (dLat, dLng)
}
/**
* wgs2gcj convert WGS-84 coordinate(wgsLat, wgsLng) to GCJ-02 coordinate(gcjLat, gcjLng).
*/
public static func wgs2gcj(wgsLat: Double, wgsLng: Double) -> (gcjLat: Double, gcjLng: Double) {
if isOutOfChina(lat: wgsLat, lng: wgsLng) {
return (wgsLat, wgsLng)
}
let (dLat, dLng) = delta(lat: wgsLat, lng: wgsLng)
return (wgsLat + dLat, wgsLng + dLng)
}
/**
* gcj2wgs convert GCJ-02 coordinate(gcjLat, gcjLng) to WGS-84 coordinate(wgsLat, wgsLng).
* The output WGS-84 coordinate's accuracy is 1m to 2m. If you want more exactly result, use gcj2wgs_exact.
*/
public static func gcj2wgs(gcjLat: Double, gcjLng: Double) -> (wgsLat: Double, wgsLng: Double) {
if isOutOfChina(lat: gcjLat, lng: gcjLng) {
return (gcjLat, gcjLng)
}
let (dLat, dLng) = delta(lat: gcjLat, lng: gcjLng)
return (gcjLat - dLat, gcjLng - dLng)
}
/**
* gcj2wgs_exact convert GCJ-02 coordinate(gcjLat, gcjLng) to WGS-84 coordinate(wgsLat, wgsLng).
* The output WGS-84 coordinate's accuracy is less than 0.5m, but much slower than gcj2wgs.
*/
public static func gcj2wgs_exact(gcjLat: Double, gcjLng: Double) -> (wgsLat: Double, wgsLng: Double) {
let initDelta = 0.01, threshold = 0.000001
var (dLat, dLng) = (initDelta, initDelta)
var (mLat, mLng) = (gcjLat - dLat, gcjLng - dLng)
var (pLat, pLng) = (gcjLat + dLat, gcjLng + dLng)
var (wgsLat, wgsLng) = (gcjLat, gcjLng)
for _ in 0 ..< 30 {
(wgsLat, wgsLng) = ((mLat + pLat) / 2, (mLng + pLng) / 2)
let (tmpLat, tmpLng) = wgs2gcj(wgsLat: wgsLat, wgsLng: wgsLng)
(dLat, dLng) = (tmpLat - gcjLat, tmpLng - gcjLng)
if (fabs(dLat) < threshold) && (fabs(dLng) < threshold) {
return (wgsLat, wgsLng)
}
if dLat > 0 {
pLat = wgsLat
} else {
mLat = wgsLat
}
if dLng > 0 {
pLng = wgsLng
} else {
mLng = wgsLng
}
}
return (wgsLat, wgsLng)
}
/**
* Distance calculate the distance between point(latA, lngA) and point(latB, lngB), unit in meter.
*/
public static func Distance(latA: Double, lngA: Double, latB: Double, lngB: Double) -> Double {
let arcLatA = latA * Double.pi / 180
let arcLatB = latB * Double.pi / 180
let x = cos(arcLatA) * cos(arcLatB) * cos((lngA-lngB) * Double.pi/180)
let y = sin(arcLatA) * sin(arcLatB)
var s = x + y
if s > 1 {
s = 1
}
if s < -1 {
s = -1
}
let alpha = acos(s)
let distance = alpha * EARTH_R
return distance
}
}
extension LocationTransform {
public static func gcj2bd(gcjLat: Double, gcjLng: Double) -> (bdLat: Double, bdLng: Double) {
if isOutOfChina(lat: gcjLat, lng: gcjLng) {
return (gcjLat, gcjLng)
}
let x = gcjLng, y = gcjLat
let z = sqrt(x * x + y * y) + 0.00002 * sin(y * Double.pi)
let theta = atan2(y, x) + 0.000003 * cos(x * Double.pi)
let bdLng = z * cos(theta) + 0.0065
let bdLat = z * sin(theta) + 0.006
return (bdLat, bdLng)
}
public static func bd2gcj(bdLat: Double, bdLng: Double) -> (gcjLat: Double, gcjLng: Double) {
if isOutOfChina(lat: bdLat, lng: bdLng) {
return (bdLat, bdLng)
}
let x = bdLng - 0.0065, y = bdLat - 0.006
let z = sqrt(x * x + y * y) - 0.00002 * sin(y * Double.pi)
let theta = atan2(y, x) - 0.000003 * cos(x * Double.pi)
let gcjLng = z * cos(theta)
let gcjLat = z * sin(theta)
return (gcjLat, gcjLng)
}
public static func wgs2bd(wgsLat: Double, wgsLng: Double) -> (bdLat: Double, bdLng: Double) {
let (gcjLat, gcjLng) = wgs2gcj(wgsLat: wgsLat, wgsLng: wgsLng)
return gcj2bd(gcjLat: gcjLat, gcjLng: gcjLng)
}
public static func bd2wgs(bdLat: Double, bdLng: Double) -> (wgsLat: Double, wgsLng: Double) {
let (gcjLat, gcjLng) = bd2gcj(bdLat: bdLat, bdLng: bdLng)
return gcj2wgs(gcjLat: gcjLat, gcjLng: gcjLng)
}
}