Парсер хабрахабра на F#
Продолжаю изучать F#. Сегодня расскажу о первой программе — парсере хабра.
Для парсинга я выбрал один хаб — «Информационная безопасность».
Страницы этого хаба имеют вид http://habrahabr.ru/hub/infosecurity/pageN/ где N — номер страницы.
Парсить HTML я буду с помощью библиотеки HtmlAgilityPack.
Сначала подготовим несколько служебных функций:
let validPath (path : string) = Array.fold (fun (acc:string) item -> acc.Replace(item, '_')) path (Path.GetInvalidFileNameChars())
Эта функция преобразует строку в корректное имя файла, заменяя невалидные символы.
let isGZipEncoding (contentEncoding: string) = contentEncoding.ToLower().StartsWith("gzip")
Здесь я проверяю, архивирован ли ответ сервера.
let getStreamContent(response : HttpWebResponse) : string = if (isGZipEncoding response.ContentEncoding) then let zip = new GZipStream(response.GetResponseStream(), CompressionMode.Decompress) (new StreamReader(zip, Text.Encoding.UTF8)).ReadToEnd() else (new StreamReader(response.GetResponseStream(), Text.Encoding.UTF8)).ReadToEnd()
Эта функция возвращает ответ сервера и, при необходимости, разархивирует его.
let loadHtml (pageUrl:string) = let client = new WebClient() let request : HttpWebRequest = WebRequest.Create(pageUrl) :?> HttpWebRequest request.Timeout <- 5000 let html = getStreamContent(request.GetResponse() :?> HttpWebResponse) let htmldoc = new HtmlDocument() htmldoc.LoadHtml(html) htmldoc
Здесь я получаю содержимое страницы по ее адресу. Возвращаемое значение — объект HtmlDocument из библиотеки HtmlAgilityPack.
let loadPost(pageUrl:string) = try let htmldoc = loadHtml pageUrl let titlenode = htmldoc.DocumentNode.SelectSingleNode("//span[@class='post_title']") let rootnode = htmldoc.DocumentNode.SelectSingleNode("//div[@class='content html_format']") let filePath = @"D:\temp\habr\" + validPath(titlenode.InnerText) + ".txt" use wr = new StreamWriter(filePath, false) wr.WriteLine(rootnode.InnerText) with | ex -> Console.WriteLine("Exception: " + ex.Message)
Здесь загружается страница со статьей. Ее текст сохраняется в файл, именем которого является заголовок статьи. Заголовок и текст получаются с помощью XPath.
let processHubPage url = try let htmldoc = loadHtml url let nodes = htmldoc.DocumentNode.SelectNodes("//a[@class='post_title']") nodes |> Seq.toList|> List.map(fun x -> x.Attributes.["href"].Value) with | ex -> Console.WriteLine("Exception: " + ex.Message) []
Эта функция обрабатывает страницу хаба и возвращает список ссылок на статьи на этой странице.
Теперь осталось только обработать все страницы хаба. Их количество я посмотрел вручную.
let pages = [ 1 .. 362 ] |> List.map (fun x -> processHubPage (@"http://habrahabr.ru/hub/infosecurity/page" + x.ToString() + "/")) |> List.concat |> List.iter (fun x -> loadPost x)
Про парсинг сайтов на C# можно прочитать здесь https://lsreg.ru/parsing-sajtov-na-c/