使用 Alamofire JSOn 和 HTML 创建通用获取器
Creating generic fetcher using Alamofire JSOn and HTML
我正在尝试启动一个基于网络抓取的项目。我已经为 JSON 的不同平台设置了工具,我使用 SwiftyJSON,对于原始的 HTML,我使用 hpple。我的问题是我正在尝试为内容设置一些通用的 class,为内容的提取器设置一些通用的 class。由于每次操作都是这样,
登录
如果有用户名或密码,请提供。
如果它有验证码显示并使用结果
使用 Alamofire 获取数据
使用 JSON 或 HTML 抓取数据
填充内容 class.
我想知道是否有办法定义某种协议、枚举或通用模板,以便我可以为每个 class 定义那些不同的函数。我想如果我做不到这一点,我会一遍又一遍地编写相同的代码。这就是我想出的。如果您能帮助我正确设置,我将不胜感激。
enum Company:Int {
case CNN
case BBC
case HN
case SO
var captcha:Bool {
switch self {
case CNN:
return false
case BBC:
return true
case HN:
return true
case SO:
return false
}
}
var description:String {
get {
switch self {
case CNN:
return "CNN"
case BBC:
return "BBC"
case HN:
return "Hacker News"
case SO:
return "Stack Overflow"
}
}
}
}
class Fetcher {
var username:String?
var password:String?
var url:String
var company:Company
init(company: Company, url:String) {
self.url = url
self.company = company
}
init(company: Company, url:String,username:String,password:String) {
self.url = url
self.company = company
self.username = username
self.password = password
}
func login() {
if username != nil {
// login
}
if company.captcha {
//show captcha
}
}
func fetch(){
}
func populate() {
}
}
class CNN: Fetcher {
}
好的,这是一个有趣的练习...
您真的只需要进一步构建您的 Company
枚举以使您的 Fetcher
更加抽象。这是一种只对您自己的方法稍作修改的方法,它应该会让您更接近您想要实现的目标。这是基于我之前对你的另一个 的回复。
公司
enum Company: Printable, URLRequestConvertible {
case CNN, BBC, HN, SO
var captcha: Bool {
switch self {
case CNN:
return false
case BBC:
return true
case HN:
return true
case SO:
return false
}
}
var credentials: (username: String, password: String)? {
switch self {
case CNN:
return ("cnn_username", "cnn_password")
case BBC:
return nil
case HN:
return ("hn_username", "hn_password")
default:
return nil
}
}
var description: String {
switch self {
case CNN:
return "CNN"
case BBC:
return "BBC"
case HN:
return "Hacker News"
case SO:
return "Stack Overflow"
}
}
var loginURLRequest: NSURLRequest {
var URLString: String?
switch self {
case CNN:
URLString = "cnn_login_url"
case BBC:
URLString = "bbc_login_url"
case HN:
URLString = "hn_login_url"
case SO:
URLString = "so_login_url"
}
return NSURLRequest(URL: NSURL(string: URLString!)!)
}
var URLRequest: NSURLRequest {
var URLString: String?
switch self {
case CNN:
URLString = "cnn_url"
case BBC:
URLString = "bbc_url"
case HN:
URLString = "hn_url"
case SO:
URLString = "so_url"
}
return NSURLRequest(URL: NSURL(string: URLString!)!)
}
}
新闻
struct News {
let title: String
let content: String
let date: NSDate
let author: String
}
抓取器
class Fetcher {
typealias FetchNewsSuccessHandler = [News] -> Void
typealias FetchNewsFailureHandler = (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void
// MARK: - Fetch News Methods
class func fetchNewsFromCompany(company: Company, success: FetchNewsSuccessHandler, failure: FetchNewsFailureHandler) {
login(
company: company,
success: { apiKey in
Fetcher.fetch(
company: company,
apiKey: apiKey,
success: { news in
success(news)
},
failure: { response, json, error in
failure(response, json, error)
}
)
},
failure: { response, json, error in
failure(response, json, error)
}
)
}
// MARK: - Private - Helper Methods
private class func login(
#company: Company,
success: (String) -> Void,
failure: (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void)
{
if company.captcha {
// You'll need to figure this part out on your own. First off, I'm not really sure how you
// would do it, and secondly, I think there may be legal implications of doing this.
}
let request = Alamofire.request(company.loginURLRequest)
if let credentials = company.credentials {
request.authenticate(username: credentials.username, password: credentials.password)
}
request.responseJSON { _, response, json, error in
if let error = error {
failure(response, json, error)
} else {
// NOTE: You'll need to parse here...I would suggest using SwiftyJSON
let apiKey = "12345678"
success(apiKey)
}
}
}
private class func fetch(
#company: Company,
apiKey: String,
success: FetchNewsSuccessHandler,
failure: FetchNewsFailureHandler)
{
let request = Alamofire.request(company.URLRequest)
request.responseJSON { _, _, json, error in
if let error = error {
failure(response, json, error)
} else {
// NOTE: You'll need to parse here...I would suggest using SwiftyJSON
let news = [News]()
success(news)
}
}
}
}
示例ViewController 调用 Fetcher
class SomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Fetcher.fetchNewsFromCompany(
Company.CNN,
success: { newsList in
for news in newsList {
println("\(news.title) - \(news.date)")
}
},
failure { response, data, error in
println("\(response) \(error)")
}
)
}
}
通过允许 Company
对象流经您的 Fetcher
,您永远不必在 Fetcher 中跟踪公司的状态。都可以直接存入Enum里面。
希望对您有所帮助。干杯。
这是我使用 alamofire 和 alamofire 对象映射器所做的:
第 1 步:创建符合 Mappable 协议的模态 classes。
class StoreListingModal: Mappable {
var store: [StoreModal]?
var status: String?
required init?(_ map: Map){
}
func mapping(map: Map) {
store <- map["result"]
status <- map["status"]
}
}
第 2 步:使用通用类型创建提取请求:
func getDataFromNetwork<T:Mappable>(urlString: String, completion: (T?, NSError?) -> Void) {
Alamofire.request(.GET, urlString).responseObject { (response: Response<T, NSError>) in
guard response.result.isSuccess else{
print("Error while fetching: \(response.result.error)")
completion(nil, response.result.error)
return
}
if let responseObject = response.result.value{
print(responseObject)
completion(responseObject, nil)
}
}
}
第 3 步:现在您只需调用此获取函数即可。可以这样做:
self.getDataFromNetwork("your url string") { (userResponse:StoreListingModal?, error) in
}
您不仅会得到您的响应对象,它还会映射到您的模式 class。
我正在尝试启动一个基于网络抓取的项目。我已经为 JSON 的不同平台设置了工具,我使用 SwiftyJSON,对于原始的 HTML,我使用 hpple。我的问题是我正在尝试为内容设置一些通用的 class,为内容的提取器设置一些通用的 class。由于每次操作都是这样,
登录 如果有用户名或密码,请提供。 如果它有验证码显示并使用结果 使用 Alamofire 获取数据 使用 JSON 或 HTML 抓取数据 填充内容 class.
我想知道是否有办法定义某种协议、枚举或通用模板,以便我可以为每个 class 定义那些不同的函数。我想如果我做不到这一点,我会一遍又一遍地编写相同的代码。这就是我想出的。如果您能帮助我正确设置,我将不胜感激。
enum Company:Int {
case CNN
case BBC
case HN
case SO
var captcha:Bool {
switch self {
case CNN:
return false
case BBC:
return true
case HN:
return true
case SO:
return false
}
}
var description:String {
get {
switch self {
case CNN:
return "CNN"
case BBC:
return "BBC"
case HN:
return "Hacker News"
case SO:
return "Stack Overflow"
}
}
}
}
class Fetcher {
var username:String?
var password:String?
var url:String
var company:Company
init(company: Company, url:String) {
self.url = url
self.company = company
}
init(company: Company, url:String,username:String,password:String) {
self.url = url
self.company = company
self.username = username
self.password = password
}
func login() {
if username != nil {
// login
}
if company.captcha {
//show captcha
}
}
func fetch(){
}
func populate() {
}
}
class CNN: Fetcher {
}
好的,这是一个有趣的练习...
您真的只需要进一步构建您的 Company
枚举以使您的 Fetcher
更加抽象。这是一种只对您自己的方法稍作修改的方法,它应该会让您更接近您想要实现的目标。这是基于我之前对你的另一个
公司
enum Company: Printable, URLRequestConvertible {
case CNN, BBC, HN, SO
var captcha: Bool {
switch self {
case CNN:
return false
case BBC:
return true
case HN:
return true
case SO:
return false
}
}
var credentials: (username: String, password: String)? {
switch self {
case CNN:
return ("cnn_username", "cnn_password")
case BBC:
return nil
case HN:
return ("hn_username", "hn_password")
default:
return nil
}
}
var description: String {
switch self {
case CNN:
return "CNN"
case BBC:
return "BBC"
case HN:
return "Hacker News"
case SO:
return "Stack Overflow"
}
}
var loginURLRequest: NSURLRequest {
var URLString: String?
switch self {
case CNN:
URLString = "cnn_login_url"
case BBC:
URLString = "bbc_login_url"
case HN:
URLString = "hn_login_url"
case SO:
URLString = "so_login_url"
}
return NSURLRequest(URL: NSURL(string: URLString!)!)
}
var URLRequest: NSURLRequest {
var URLString: String?
switch self {
case CNN:
URLString = "cnn_url"
case BBC:
URLString = "bbc_url"
case HN:
URLString = "hn_url"
case SO:
URLString = "so_url"
}
return NSURLRequest(URL: NSURL(string: URLString!)!)
}
}
新闻
struct News {
let title: String
let content: String
let date: NSDate
let author: String
}
抓取器
class Fetcher {
typealias FetchNewsSuccessHandler = [News] -> Void
typealias FetchNewsFailureHandler = (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void
// MARK: - Fetch News Methods
class func fetchNewsFromCompany(company: Company, success: FetchNewsSuccessHandler, failure: FetchNewsFailureHandler) {
login(
company: company,
success: { apiKey in
Fetcher.fetch(
company: company,
apiKey: apiKey,
success: { news in
success(news)
},
failure: { response, json, error in
failure(response, json, error)
}
)
},
failure: { response, json, error in
failure(response, json, error)
}
)
}
// MARK: - Private - Helper Methods
private class func login(
#company: Company,
success: (String) -> Void,
failure: (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void)
{
if company.captcha {
// You'll need to figure this part out on your own. First off, I'm not really sure how you
// would do it, and secondly, I think there may be legal implications of doing this.
}
let request = Alamofire.request(company.loginURLRequest)
if let credentials = company.credentials {
request.authenticate(username: credentials.username, password: credentials.password)
}
request.responseJSON { _, response, json, error in
if let error = error {
failure(response, json, error)
} else {
// NOTE: You'll need to parse here...I would suggest using SwiftyJSON
let apiKey = "12345678"
success(apiKey)
}
}
}
private class func fetch(
#company: Company,
apiKey: String,
success: FetchNewsSuccessHandler,
failure: FetchNewsFailureHandler)
{
let request = Alamofire.request(company.URLRequest)
request.responseJSON { _, _, json, error in
if let error = error {
failure(response, json, error)
} else {
// NOTE: You'll need to parse here...I would suggest using SwiftyJSON
let news = [News]()
success(news)
}
}
}
}
示例ViewController 调用 Fetcher
class SomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Fetcher.fetchNewsFromCompany(
Company.CNN,
success: { newsList in
for news in newsList {
println("\(news.title) - \(news.date)")
}
},
failure { response, data, error in
println("\(response) \(error)")
}
)
}
}
通过允许 Company
对象流经您的 Fetcher
,您永远不必在 Fetcher 中跟踪公司的状态。都可以直接存入Enum里面。
希望对您有所帮助。干杯。
这是我使用 alamofire 和 alamofire 对象映射器所做的: 第 1 步:创建符合 Mappable 协议的模态 classes。
class StoreListingModal: Mappable {
var store: [StoreModal]?
var status: String?
required init?(_ map: Map){
}
func mapping(map: Map) {
store <- map["result"]
status <- map["status"]
}
}
第 2 步:使用通用类型创建提取请求:
func getDataFromNetwork<T:Mappable>(urlString: String, completion: (T?, NSError?) -> Void) {
Alamofire.request(.GET, urlString).responseObject { (response: Response<T, NSError>) in
guard response.result.isSuccess else{
print("Error while fetching: \(response.result.error)")
completion(nil, response.result.error)
return
}
if let responseObject = response.result.value{
print(responseObject)
completion(responseObject, nil)
}
}
}
第 3 步:现在您只需调用此获取函数即可。可以这样做:
self.getDataFromNetwork("your url string") { (userResponse:StoreListingModal?, error) in
}
您不仅会得到您的响应对象,它还会映射到您的模式 class。