Рубрики
Без рубрики

Сравнение того же веб -скребка в Haskell, Python, Go

Таким образом, этот проект начался с необходимости – или, не на самом деле необходимости, но раздражение, которое я понял, будет G … Tagged with Haskell, Python, Go.

Таким образом, этот проект начался с необходимости – или, не на самом деле необходимости, но раздражение, которое, как я понял, станет хорошей возможностью укрепить свой Haskell, даже если решение, вероятно, не стоило того в конце.

Есть блог, за которым я следую ( поддельный Nous ), который использует WordPress, что означает, что его механика для комментариев и система учетных записей столь же запутанно и кошмарно, как управление пакетами Haskell. В частности, я хотел посмотреть, смогу ли я покончить с полагаться на уведомления Kludgy WordPress, которые, кажется, работают только время от времени, и писать веб -скребок, который принесет страницу, найдите недавний элемент комментариев и посмотрите, был ли новый комментарий.

Теперь я сделал основной удар по работе – я написал сценарий Haskell, который выводит строку «Имя на посте» самого последнего комментария. И я подумал, что было бы интересно сравнить решение Haskell с Python и Go Solutions.

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE ViewPatterns #-}

import Network.HTTP.Req
import qualified Text.HTML.DOM as DOM
import qualified Text.XML.Cursor as Cursor
import qualified Text.XML.Selector as Selector
import qualified Data.XML.Types as Types
import qualified Text.XML as XML
import Data.Text (Text, unpack)
import Control.Monad

main = do
    resp <- runReq defaultHttpConfig $ req GET (https "fakenous.net") NoReqBody lbsResponse mempty
    let dom = Cursor.fromDocument $ DOM.parseLBS $ responseBody resp
        recentComments = XML.toXMLNode $ Cursor.node $ head $ Selector.query "#recentcomments" $ dom
        newest = head $ Types.nodeChildren recentComments
    putStrLn $ getCommentText newest

getCommentText commentElem =
    let children = Types.nodeChildren commentElem
    in foldl (++) "" $ unwrap <$> children

unwrap :: Types.Node -> String
unwrap (Types.NodeContent (Types.ContentText s)) = unpack s 
unwrap e = unwrap $ head $ Types.nodeChildren e

Мой Хаскелл cloc В 25 строках, хотя, если вы удалите неиспользованные расширения языка, это сводится к 21 (остальные четыре там только потому, что они «ходят» для меня). Итак, 21 – это более справедливое количество. Если вы не считаете импорт как строки кода, это может быть 13.

Писать это было на самом деле не очень сложно; Из 5 часов, которые я, вероятно, вложил в это, в конце концов, 90% этого времени было потрачено на борьбу с управлением пакетами (худший аспект Хаскелла). В конце концов я наконец прибегал к стеку, хотя это сценарий с одним файлом, который должен быть в состоянии компилировать только GHC Анкет

Я горжусь своей работой, и думал, что она довольно хорошо отразилась на языке, чтобы сделать это так кратко. Мой энтузиазм немного упал, когда я написал решение Python:

import requests
from bs4 import BeautifulSoup

file = requests.get("https://fakenous.net").text

dom = BeautifulSoup(file, features='html.parser')
recentcomments = dom.find(id = 'recentcomments')
print(''.join(list(recentcomments.children)[0].strings))

6 Линии до 21, или от 4 до 13. Проклятие. Я все более и больше убежден, что ничто никогда не вытеснит мою любовь к Python.

Конечно, вы можете приписать некоторые из относительных размеров Haskell наличие низшей библиотеки, но все же.

Вот решение GO:

package main

import (
    "fmt"
    "net/http"

    "github.com/ericchiang/css"
    "golang.org/x/net/html"
)

func main() {
    var resp, err = http.Get("https://fakenous.net")
    must(err)
    defer resp.Body.Close()
    tree, err := html.Parse(resp.Body)
    must(err)
    sel, err := css.Compile("#recentcomments > *:first-child")
    must(err)
    // It will only match one element.
    for _, elem := range sel.Select(tree) {
        var name = elem.FirstChild
        var on = name.NextSibling
        fmt.Printf("%s%s%s\n", unwrap(name), unwrap(on), unwrap(on.NextSibling))
    }

}

func unwrap(node *html.Node) string {
    if node.Type == html.TextNode {
        return node.Data
    }
    return unwrap(node.FirstChild)
}

func must(err error) {
    if err != nil {
        panic(err)
    }
}

32 строки, включая импорт. Так что, по крайней мере, Хаскелл стал короче, чем Go. Я горжусь тобой, О, не так, это не очень высокая барная планка.

Было бы разумно возразить, что решение Python настолько краткое, потому что оно не нуждается в основной функции, но в реальных приложениях Python вы, как правило, все еще этого хотите. Но Даже если я его изменяю:

import requests
from bs4 import BeautifulSoup

def main():
    file = requests.get("https://fakenous.net").text
    dom = BeautifulSoup(file, features='html.parser')
    recentcomments = dom.find(id = 'recentcomments')
    return ''.join(list(recentcomments.children)[0].strings)

if __name__ == '__main__': main()

Он только в 8 строках, включая импорт.

Альтернативная версия решения GO, которая не жестко кодирует количество узлов (поскольку не делают Python и Haskell):

package main

import (
    "fmt"
    "net/http"

    "github.com/ericchiang/css"
    "golang.org/x/net/html"
)

func main() {
    var resp, err = http.Get("https://fakenous.net")
    must(err)
    defer resp.Body.Close()
    tree, err := html.Parse(resp.Body)
    must(err)
    sel, err := css.Compile("#recentcomments > *:first-child")
    must(err)
    // It will only match one element.
    for _, elem := range sel.Select(tree) {
        fmt.Printf("%s\n", textOfNode(elem))
    }

}

func textOfNode(node *html.Node) string {
    var total string
    var elem = node.FirstChild
    for elem != nil {
        total += unwrap(elem)
        elem = elem.NextSibling
    }
    return total
}

func unwrap(node *html.Node) string {
    if node.Type == html.TextNode {
        return node.Data
    }
    return unwrap(node.FirstChild)
}

func must(err error) {
    if err != nil {
        panic(err)
    }
}

Хотя в итоге это 39 строк.

Возможно, лидерство Python уменьшится, если бы я внедрил вторую половину, чтобы сценарии сохранили последний комментарий, который они нашли в файле, прочитали его при запуске и обновите, если он отличается, и каким -то образом уведомит меня (электронное письмо может быть интересным тестом). Я сомневаюсь в этом, но если людям понравится этот пост, я закончу их.

РЕДАКТИРОВАТЬ: Я закончил их.

Оригинал: “https://dev.to/yujiri8/comparing-the-same-web-scraper-in-haskell-python-go-387a”