将多次抓取的 scrapeURL 结果连接到一个列表中

Concatenating scrapeURL results from multiples scrapings into one list

我正在使用 Haskell 的 Scalpel 库抓取 https://books.toscrape.com。到目前为止,这是我的代码:

import Text.HTML.Scalpel
import Data.List.Split (splitOn)
import Data.List (sortBy)
import Control.Monad (liftM2)

data Entry = Entry {entName :: String
                   , entPrice :: Float
                   , entRate :: Int
                   } deriving Eq

instance Show Entry where
  show (Entry n p r) = "Name: " ++ n ++ "\nPrice: " ++ show p ++ "\nRating: " ++ show r ++ "/5\n"

entries :: Maybe [Entry]
entries = Just []

scrapePage :: Int -> IO ()
scrapePage num = do
  items <- scrapeURL ("https://books.toscrape.com/catalogue/page-" ++ show num ++ ".html") allItems
  let sortedItems = items >>= Just . sortBy (\(Entry _ a _) (Entry _ b _) -> compare a b)
                          >>= Just . filter (\(Entry _ _ r) -> r == 5)
  maybe (return ()) (mapM_ print) sortedItems

allItems :: Scraper String [Entry]
allItems = chroots ("article" @: [hasClass "product_pod"]) $ do
    p <- text $ "p" @: [hasClass "price_color"]
    t <- attr "href" $ "a"
    star <- attr "class" $ "p" @: [hasClass "star-rating"]
    let fp = read $ flip (!!) 1 $ splitOn "£" p
    let fStar = drop 12 star
    return $ Entry t fp $ r fStar
      where
        r f = case f of
          "One" -> 1
          "Two" -> 2
          "Three" -> 3
          "Four" -> 4
          "Five" -> 5

main :: IO ()
main = mapM_ scrapePage [1..10]

基本上,allItems 抓取每本书的标题、价格和评级,对价格进行一些格式化以获得浮动,然后 returns 将其作为一种类型 EntryscrapePage 获取与结果页码相对应的数字,抓取该页面以获得 IO (Maybe [Entry]),对其进行格式化 - 在本例中,过滤 5 星书籍并按价格排序 - 并打印每个条目。 main 在第 1 到 10 页执行 scrapePage

我 运行 遇到的问题是我的代码抓取、过滤和排序每个页面,而我想抓取所有页面 然后 过滤和排序.

两页(在 GHCi 中)有效的是:

i <- scrapeURL ("https://books.toscrape.com/catalogue/page-1.html") allItems
j <- scrapeURL ("https://books.toscrape.com/catalogue/page-2.html") allItems
liftM2 (++) i j

这个 returns 由第 1 页和第 2 页的结果组成的列表,然后我可以打印它,但我不知道如何为所有 50 个结果页实现此列表。帮助将不胜感激。

只是return条目列表,没有任何处理(或者你可以在这个阶段做过滤)

-- no error handling 
scrapePage :: Int -> IO [Entry]
scrapePage num =
  concat . maybeToList <$> scrapeURL ("https://books.toscrape.com/catalogue/page-" ++ show num ++ ".html") allItems

以后可以一起处理

process = filter (\e -> entRate e == 5) . sortOn entPrice
main = do
  entries <- concat <$> mapM scrapePage [1 .. 10]
  print $ process entries

此外,您可以轻松地使您的代码与 async

中的 mapConcurrently 并发
main = do
 entries <- concat <$> mapConcurrently scrapePage [1 .. 20]
 print $ process entries