福岡市のCOVID19オープンデータは更新されるたびにURLが更新される(そして古いURLは削除される)という意味不明な運用なので、毎日swiftプログラムを書き換えるという馬鹿なことをしていたのだけど、半年たってようやくcrawlingしようという気になりました。
やり方を紹介しているサイトのコードはそれなりに簡単なんだけど、コピペしても動かない。 そもそもこの正規表現は何?というレベルでコードに不信感を抱いたのでplaygroundで色々修正して正解を探した。
結局こうなった。
import Foundation
// Input your parameters here
let startUrl = URL(string: "https://ckan.open-governmentdata.org/dataset/401000_pref_fukuoka_covid19_patients")!
let maximumPagesToVisit = 10
// Crawler Parameters
let semaphore = DispatchSemaphore(value: 0)
var visitedPages: Set<URL> = []
var pagesToVisit: Set<URL> = [startUrl]
// Crawler Core
func crawl() {
guard visitedPages.count <= maximumPagesToVisit else {
semaphore.signal()
return
}
guard let pageToVisit = pagesToVisit.popFirst() else {
semaphore.signal()
return
}
if visitedPages.contains(pageToVisit) {
crawl()
} else {
visit(page: pageToVisit)
}
}
func visit(page url: URL) {
visitedPages.insert(url)
let task = URLSession.shared.dataTask(with: url) { data, response, error in
defer { crawl() }
guard
let data = data,
error == nil,
let document = String(data: data, encoding: .utf8) else { return }
parse(document: document, url: url)
}
task.resume()
}
func parse(document: String, url: URL) {
func collectLinks() -> [URL] {
let regex = try! NSRegularExpression(pattern: "https://[^\"]*", options: [])
let matches = regex.matches(in: document, options: [], range: NSRange(document.startIndex..<document.endIndex, in: document))
print(matches.map { m in document[Range(m.range, in: document)!]} )
return matches.compactMap { m in URL(string: String(document[Range(m.range, in: document)!])) }
}
print(collectLinks())
collectLinks().forEach { pagesToVisit.insert($0) }
}
crawl()
//semaphore.wait()
parse
の中身をほぼ作り直し。 しかし、それにしても正規表現を使うのにNSなんとか
を使うというあたりが、言語が「閉じてない」感。ちょっとねえ。