Парсер хабрахабра на 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/