在网络爬虫中惯用惯用原子

时间:2015-01-05 16:54:48

标签: clojure

例如,在网络抓取工具中,它会维护一组全球访问过的网址。一旦工作人员开始处理URL或已完成URL,其他工作人员不应使用相同的URL。在Java中实现这一点的一种方法是将访问过的URL放在ConcurrentHashMap中(可能是Set更好)。每个工作人员在访问URL之前都会查看地图

if (visited.putIfAbsent(url, true) == null) {
  crawl(url);
} else {
  // do nothing
}

在Clojure中,我在atom中使用了一个集合。每次我要使用最新访问过的URL交换新集时,交换函数应该检查集合是否已经具有此URL。如果URL存在,那么工作人员应该从那里停止。为了能够告诉工作者交换是否成功,我必须将返回值保存在全局状态,如[visited-urls last-swap-succeeded]

(def state (atom [#{} nil]))
(defn f [state key] (let [[visited-urls l] state] (if (visited-urls key) [visited-urls false] [(conj (visited-urls key) true]))))

工人应该

(when (second (swap! state f url)) (crawl url))

它有效,但对我来说看起来很难看。问题是交换功能不允许返回呼叫站点的值。在Clojure中有更好的方法吗?

1 个答案:

答案 0 :(得分:5)

Refs是为这种事做的。这是一个简单的方法

(when (dosync (when-not (@visited-urls-ref url-to-visit)
                (alter visited-urls-ref conj url-to-visit)))
  ; continue crawling url-to-visit
  )

我无法想象它会为网络抓取工具增加任何重大开销。

就个人而言,假设访问网址的顺序并不重要,我会创建一个带有dedupe传感器的core.async频道,并让所有工作人员将网址放入/取出。