Skip to content

Instantly share code, notes, and snippets.

@maralorn
Created September 5, 2021 14:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maralorn/a288329af50955dff353e5529f377488 to your computer and use it in GitHub Desktop.
Save maralorn/a288329af50955dff353e5529f377488 to your computer and use it in GitHub Desktop.
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# OPTIONS_GHC -Wall #-}
-- This script fetches prices of commodities like funds, etfs, stocks and
-- currencies by scraping a webpage and prints it in hledger compatible format.
module Main where
import Data.ByteString (ByteString)
import Data.String.Interpolate (i) -- dependency: string-interpolate (You could do this without it, but it’s super convenient.)
import Data.Text (Text)
import Data.Time (defaultTimeLocale, formatTime, getZonedTime)
-- dependencies: req, tagsoup
import Network.HTTP.Req (GET (GET), NoReqBody (NoReqBody), Scheme (Https), Url, bsResponse, defaultHttpConfig, https, req, responseBody, runReq, (/:))
import Text.HTML.TagSoup (Tag, innerText, parseTags, (~/=))
fetchPrice :: (Url scheme, Text, Text, [Tag ByteString] -> ByteString) -> IO ()
fetchPrice (url, name, currency, scraper) = do
response <- runReq defaultHttpConfig $ req GET url NoReqBody bsResponse mempty
let tags = parseTags $ responseBody response
time <- getZonedTime
putStrLn [i|P #{formatTime defaultTimeLocale "%Y-%m-%d" time} "#{name}" #{scraper tags}#{currency}|]
-- See https://hackage.haskell.org/package/req-3.9.1/docs/Network-HTTP-Req.html#t:Url for how to build an URL.
-- You might need to modify the call to `req` in `fetchPrice` to pass "?foo=bar" parameters.
tagesschauURL :: Text -> Url 'Https
tagesschauURL name = https "www.tagesschau.de" /: "wirtschaft" /: "boersenkurse" /: name /: ""
-- See https://hackage.haskell.org/package/tagsoup for how to write a parser for prices from other webpages.
getTagesschauPrice :: [Tag ByteString] -> ByteString
getTagesschauPrice = innerText . take 2 . dropWhile (~/= ("<span class=\"price\">" :: String))
-- Modify this list to list the commodity prices you need.
-- [(urltofetch, nameOfCommodity, currencyOfCommodity, tagsoupFilterToScrapePrice)]
--
-- For tagesschauURL go to www.tagesschau.de search for your commodity and take the last part of the url
prices :: [(Url 'Https, Text, Text, [Tag ByteString] -> ByteString)]
prices =
[ (tagesschauURL "lu0629459743-9922402", "A1JA1R", "€", getTagesschauPrice)
, (tagesschauURL "xc0009666410-25216210", "$", "€", getTagesschauPrice)
]
main :: IO ()
main = mapM_ fetchPrice prices
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment