Парсинг сайта на haskell

Опишу пример парсинга сайта(хоть примеров и много), но я постараюсь на основе этой задачи указать ещё несколько "полезностей".
Во первых нам нужно получить страничку которую будем парсить, для этого воспользуемся библиотекой
Network.HTTP.Conduit она нам позволит использовать прокси сервер с авторизацией, подключаем библиотеки:

module GetPage where
import Network.HTTP.Conduit
import Network
import Data.Maybe()
import qualified Data.ByteString.Lazy.UTF8 as UTF8
import qualified Data.ByteString.Char8 as C8

Далее следует добавление авторизации и самого прокси:

applyAuthorities :: Maybe (String, String) -> Request a -> Request a
applyAuthorities auths request' =
    case auths of
        Just (user, pass) -> applyBasicAuth (C8.pack user) (C8.pack pass) request'
        Nothing -> request'
 
applyProxy:: Maybe (String, Int) -> Request a -> Request a
applyProxy proxy' request' =
 case proxy' of
  Just (proxy_,port_) -> addProxy (C8.pack proxy_) port_ request'
  Nothing -> request'  



делаем обёртки для библиотечных функций: авторизации и прокси, потому что они могут и не использоваться.
Теперь нам надо собрать все в одном:

getPage :: String -> Maybe (String, String) -> Maybe (String, Int) -> IO String
getPage url auths proxy' = withSocketsDo $ do
 request' <- parseUrl url
 let request'' = applyProxy proxy' $ applyAuthorities auths request'
 let request = request'' { checkStatus = _ _ -> Nothing }
 res <- withManager $ httpLbs request
 return $ UTF8.toString $ responseBody res

на входе получаем адрес авторизацию и прокси на выходе внутренности сайта - все довольно просто. parseUrl - преобразует адрес в запрос. httpLbs - выкачивает страничку. Файл целиком.
Допустим мы хотим получить все ссылки с сайта, с доменом этого сайта, их содержание и путь куда ссылаются, это довольно простая задача. Понадобятся библиотеки:

module Parse where
import Text.HTML.TagSoup
import Data.List (tails)
import Text.HTML.TagSoup.Tree



И сама реализация функции:



findA :: String -> String -> [(String, String)]
findA url urlContent = 
 [(x, fromAttrib "href" a) | a:TagText x:_ <- tails $ parseTags urlContent, a ~== "<a href>", (url == take (length url) ( 
 fromAttrib "href" a)) || ('/' == head (fromAttrib "href" a))]

parseTags - преобразует контент страницы в теги. А далее перебираем список на наличие подходящих нам ссылок. Пусть нам нужно ещё получить все элементы списков с информацией о их позиции на странице.
tagTree - даст нам возможность представить список тегов в виде дерева, теперь необходимо пробежаться по дереву сохраняя путь до элемента, я сделал это так:

treeGo :: String -> TagTree String -> [(String, String)]
treeGo ind (TagLeaf (TagText x)) = [(ind, x)]
treeGo ind (TagLeaf x)   = [(ind, show x)]
treeGo ind (TagBranch name _ xs)  = concatMap ((a,b) -> treeGo (ind++" "++name++" "++show b) a) (zip xs [0..(length xs)]) 

Думаю тут ничего сложного  в понимании - в результате мы получаем список из которого нужно выделить только нужные нам элементы:

filterOlUl :: [(String, t)] -> [(String, t)]
filterOlUl xs = [ x | x@(ind, _) <- xs, (elem "ul" $ words ind) || (elem "ol" $ words ind)]

Теперь собираем все в одной функции:

findOlUl :: String -> [(String, String)]
findOlUl urlContent = filterOlUl $ concatMap (treeGo "") (tagTree $ parseTags urlContent)

Файл целиком

Комментируйте что я написал не так или как было бы лучше или что из этого совсем ужасно - я всегда готов учиться - лучшему написанию)

Комментарии

Популярные сообщения из этого блога

Bitrix: кнопка добавить в корзину

Битрикс: какого х*я ты ищешь в неактивных разделах

Битрикс: highloadblock значения в свойстве список