gpt4 book ai didi

oop - 如何在 Haskell 中设计一个带有状态的 "web spider"?

转载 作者:行者123 更新时间:2023-12-02 01:26:22 24 4
gpt4 key购买 nike

经过多年的 OOP,我正在学习 Haskell。
我正在编写一个功能和状态很少的哑网络蜘蛛。
我不确定如何在 FP 世界中正确地做到这一点。
在 OOP 世界中,这个蜘蛛可以这样设计(通过使用):

Browser b = new Browser()
b.goto(“http://www.google.com/”)

String firstLink = b.getLinks()[0]

b.goto(firstLink)
print(b.getHtml())
此代码加载 http://www.google.com/ ,然后“单击”第一个链接,加载第二页的内容,然后打印该内容。
class Browser {
goto(url: String) : void // loads HTML from given URL, blocking
getUrl() : String // returns current URL
getHtml() : String // returns current HTML
getLinks(): [String] // parses current HTML and returns a list of available links (URLs)

private _currentUrl:String
private _currentHtml:String
}
一次拥有 2 个或“浏览器”是可能的,它们有自己独立的状态:
Browser b1 = new Browser()
Browser b2 = new Browser()

b1.goto(“http://www.google.com/”)
b2.goto(“http://www.stackoverflow.com/”)

print(b1.getHtml())
print(b2.getHtml())
问题 : 展示你如何在 Haskell 中从 scracth 设计这样的东西(类似浏览器的 API,可能有几个独立的实例)?请给一个代码片段。
注意 : 为简单起见,跳过 getLinks() 函数的细节(它琐碎且不有趣)。另外假设有一个 API 函数
getUrlContents :: String -> IO String
打开 HTTP 连接并返回给定 URL 的 HTML。

更新 :为什么要有状态(或可能没有)?
API 可以有更多的功能,而不仅仅是单一的“加载和解析结果”。
我没有添加它们以避免复杂性。
它还可以通过在每个请求中发送它们来关心 HTTP Referer header 和 cookie,以模拟真实的浏览器行为。
考虑以下场景:
  • 打开http://www.google.com/
  • 在第一个输入区域输入“haskell”
  • 点击按钮“谷歌搜索”
  • 点击链接“2”
  • 点击链接“3”
  • 打印当前页面的 HTML(“haskell”的谷歌结果页面 3)

  • 手头有这样的场景,作为开发人员,我希望尽可能地将其转移到代码中:
    Browser b = new Browser()
    b.goto("http://www.google.com/")
    b.typeIntoInput(0, "haskell")
    b.clickButton("Google Search") // b.goto(b.finButton("Google Search"))
    b.clickLink("2") // b.goto(b.findLink("2"))
    b.clickLink("3")
    print(b.getHtml())
    此方案的目标是在一组操作后获取最后一页的 HTML。
    另一个不太明显的目标是保持代码紧凑。
    如果浏览器有状态,它可以发送 HTTP Referer header 和 cookie,同时将所有机制隐藏在自身内部并提供漂亮的 API。
    如果浏览器没有状态,开发人员很可能会传递所有当前的 URL/HTML/Cookie——这会给场景代码增加噪音。
    注意:我想有一些库可以在 Haskell 中抓取 HTML,但我的目的不是抓取 HTML,而是了解如何在 Haskell 中正确设计这些“黑盒”的东西。

    最佳答案

    正如您描述的问题,根本不需要状态:

    data Browser = Browser { getUrl :: String, getHtml :: String, getLinks :: [String]} 

    getLinksFromHtml :: String -> [String] -- use Text.HTML.TagSoup, it should be lazy

    goto :: String -> IO Browser
    goto url = do
    -- assume getUrlContents is lazy, like hGetContents
    html <- getUrlContents url
    let links = getLinksFromHtml html
    return (Browser url html links)

    It’s possbile to have 2 or “browsers” at once, with its own separate state:



    显然,您可以拥有任意数量,并且它们不能相互干扰。

    现在相当于你的片段。第一的:
    htmlFromGooglesFirstLink = do
    b <- goto "http://www.google.com"
    let firstLink = head (links b)
    b2 <- goto firstLink -- note that a new browser is returned
    putStr (getHtml b2)

    第二:
    twoBrowsers = do
    b1 <- goto "http://www.google.com"
    b2 <- goto "http://www.stackoverflow.com/"
    putStr (getHtml b1)
    putStr (getHtml b2)

    更新(回复您的更新):

    If Browser has a state, it can send HTTP Referer header and cookies while hiding all mechanics inside itself and giving nice API.



    仍然不需要状态, goto可以只接受一个浏览器参数。首先,我们需要扩展类型:
    data Browser = Browser { getUrl :: String, getHtml :: String, getLinks :: [String], 
    getCookies :: Map String String } -- keys are URLs, values are cookie strings

    getUrlContents :: String -> String -> String -> IO String
    getUrlContents url referrer cookies = ...

    goto :: String -> Browser -> IO Browser
    goto url browser = let
    referrer = getUrl browser
    cookies = getCookies browser ! url
    in
    do
    html <- getUrlContents url referrer cookies
    let links = getLinksFromHtml html
    return (Browser url html links)

    newBrowser :: Browser
    newBrowser = Browser "" "" [] empty

    If Browser has no state, the developer is likely to pass around all current URL/HTML/Cookies -- and this adds noise to scenario code.



    不,您只需传递 Browser 类型的值。对于你的例子,
    useGoogle :: IO ()
    useGoogle = do
    b <- goto "http://www.google.com/" newBrowser
    let b2 = typeIntoInput 0 "haskell" b
    b3 <- clickButton "Google Search" b2
    ...

    或者您可以摆脱这些变量:
    (>>~) = flip mapM -- use for binding pure functions

    useGoogle = goto "http://www.google.com/" newBrowser >>~
    typeIntoInput 0 "haskell" >>=
    clickButton "Google Search" >>=
    clickLink "2" >>=
    clickLink "3" >>~
    getHtml >>=
    putStr

    这看起来够好吗?请注意,浏览器仍然是不可变的。

    关于oop - 如何在 Haskell 中设计一个带有状态的 "web spider"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2305370/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com