private void ExtractPage(WikiPage page)
        {
            this.currentPageLink = new PageLink(page);
            this.currentPage = page;

            if (this.Redirections.ContainsKey(this.currentPageLink))
            {
                // Cette page est une redirection, on l'ignore
                this.Log.Info("Extraction page {0} ignorée, redirection", page.FullName);

                return;
            }

            var pageContent = page.GetContent();

            if (pageContent == null)
            {
                this.Log.Info("Extraction page {0} ignorée, vide", page.FullName);
                return;
            }

            List<string> warnings = new List<string>();

            var formatter = new Formatter();
            string formatted = string.Empty;
            try
            {
                formatted = page.FormatWithPhase3(formatter);
            }
            catch (Exception ex)
            {
                this.Log.Error("Erreur durant la mise en forme de la page {0}, extraction annulée. Détail : {1}", page.FullName, ex);
                warnings.Add("Erreur lors de la mise en page, opération annulée");
            }

            // Traitements personnalisés
            formatted = Regex.Replace(formatted, AnchorHrefPattern, ReplaceLinks);

            var matches = Regex.Matches(formatted, AshxHrefPattern);
            if (matches.Count > 0)
            {
                foreach (Match match in matches)
                {
                    warnings.Add(string.Format("Lien en dur : [url=http://www.pathfinder-fr.org/Wiki/{0}]{0}[/url]", match.Groups["Href"].Value));
                }
            }

            var ns = NameTools.GetNamespace(page.FullName);

            if (string.IsNullOrEmpty(ns))
            {
                ns = ".";
            }

            var fullPath = Path.Combine(outDir, string.Format(@"{0}\{1}.{2}", ns, PageLink.SanitizeName(NameTools.GetLocalName(page.FullName)), this.Extension));
            var dir = Path.GetDirectoryName(fullPath);

            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }

            string newContent;

            if (!string.IsNullOrEmpty(Template))
            {
                // Catégories
                var categoryBuilder = new StringBuilder();
                foreach (var category in page.Categories)
                {
                    if (category != null)
                    {
                        categoryBuilder.AppendFormat("<category>{0}</category>", category.FullName);
                    }
                }

                var categories = categoryBuilder.ToString();

                var currentPageLinks = this.PageLinks[currentPageLink];

                // Liens entrants
                string inLinks = string.Empty;
                foreach (var link in currentPageLinks.EnteringLinks)
                {
                    inLinks += "<link>" + link.FullName + "</link>";
                }

                // Liens sortants
                string outLinks = string.Empty;
                foreach (var link in currentPageLinks.Links)
                {
                    outLinks += "<link>" + link.FullName + "</link>";
                }

                newContent = string.Format(
                    templateContent,
                    formatted, // 0
                    pageContent.Title,
                    categories,
                    inLinks,
                    outLinks,
                    pageContent.LastModified.ToString("u"), // 5
                    page.ProviderGetBackups().Max<int, int?>(i => i) ?? 0,
                    System.Net.WebUtility.HtmlEncode(formatted),
                    pageContent.Content, // 8
                    page.FullName // 9
                );
            }
            else
            {
                newContent = formatted;
            }

            if (File.Exists(fullPath))
            {
                var bytes = Encoding.Default.GetBytes(newContent);

                string newHash = string.Empty;
                string oldHash = string.Empty;

                using (Crc32 crc = new Crc32())
                {
                    foreach (var hashByte in crc.ComputeHash(bytes))
                    {
                        newHash += hashByte.ToString("x2");
                    }

                    using (var fs = File.OpenRead(fullPath))
                    {
                        foreach (var hashByte in crc.ComputeHash(fs))
                        {
                            oldHash += hashByte.ToString("x2");
                        }
                    }
                }

                if (oldHash.Equals(newHash, StringComparison.Ordinal))
                {
                    this.Log.Verbose("Extraction page {0} ignorée, non modifiée", page.FullName);
                    return;
                }
            }

            using (var writer = new StreamWriter(fullPath, false))
            {
                writer.Write(newContent);
            }

            if (warnings.Count == 0)
            {
                this.Log.Verbose("Extraction page {0} terminée", page.FullName);
            }
            else
            {
                this.Log.Warning("Extraction page {0} terminée avec avertissements :", page.FullName);

                foreach (var line in warnings)
                {
                    this.Log.Warning("- {0}", line);
                }
            }
        }