packages feed

scalpel 0.1.3.1 → 0.2.0

raw patch · 7 files changed

+89/−113 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Text.HTML.Scalpel: type AttributePredicate str = Attribute str -> Bool
+ Text.HTML.Scalpel: data AttributePredicate
- Text.HTML.Scalpel: (//) :: (StringLike str, Selectable str a, Selectable str b) => a -> b -> Selector str
+ Text.HTML.Scalpel: (//) :: (Selectable a, Selectable b) => a -> b -> Selector
- Text.HTML.Scalpel: (@:) :: (StringLike str, TagName str tag) => tag -> [AttributePredicate str] -> Selector str
+ Text.HTML.Scalpel: (@:) :: TagName tag => tag -> [AttributePredicate] -> Selector
- Text.HTML.Scalpel: (@=) :: (StringLike str, AttributeName str key) => key -> str -> AttributePredicate str
+ Text.HTML.Scalpel: (@=) :: AttributeName key => key -> String -> AttributePredicate
- Text.HTML.Scalpel: (@=~) :: (StringLike str, AttributeName str key, RegexLike re str) => key -> re -> AttributePredicate str
+ Text.HTML.Scalpel: (@=~) :: (AttributeName key, RegexLike re String) => key -> re -> AttributePredicate
- Text.HTML.Scalpel: Any :: Any str
+ Text.HTML.Scalpel: Any :: Any
- Text.HTML.Scalpel: attr :: (Show str, StringLike str, Selectable str s) => str -> s -> Scraper str str
+ Text.HTML.Scalpel: attr :: (Show str, StringLike str, Selectable s) => String -> s -> Scraper str str
- Text.HTML.Scalpel: attrs :: (Show str, StringLike str, Selectable str s) => str -> s -> Scraper str [str]
+ Text.HTML.Scalpel: attrs :: (Show str, StringLike str, Selectable s) => String -> s -> Scraper str [str]
- Text.HTML.Scalpel: chroot :: (StringLike str, Selectable str s) => s -> Scraper str a -> Scraper str a
+ Text.HTML.Scalpel: chroot :: (StringLike str, Selectable s) => s -> Scraper str a -> Scraper str a
- Text.HTML.Scalpel: chroots :: (StringLike str, Selectable str s) => s -> Scraper str a -> Scraper str [a]
+ Text.HTML.Scalpel: chroots :: (StringLike str, Selectable s) => s -> Scraper str a -> Scraper str [a]
- Text.HTML.Scalpel: class AttributeName str k | k -> str
+ Text.HTML.Scalpel: class AttributeName k
- Text.HTML.Scalpel: class Selectable str s | s -> str
+ Text.HTML.Scalpel: class Selectable s
- Text.HTML.Scalpel: class TagName str t | t -> str
+ Text.HTML.Scalpel: class TagName t
- Text.HTML.Scalpel: data Any str
+ Text.HTML.Scalpel: data Any
- Text.HTML.Scalpel: data Selector str
+ Text.HTML.Scalpel: data Selector
- Text.HTML.Scalpel: hasClass :: StringLike str => str -> AttributePredicate str
+ Text.HTML.Scalpel: hasClass :: String -> AttributePredicate
- Text.HTML.Scalpel: html :: (StringLike str, Selectable str s) => s -> Scraper str str
+ Text.HTML.Scalpel: html :: (StringLike str, Selectable s) => s -> Scraper str str
- Text.HTML.Scalpel: htmls :: (StringLike str, Selectable str s) => s -> Scraper str [str]
+ Text.HTML.Scalpel: htmls :: (StringLike str, Selectable s) => s -> Scraper str [str]
- Text.HTML.Scalpel: select :: (StringLike str, Selectable str s) => s -> [Tag str] -> [[Tag str]]
+ Text.HTML.Scalpel: select :: (StringLike str, Selectable s) => s -> [Tag str] -> [[Tag str]]
- Text.HTML.Scalpel: text :: (StringLike str, Selectable str s) => s -> Scraper str str
+ Text.HTML.Scalpel: text :: (StringLike str, Selectable s) => s -> Scraper str str
- Text.HTML.Scalpel: texts :: (StringLike str, Selectable str s) => s -> Scraper str [str]
+ Text.HTML.Scalpel: texts :: (StringLike str, Selectable s) => s -> Scraper str [str]
- Text.HTML.Scalpel: toSelector :: Selectable str s => s -> Selector str
+ Text.HTML.Scalpel: toSelector :: Selectable s => s -> Selector

Files

scalpel.cabal view
@@ -1,5 +1,5 @@ name:                scalpel-version:             0.1.3.1+version:             0.2.0 synopsis:            A high level web scraping library for Haskell. description:     Scalpel is a web scraping library inspired by libraries like Parsec and@@ -21,7 +21,7 @@ source-repository this   type:     git   location: https://github.com/fimad/scalpel.git-  tag:      v0.1.3.1+  tag:      v0.2.0  library   other-extensions:@@ -47,8 +47,7 @@       ,   tagsoup       >= 0.12.2       ,   text   default-extensions:-          OverloadedStrings-      ,   ParallelListComp+          ParallelListComp       ,   PatternGuards   ghc-options: -W 
src/Text/HTML/Scalpel.hs view
@@ -40,7 +40,7 @@ -- -- -- The following is an example that demonstrates most of the features provided--- by this library. Supposed you have the following hypothetical HTML located at+-- by this library. Suppose you have the following hypothetical HTML located at -- @\"http://example.com/article.html\"@ and you would like to extract a list of -- all of the comments. --@@ -54,7 +54,7 @@ -- >       <div class='comment container'> -- >         <span class='comment author'>Bill</span> -- >         <img class='comment image' src='http://example.com/cat.gif' />--- >       </div>\+-- >       </div> -- >       <div class='comment container'> -- >         <span class='comment author'>Susan</span> -- >         <div class='comment text'>WTF!?!</div>
src/Text/HTML/Scalpel/Internal/Scrape.hs view
@@ -58,7 +58,7 @@ -- -- This function will match only the first set of tags matching the selector, to -- match every set of tags, use 'chroots'.-chroot :: (TagSoup.StringLike str, Selectable str s)+chroot :: (TagSoup.StringLike str, Selectable s)        => s -> Scraper str a -> Scraper str a chroot selector (MkScraper inner) = MkScraper                                   $ join . (inner <$>)@@ -68,7 +68,7 @@ -- the inner scraper as if it were scraping a document that consists solely of -- the tags corresponding to the selector. The inner scraper is executed for -- each set of tags matching the given selector.-chroots :: (TagSoup.StringLike str, Selectable str s)+chroots :: (TagSoup.StringLike str, Selectable s)         => s -> Scraper str a -> Scraper str [a] chroots selector (MkScraper inner) = MkScraper                                    $ return . mapMaybe inner . select selector@@ -78,12 +78,12 @@ -- -- This function will match only the first set of tags matching the selector, to -- match every set of tags, use 'texts'.-text :: (TagSoup.StringLike str, Selectable str s) => s -> Scraper str str+text :: (TagSoup.StringLike str, Selectable s) => s -> Scraper str str text s = MkScraper $ withHead tagsToText . select s  -- | The 'texts' function takes a selector and returns the inner text from every -- set of tags matching the given selector.-texts :: (TagSoup.StringLike str, Selectable str s) => s -> Scraper str [str]+texts :: (TagSoup.StringLike str, Selectable s) => s -> Scraper str [str] texts s = MkScraper $ withAll tagsToText . select s  -- | The 'html' function takes a selector and returns the html string from the@@ -91,12 +91,12 @@ -- -- This function will match only the first set of tags matching the selector, to -- match every set of tags, use 'htmls'.-html :: (TagSoup.StringLike str, Selectable str s) => s -> Scraper str str+html :: (TagSoup.StringLike str, Selectable s) => s -> Scraper str str html s = MkScraper $ withHead tagsToHTML . select s  -- | The 'htmls' function takes a selector and returns the html string from every -- set of tags matching the given selector.-htmls :: (TagSoup.StringLike str, Selectable str s) => s -> Scraper str [str]+htmls :: (TagSoup.StringLike str, Selectable s) => s -> Scraper str [str] htmls s = MkScraper $ withAll tagsToHTML . select s  -- | The 'attr' function takes an attribute name and a selector and returns the@@ -105,16 +105,19 @@ -- -- This function will match only the opening tag matching the selector, to match -- every tag, use 'attrs'.-attr :: (Show str, TagSoup.StringLike str, Selectable str s)-     => str -> s -> Scraper str str-attr name s = MkScraper $ join . withHead (tagsToAttr name) . select s+attr :: (Show str, TagSoup.StringLike str, Selectable s)+     => String -> s -> Scraper str str+attr name s = MkScraper+            $ join . withHead (tagsToAttr $ TagSoup.castString name) . select s  -- | The 'attrs' function takes an attribute name and a selector and returns the -- value of the attribute of the given name for every opening tag that matches -- the given selector.-attrs :: (Show str, TagSoup.StringLike str, Selectable str s)-     => str -> s -> Scraper str [str]-attrs name s = MkScraper $ fmap catMaybes . withAll (tagsToAttr name) . select s+attrs :: (Show str, TagSoup.StringLike str, Selectable s)+     => String -> s -> Scraper str [str]+attrs name s = MkScraper+             $ fmap catMaybes . withAll (tagsToAttr nameStr) . select s+    where nameStr = TagSoup.castString name  withHead :: (a -> b) -> [a] -> Maybe b withHead _ []    = Nothing
src/Text/HTML/Scalpel/Internal/Select.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE ImpredicativeTypes #-} {-# OPTIONS_HADDOCK hide #-} module Text.HTML.Scalpel.Internal.Select (     select@@ -14,13 +15,13 @@ -- | The 'select' function takes a 'Selectable' value and a list of -- 'TagSoup.Tag's and returns a list of every subsequence of the given list of -- Tags that matches the given selector.-select :: (TagSoup.StringLike str, Selectable str s)+select :: (TagSoup.StringLike str, Selectable s)        => s -> [TagSoup.Tag str] -> [[TagSoup.Tag str]] select s = selectNodes nodes     where (MkSelector nodes) = toSelector s  selectNodes :: TagSoup.StringLike str-            => [SelectNode str] -> [TagSoup.Tag str] -> [[TagSoup.Tag str]]+            => [SelectNode] -> [TagSoup.Tag str] -> [[TagSoup.Tag str]] selectNodes nodes tags = head' $ reverse results     where results = [concatMap (selectNode s) ts | s  <- nodes                                                  | ts <- [tags] : results]@@ -28,7 +29,7 @@           head' (x:_) = x  selectNode :: TagSoup.StringLike str-           => SelectNode str -> [TagSoup.Tag str] -> [[TagSoup.Tag str]]+           => SelectNode -> [TagSoup.Tag str] -> [[TagSoup.Tag str]] selectNode (SelectNode node attributes) tags = concatMap extractTagBlock nodes     where nodes = filter (checkTag node attributes) $ tails tags selectNode (SelectAny attributes) tags = concatMap extractTagBlock nodes@@ -37,15 +38,15 @@ -- Given a tag name and a list of attribute predicates return a function that -- returns true if a given tag matches the supplied name and predicates. checkTag :: TagSoup.StringLike str-          => str -> [AttributePredicate str] -> [TagSoup.Tag str] -> Bool+          => String -> [AttributePredicate] -> [TagSoup.Tag str] -> Bool checkTag name preds tags@(TagSoup.TagOpen str _:_)-    = name == str && checkPreds preds tags+    = TagSoup.fromString name == str && checkPreds preds tags checkTag _ _ _ = False  checkPreds :: TagSoup.StringLike str-            => [AttributePredicate str] -> [TagSoup.Tag str] -> Bool+            => [AttributePredicate] -> [TagSoup.Tag str] -> Bool checkPreds preds (TagSoup.TagOpen _ attrs:_)-    = and [or [p attr | attr <- attrs] | p <- preds]+    = and [or [checkPred p attr | attr <- attrs] | p <- preds] checkPreds _ _ = False  -- Given a list of tags, return the prefix of the tags up to the closing tag
src/Text/HTML/Scalpel/Internal/Select/Combinators.hs view
@@ -1,3 +1,6 @@+{-# LANGUAGE ImpredicativeTypes #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiWayIf #-} {-# OPTIONS_HADDOCK hide #-} module Text.HTML.Scalpel.Internal.Select.Combinators (     (//)@@ -16,8 +19,7 @@  -- | The '@:' operator creates a 'Selector' by combining a 'TagName' with a list -- of 'AttributePredicate's.-(@:) :: (TagSoup.StringLike str, TagName str tag)-     => tag -> [AttributePredicate str] -> Selector str+(@:) :: TagName tag => tag -> [AttributePredicate] -> Selector (@:) tag attrs = MkSelector [toSelectNode tag attrs] infixl 9 @: @@ -26,26 +28,26 @@ -- -- If you are attempting to match a specific class of a tag with potentially -- multiple classes, you should use the 'hasClass' utility function.-(@=) :: (TagSoup.StringLike str, AttributeName str key)-     => key -> str -> AttributePredicate str-(@=) key value (attrKey, attrValue) =  matchKey key attrKey-                                    && value == attrValue+(@=) :: AttributeName key => key -> String -> AttributePredicate+(@=) key value = MkAttributePredicate $ \(attrKey, attrValue) ->+                                         matchKey key attrKey+                                      && TagSoup.fromString value == attrValue infixl 6 @=  -- | The '@=~' operator creates an 'AttributePredicate' that will match -- attributes with the given name and whose value matches the given regular -- expression.-(@=~) :: (TagSoup.StringLike str, AttributeName str key, RE.RegexLike re str)-      => key -> re -> AttributePredicate str-(@=~) key re (attrKey, attrValue) =  matchKey key attrKey-                                  && RE.matchTest re attrValue+(@=~) :: (AttributeName key, RE.RegexLike re String)+      => key -> re -> AttributePredicate+(@=~) key re = MkAttributePredicate $ \(attrKey, attrValue) ->+       matchKey key attrKey+    && RE.matchTest re (TagSoup.toString attrValue) infixl 6 @=~  -- | The '//' operator creates an 'Selector' by nesting one 'Selector' in -- another. For example, @"div" // "a"@ will create a 'Selector' that matches -- anchor tags that are nested arbitrarily deep within a div tag.-(//) :: (TagSoup.StringLike str, Selectable str a, Selectable str b)-    => a -> b -> Selector str+(//) :: (Selectable a, Selectable b) => a -> b -> Selector (//) a b = MkSelector (as ++ bs)     where (MkSelector as) = toSelector a           (MkSelector bs) = toSelector b@@ -54,10 +56,12 @@ -- | The classes of a tag are defined in HTML as a space separated list given by -- the @class@ attribute. The 'hasClass' function will match a @class@ attribute -- if the given class appears anywhere in the space separated list of classes.-hasClass :: TagSoup.StringLike str => str -> AttributePredicate str-hasClass clazz (attrName, classes)-    | "class" == TagSoup.toString attrName = textClass `elem` classList-    | otherwise                            = False-    where textClass   = TagSoup.castString clazz-          textClasses = TagSoup.castString classes-          classList   = T.split (== ' ') textClasses+hasClass :: String -> AttributePredicate+hasClass clazz = MkAttributePredicate hasClass'+    where+        hasClass' (attrName, classes)+            | "class" == TagSoup.toString attrName = textClass `elem` classList+            | otherwise                            = False+            where textClass   = TagSoup.castString clazz+                  textClasses = TagSoup.castString classes+                  classList   = T.split (== ' ') textClasses
src/Text/HTML/Scalpel/Internal/Select/Types.hs view
@@ -1,10 +1,12 @@ {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ImpredicativeTypes #-} {-# OPTIONS_HADDOCK hide #-} module Text.HTML.Scalpel.Internal.Select.Types (     Selector (..) ,   Selectable (..)-,   AttributePredicate+,   AttributePredicate (..)+,   checkPred ,   Any (..) ,   AttributeName (..) ,   TagName (..)@@ -12,18 +14,15 @@ ,   SelectNode (..) ) where -import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT import qualified Text.HTML.TagSoup as TagSoup+import qualified Text.StringLike as TagSoup   -- | The 'Selectable' class defines a class of types that are capable of being -- cast into a 'Selector' which in turns describes a section of an HTML DOM -- tree.-class Selectable str s | s -> str where-    toSelector :: s -> Selector str+class Selectable s where+    toSelector :: s -> Selector  -- | The 'AttributeName' class defines a class of types that can be used when -- creating 'Selector's to specify the name of an attribute of a tag.@@ -33,8 +32,8 @@ -- names of that value. -- -- In addition there is also the 'Any' type which will match any attribute name.-class AttributeName str k | k -> str where-    matchKey :: k -> str -> Bool+class AttributeName k where+    matchKey :: TagSoup.StringLike str => k -> str -> Bool  -- | The 'TagName' class defines a class of types that can be used when creating -- 'Selector's to specify the name of a tag.@@ -43,81 +42,51 @@ -- 'T.Text', etc). Values of these types refer to tags of the given value. -- -- In addition there is also the 'Any' type which will match any tag.-class TagName str t | t -> str where-    toSelectNode :: t -> [AttributePredicate str] -> SelectNode str+class TagName t where+    toSelectNode :: t -> [AttributePredicate] -> SelectNode  -- | An 'AttributePredicate' is a method that takes a 'TagSoup.Attribute' and -- returns a 'Bool' indicating if the given attribute matches a predicate.-type AttributePredicate str = TagSoup.Attribute str -> Bool+data AttributePredicate+        = MkAttributePredicate+                (TagSoup.StringLike str => TagSoup.Attribute str -> Bool) +checkPred :: TagSoup.StringLike str+          => AttributePredicate -> TagSoup.Attribute str -> Bool+checkPred (MkAttributePredicate p) = p+ -- | 'Any' can be used as a wildcard when constructing selectors to match tags -- and attributes with any name. -- -- For example, the selector @Any \@: [Any \@= \"foo\"]@ matches all tags that -- have any attribute where the value is @\"foo\"@.-data Any str = Any+data Any = Any  -- | 'Selector' defines a selection of an HTML DOM tree to be operated on by -- a web scraper. The selection includes the opening tag that matches the -- selection, all of the inner tags, and the corresponding closing tag.-newtype Selector str = MkSelector [SelectNode str]+newtype Selector = MkSelector [SelectNode] -data SelectNode str = SelectNode str [AttributePredicate str]-                    | SelectAny [AttributePredicate str]+data SelectNode = SelectNode String [AttributePredicate]+                | SelectAny [AttributePredicate] -instance Selectable str (Selector str) where+instance Selectable Selector where     toSelector = id -instance Selectable String String where-    toSelector node = MkSelector [SelectNode node []]--instance Selectable BS.ByteString BS.ByteString where-    toSelector node = MkSelector [SelectNode node []]--instance Selectable LBS.ByteString LBS.ByteString where-    toSelector node = MkSelector [SelectNode node []]--instance Selectable T.Text T.Text where-    toSelector node = MkSelector [SelectNode node []]--instance Selectable LT.Text LT.Text where+instance Selectable String where     toSelector node = MkSelector [SelectNode node []] -instance Selectable str (Any str) where+instance Selectable Any where     toSelector = const (MkSelector [SelectAny []]) -instance AttributeName str (Any str) where+instance AttributeName Any where     matchKey = const . const True -instance AttributeName String String where-    matchKey = (==)--instance AttributeName BS.ByteString BS.ByteString where-    matchKey = (==)--instance AttributeName LBS.ByteString LBS.ByteString where-    matchKey = (==)--instance AttributeName T.Text T.Text where-    matchKey = (==)--instance AttributeName LT.Text LT.Text where-    matchKey = (==)+instance AttributeName String where+    matchKey = (==) . TagSoup.fromString -instance TagName str (Any str) where+instance TagName Any where     toSelectNode = const SelectAny -instance TagName String String where-    toSelectNode = SelectNode--instance TagName BS.ByteString BS.ByteString where-    toSelectNode = SelectNode--instance TagName LBS.ByteString LBS.ByteString where-    toSelectNode = SelectNode--instance TagName T.Text T.Text where-    toSelectNode = SelectNode--instance TagName LT.Text LT.Text where-    toSelectNode = SelectNode+instance TagName String where+    toSelectNode = SelectNode . TagSoup.fromString
tests/TestMain.hs view
@@ -104,7 +104,7 @@             ["<b bar=value>bar</b>"]     ] -selectTest :: Selectable String s => s -> String -> [String] -> Test+selectTest :: Selectable s => s -> String -> [String] -> Test selectTest selector tags expectedText = label ~: expected @=? actual     where         label  = "select (" ++ show tags ++ ")"@@ -133,12 +133,12 @@     ,   scrapeTest             "<a>foo</a><a>bar</a>"             (Just [True, False])-            (map (== "foo") <$> (texts $ "a"))+            (map (== "foo") <$> texts "a")      ,   scrapeTest             "<a key=foo />"             (Just "foo")-            (attr "key" $ "a")+            (attr "key" "a")      ,   scrapeTest             "<a key1=foo/><b key1=bar key2=foo /><a key1=bar key2=baz />"@@ -158,16 +158,16 @@     ,   scrapeTest             "<a><b>foo</b></a><a><c>bar</c></a>"             (Just "foo")-            ((text $ "a" // "b") <|> (text $ "a" // "c"))+            (text ("a" // "b") <|> text ("a" // "c"))      ,   scrapeTest             "<a><b>foo</b></a><a><c>bar</c></a>"             (Just "bar")-            ((text $ "a" // "d") <|> (text $ "a" // "c"))+            (text ("a" // "d") <|> text ("a" // "c")) -    ,   scrapeTest "<img src='foobar'>" (Just "foobar") (attr "src" $ "img")+    ,   scrapeTest "<img src='foobar'>" (Just "foobar") (attr "src" "img") -    ,   scrapeTest "<img src='foobar' />" (Just "foobar") (attr "src" $ "img")+    ,   scrapeTest "<img src='foobar' />" (Just "foobar") (attr "src" "img")     ]  scrapeTest :: (Eq a, Show a) => String -> Maybe a -> Scraper String a -> Test