packages feed

scrappy-core-0.1.0.0: src/Scrappy/Elem/ChainHTML.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TupleSections #-}

module Scrappy.Elem.ChainHTML where



import Scrappy.Find (findNaive)
import Scrappy.Links (maybeUsefulUrl)
import Scrappy.Elem.SimpleElemParser (elemParser)
import Scrappy.Elem.ElemHeadParse (parseOpeningTag, hrefParser)
import Scrappy.Elem.Types (Elem', ShowHTML, ElemHead, Elem, innerText'
                  , matches')

import Control.Monad.Trans.Maybe (MaybeT)
import Text.Parsec (ParsecT, Stream, char, (<|>), many, parserFail, parse, parserZero, string, optional)
import Control.Applicative (some, liftA2)
import Data.Functor.Identity (Identity)
import Data.Maybe (catMaybes)
-- functions for chaining free-range html patterns based on the previous
-- patterns to allow for maximum flexibility 

nl :: Stream s m Char => ParsecT s u m ()
nl = optional (many $ (char '\n' <|> char ' '))

manyHtml p = many $ p <* nl

someHtml p = some $ p <* nl

manyTillHtml_ p end = manyTill_ (p <* nl) end 


htmlTag :: Stream s m Char => ParsecT s u m ElemHead
htmlTag = parseOpeningTag (Just ["html"]) [] 




manyTill_ :: ParsecT s u m a -> ParsecT s u m end -> ParsecT s u m ([a], end)
manyTill_ p end = go
  where
    go = (([],) <$> end) <|> liftA2 (\x (xs, y) -> (x : xs, y)) p go



clean :: String -> String
clean = undefined -- drop if == ( \n | \" | '\\' )


-- -- same site is guranteed
-- allLinks :: String -> ParsecT String () Identity [String] 
-- allLinks baseUrl = do
--   x <- findNaive hrefParser 
--   return $ case x of
--     Just (x':xs') -> catMaybes $ fmap (maybeUsefulUrl baseUrl) (x':xs')
--     Just [] -> []
--     Nothing -> [] 



mustContain :: ParsecT s u m (Elem' a) -> Int -> ParsecT s u m b -> ParsecT s u m (Elem' a)
mustContain e count pat = do
  out <- e
  case parse (findNaive $ string "Search") "" (innerText' out) of
    Right (Just xs) -> if count > (length xs) then parserZero else return out
    _ -> parserZero
    
-- | An elem head configures the bracketing so this is all we need for
-- | crafting

type Shell =  (Elem, [(String, Maybe String)]) 

-- incomplete
contains'' :: (Stream s m Char, ShowHTML a) => Shell 
           -> ParsecT s u m a
           -> ParsecT s u m [a]
contains'' (e,as) p = matches' <$> elemParser (Just [e]) (Just p) as

parseInShell = contains 

-- | This will be fully removed in the future
-- | 99% of the time this is gonna be desired to pair with findNaive 
{-# DEPRECATED contains "this should have been called parseInShell from the start, you probably want contains' or containsFirst" #-}
contains :: ParsecT s u m (Elem' a) -> ParsecT String () Identity b -> ParsecT s u m b
contains shell b = do
  x <- shell

  let
    ridNL p = (many (char ' ' <|> char '\n')) >> p 
  
  -- need to skip 
  case parse (ridNL b) "" (innerText' x) of
    Right match -> return match
    Left err -> parserFail (show err)

-- | finds multiple matches anywhere inside the passed elem
-- | This function is also quite extensible because when used with `scrape`
-- | this combo will return a list of list of elems where the hierarchy of HTML has been preserved
-- | but a great deal of information has been filtered out. An example use case would be knowing that
-- | you want <p> tags from a Set of very specific shells, this could allow you to analyze what and how many
-- | came from each shell.
-- | This also naturally extends to running this same scraper on multiple pages which would allow you to recover
-- | ample details on the number of match_A in shell_S on Page_P ~~ [[[MatchA]]] and this Match can be any
-- | arbitarily defined type. You can further imagine pairing with NLP analysis but this is a long enough point here.
containsMany, contains'
  :: ShowHTML a =>
     ParsecT s u m (Elem' a) 
  -> ParsecT String () Identity b
  -> ParsecT s u m [b]
containsMany = contains' -- legacy
contains' shell b = do
  x <- shell
  case parse (findNaive b) "" (innerText' x) of
    Right (Just matches) -> pure matches
    Left err -> parserFail (show err)
    Right Nothing -> parserFail "no matches in this container" 

containsFirst :: ShowHTML a =>
                 ParsecT s u m (Elem' a) 
              -> ParsecT String () Identity b
              -> ParsecT s u m b
containsFirst shell b = head <$> contains' shell b


sequenceHtml :: Stream s m Char => ParsecT s u m a -> ParsecT s u m b -> ParsecT s u m (a, b)
sequenceHtml p1 p2 = do
  x <- p1
  _ <- many (char ' ' <|> char '\n' <|> char '\t')
  y <- p2
  return (x, y)

sequenceHtml_ :: Stream s m Char => ParsecT s u m a -> ParsecT s u m b -> ParsecT s u m b
sequenceHtml_ p1 p2 = do
  _ <- p1
  _ <- many (char ' ' <|> char '\n' <|> char '\t')
  p2

(</>>) :: Stream s m Char => ParsecT s u m a -> ParsecT s u m b -> ParsecT s u m b 
(</>>) = sequenceHtml_

(</>>=) :: Stream s m Char => ParsecT s u m a -> ParsecT s u m b -> ParsecT s u m (a, b)
(</>>=) = sequenceHtml

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


-- manyHtml :: Stream s m Char => ParsecT s u m a -> ParsecT s u m [a]
-- manyHtml prsrHtml = (many (char ' ' <|> char '\n')) >> many (fmap snd $ manyTill_ (char ' ' <|> char '\n') prsrHtml)

-- someHtml :: Stream s m Char => ParsecT s u m a -> ParsecT s u m [a]
-- someHtml prsrHtml = many (char ' ' <|> char '\n') >> some prsrHtml