命令行工具中的URLSession:控制多个任务并将HTML转换为文本

时间:2016-07-21 13:26:01

标签: swift macos html-parsing nsurlsession swift3

在我使用Swift 3.0(Beta 2)的OS X命令行工具项目中,我需要将HTML数据从多个URL转换为String。使用具有许多后台任务的此类函数存在一个问题(除了主线程之外它没有工作,所以可能有更优雅的方法来控制所有任务的完成并在有或没有解析器的情况下读取此类工具中的HTML数据我需要Swift 3和Mac OS X(Linux在不久的将来)):

func html2text (html: String, usedEncoding: String.Encoding) -> String {

    let data = html.data(using: usedEncoding)!

    if let htmlString = AttributedString(html: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: usedEncoding.rawValue], documentAttributes: nil)?.string {
        return htmlString
    } else {
        return ""
    }
}

所以我首先将数据读入一个数组,等待所有DataTasks完成然后在主线程中转换它。还使用全局变量(URL集)来控制每个任务的完成:

import Foundation
import WebKit

var urlArr = [String]()
var urlSet = Set<String>()
var htmlTup : [(url : String, html : String, encoding : String.Encoding)] = []

let session = URLSession.shared

具有多个URLSession DataTasks的for-in循环

    for myurl in urlArr {
        if urlSet.insert(myurl).inserted {
            print ("Loading \(myurl)...")

            let inputURL = URL(string: myurl)!
            let task = session.dataTask(with: inputURL, completionHandler: {mydata, response, error in

从HTML开始读取编码

           var usedEncoding =  String.Encoding.utf8
           if let encodingName = response!.textEncodingName {

                    let encoding = CFStringConvertIANACharSetNameToEncoding(encodingName)
                    if encoding != kCFStringEncodingInvalidId {                            
                        usedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(encoding))                            
                    }
                }  

使用HTML String并将数据读入数组

if let myString = String(data: mydata!, encoding: usedEncoding) {                  
                htmlTup += [(url: myurl,html: myString, encoding: usedEncoding)]
                }
                // The end of task removing URL from Set
                urlSet.remove(myurl)                    
            })
            //Run Task
            task.resume()  
        }
    } 
}

等待完成任务并将HTML转换为文本

while !urlSet.isEmpty {
// Do nothing
}

for (url,html,encoding) in htmlTup {
    print ("Writing data from \(url)...")    
    print (html2text(html: html, usedEncoding: encoding))
}
来自this主线程的

更新1: RunLoop 这样的代码用于检查每个任务的完成时间:

var taskArr = [Bool]()
let task = session.dataTask(with: request) { (data, response, error) in
}
                            taskArr.removeLast()
                        }
                        taskArr.append(true)
                        task.resume()

// Waiting for tasks to complete
            let theRL = RunLoop.current
            while !taskArr.isEmpty && theRL.run(mode: .defaultRunLoopMode, before: .distantFuture) { }

1 个答案:

答案 0 :(得分:0)

你不能在繁忙的循环中旋转等待结果,因为你通过这样做来阻止主运行循环/线程/调度队列。

相反,返回该点,从而允许主运行循环运行。然后,在您的完成处理程序中,检查您是否已经获得了您期望的所有响应,如果是,请在忙碌等待循环之后执行您当前拥有的内容。