异步抓取F#

时间:2010-06-11 10:08:34

标签: f# web-crawler

在网页上抓取时,我需要小心,不要向同一个域发出太多请求,例如我想在请求之间放置1秒。据我所知,这是请求之间的重要时间。因此,为了加快速度,我想在F#中使用异步工作流,这个想法是以1秒的间隔发出请求,但是在等待请求响应时避免阻塞。

let getHtmlPrimitiveAsyncTimer (uri : System.Uri) (timer:int) =
    async{

            let req =  (WebRequest.Create(uri)) :?> HttpWebRequest
            req.UserAgent<-"Mozilla"
            try 

                Thread.Sleep(timer)
                let! resp =    (req.AsyncGetResponse())
                Console.WriteLine(uri.AbsoluteUri+" got response")
                use stream = resp.GetResponseStream()
                use reader = new StreamReader(stream)
                let html = reader.ReadToEnd()
                return html
            with 
            | _ as ex -> return "Bad Link"
                 }

然后我做了类似的事情:

let uri1 = System.Uri "http://rue89.com"
let timer = 1000
let jobs = [|for i in 1..10 -> getHtmlPrimitiveAsyncTimer uri1 timer|]

jobs
|> Array.mapi(fun i job -> Console.WriteLine("Starting job "+string i)
                               Async.StartAsTask(job).Result)

这样好吗?我不确定两件事: - Thread.Sleep是否可以延迟请求? - 使用StartTask是个问题吗?

我是F#的初学者(你可能已经注意到了)(实际编码实际上是这样),并且所有涉及Threads的东西都让我害怕:)

谢谢!

1 个答案:

答案 0 :(得分:4)

我认为你想做的是   - 创建10个作业,编号为'n',每个从现在开始'n'秒   - 并行运行

大致喜欢

let makeAsync uri n = async {
    // create the request
    do! Async.Sleep(n * 1000)
    // AsyncGetResponse etc
    }

let a = [| for i in 1..10 -> makeAsync uri i |]
let results = a |> Async.Parallel |> Async.RunSynchronously

请注意,如果例如,他们当然不会完全开始你有一台4核机器,4台将很快开始运行,但随后快速执行到Async.Sleep,此时接下来的4台将一直运行直到它们休眠,依此类推。然后在一秒钟内第一个异步唤醒并发布一个请求,另一个秒后第二个异步唤醒,...所以这应该工作。 1s只是近似的,因为他们每个人都是一个非常小的位置,他们开始计时器......你可能想稍微缓冲它,例如1100毫秒或者其他什么,如果您需要的截止时间恰好是一秒钟(网络延迟,还有什么可能会留下一些可能控制程序之外的东西)。

Thread.Sleep不是最理想的,它可以用于少量请求,但是你正在刻录一个线程,而且线程很昂贵而且它不会扩展到很多。

您不需要StartAsTask,除非您想要与.NET任务进行互操作,或者稍后通过.Result对结果进行阻塞集合。如果你只是希望这些都运行然后阻塞以收集数组中的所有结果,Async.Parallel将为你做这个fork-join并行性就好了。如果他们只打算打印结果,你可以通过Async.Start点击发射,这会将结果丢弃在地板上。

(另一种策略是使用代理作为限制。将所有http请求发布到单个代理,其中代理在逻辑上是单线程的并且处于循环中,执行Async.Sleep 1秒,并且然后处理下一个请求。这是制作通用油门的好方法......可能对我来说值得博客,想想它。)