我正在使用 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對每本書的標題、價格和評級進行刮擦,對價格進行一些格式化以獲得浮點數,并將其作為 type 回傳Entry。scrapePage獲取與結果頁碼相對應的數字,抓取該頁面以獲取IO (Maybe [Entry]),格式化它 - 在這種情況下,過濾 5 星級書籍并按價格排序 - 并列印每個條目。main執行scrapePage第 1 至 10 頁。
我遇到的問題是我的代碼抓取、過濾和排序每個頁面,而我想抓取所有頁面然后過濾和排序。
兩頁(在 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
這將回傳一個由第 1 頁和第 2 頁的結果組成的串列,然后我可以列印該串列,但我不知道如何為所有 50 個結果頁面實作此功能。幫助將不勝感激。
uj5u.com熱心網友回復:
直接回傳條目串列,不做任何處理(也可以在這個階段進行過濾)
-- 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
mapConcurrently此外,您可以輕松地使您的代碼與from asyncpackage并發
main = do
entries <- concat <$> mapConcurrently scrapePage [1 .. 20]
print $ process entries
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/440929.html
