F# 异步爬网#
在网页上爬行时,我需要小心,不要对同一个域发出太多请求,例如,我希望在请求之间放置1s。据我所知,请求之间的时间是重要的。因此,为了加快速度,我想在F#中使用异步工作流,其思想是以1秒的间隔发出请求,但避免在等待请求响应时阻塞F# 异步爬网#,f#,web-crawler,F#,Web Crawler,在网页上爬行时,我需要小心,不要对同一个域发出太多请求,例如,我希望在请求之间放置1s。据我所知,请求之间的时间是重要的。因此,为了加快速度,我想在F#中使用异步工作流,其思想是以1秒的间隔发出请求,但避免在等待请求响应时阻塞 let getHtmlPrimitiveAsyncTimer (uri : System.Uri) (timer:int) = async{ let req = (WebRequest.Create(uri)) :?> HttpW
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"
}
这样行吗?我对两件事非常不确定:
-Thread.Sleep事件是否能延迟请求?
-使用StartTask是否存在问题
我是F#的初学者(正如您可能已经注意到的那样)(实际上是一般的编码),所有围绕线程的东西都让我害怕:)
谢谢 我想你想做的是 -创建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将运行到睡眠状态,依此类推。然后在一秒钟内第一个异步唤醒并发出请求,另一秒钟后第二个异步唤醒。。。所以这应该行得通。1只是一个近似值,因为它们启动计时器时,每个计时器彼此都有一点点的交错。。。如果您需要的截止时间正好是一秒钟,您可能需要对其进行一点缓冲,例如1100毫秒或其他什么(网络延迟等可能仍会使您的程序无法控制)
Thread.Sleep
是次优的,它可以处理少量的请求,但是您正在烧掉一个线程,而且线程非常昂贵,而且无法扩展到大量
您不需要startask
,除非您希望与.NET任务互操作,或稍后通过.result
与结果进行阻塞会合。如果您只想让它们全部运行,然后阻塞以在一个数组中收集所有结果,Async.Parallel
将为您完成fork-join-parallelism。如果他们只是要打印结果,您可以通过Async.Start
启动并忘记,这样会将结果放在地板上
(另一种策略是使用代理作为节流阀。将所有http请求发布到单个代理,该代理在逻辑上是单线程的,并处于循环中,执行
Async.Sleep
,持续1s,然后处理下一个请求。这是一种很好的方法,可以使通用节流阀…对我来说可能是值得写博客的,想想看)我想你想做的是
-创建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将运行到睡眠状态,依此类推。然后在一秒钟内第一个Async唤醒并发布请求,另一秒钟后第二个Async将作为ync唤醒,…所以这应该是可行的。1s只是近似值,因为它们启动计时器时彼此之间的间隔非常小……如果您需要的截止时间确实是一秒钟,您可能需要缓冲一下,例如1100毫秒或其他什么(网络延迟和诸如此类的事情仍然会让您的程序无法控制)
Thread.Sleep
是次优的,它可以处理少量的请求,但是您正在烧掉一个线程,而且线程非常昂贵,而且无法扩展到大量
你不需要startask
,除非你想与.NET任务互操作,或者以后通过.result
与结果进行阻塞会合。如果你只想让这些都运行,然后阻塞以收集数组中的所有结果,Async.Parallel
将为你做一个fork-join并行e只要打印结果,您就可以通过Async.Start
启动并忘记,这将把结果扔到地板上
(另一种策略是使用代理作为节流阀。将所有http请求发布到单个代理,该代理在逻辑上是单线程的,并处于循环中,执行
Async.Sleep
,持续1s,然后处理下一个请求。这是一种很好的方法,可以使通用节流阀…对我来说可能是值得写博客的,想想看)呵呵!非常非常好,谢谢。F#真是太棒了,我喜欢“Async.Parallel”能帮你做到这一点:),对于像我这样的初学者来说,它让你担心代码是否正确。谢谢!已经取得了进展:事实上,Brian,我不确定这个解决方案是否适用于大量uri,或者是真的吗?请看我在那里的回答;那里的问题与这里的问题不同,因为在这里,你预先知道所有uri,并立即启动所有操作,而您在程序运行期间发现了新的URI。是的,谢谢您看到了,正在使用:)。在这篇文章中,在一个非常大的异步元素序列上同步启动|>Asyn.Parallel |>Asyn.RunSynchronously是没有问题的?呵呵!非常好,谢谢。F#真的很神奇,我喜欢“Async.Parallel”可以为您做到这一点:),对于像我这样的初学者来说,它让你担心代码是否正确。非常感谢。已经取得了进展:实际上,Brian,我不确定这个解决方案是否适用于大量uri,或者它是否有效?请参见我的回答;这里的问题与这里的问题不同,因为在这里您预先知道所有URI并立即启动所有URI,而在那里您在程序运行期间发现了新的URI。是的,谢谢您看到了它,正在处理它:)。对于这篇文章,在一个非常大的异步元素序列上同步启动|>Asyn.Parallel |>Asyn.runsynchronous没有问题吗?