Парсинг сайта на haskell
Опишу пример парсинга сайта(хоть примеров и много), но я постараюсь на основе этой задачи указать ещё несколько "полезностей".
Во первых нам нужно получить страничку которую будем парсить, для этого воспользуемся библиотекой
Network.HTTP.Conduit она нам позволит использовать прокси сервер с авторизацией, подключаем библиотеки:
Далее следует добавление авторизации и самого прокси:
делаем обёртки для библиотечных функций: авторизации и прокси, потому что они могут и не использоваться.
Теперь нам надо собрать все в одном:
на входе получаем адрес авторизацию и прокси на выходе внутренности сайта - все довольно просто. parseUrl - преобразует адрес в запрос. httpLbs - выкачивает страничку. Файл целиком.
Допустим мы хотим получить все ссылки с сайта, с доменом этого сайта, их содержание и путь куда ссылаются, это довольно простая задача. Понадобятся библиотеки:
tagTree - даст нам возможность представить список тегов в виде дерева, теперь необходимо пробежаться по дереву сохраняя путь до элемента, я сделал это так:
Думаю тут ничего сложного в понимании - в результате мы получаем список из которого нужно выделить только нужные нам элементы:
Теперь собираем все в одной функции:
findOlUl :: String -> [(String, String)]
findOlUl urlContent = filterOlUl $ concatMap (treeGo "") (tagTree $ parseTags urlContent)
Во первых нам нужно получить страничку которую будем парсить, для этого воспользуемся библиотекой
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)
Файл целиком
Комментируйте что я написал не так или как было бы лучше или что из этого совсем ужасно - я всегда готов учиться - лучшему написанию)
Комментарии
Отправить комментарий