网络数据采集(AngleSharp)-使用AngleSharp做html解析

有这么一本 Python 的书: <<Python 网络数据采集>>

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

我准备用.NET Core 及第三方库实现里面所有的例子.

这是第一部分, 主要使用的是 AngleSharp: https://anglesharp.github.io/

(文章的章节书与该书是对应的)

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

发送 Http 请求

 在python里面这样发送 http 请求, 它使用的是python的标准库 urllib:

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

在.NET Core 里面, 你可以使用HttpClient, 相应的C#代码如下:

            var client = new HttpClient();
      HttpResponseMessage response = await client.GetAsync("http://pythonscraping.com/pages/page1.html");
            response.EnsureSuccessStatusCode();
            var responseBody = await response.Content.ReadAsStringAsync();
            Console.WriteLine(responseBody);
            return responseBody;

或者可以简写为:

                var client = new HttpClient();
                var responseBody = await client.GetStringAsync("http://pythonscraping.com/pages/page1.html");
                Console.WriteLine(responseBody);

其结果如下:

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

使用 AngleSharp 解析 html 源码

python里面可以使用 BeautifulSoup 或者 MechanicalSoup 等库对 html 源码进行解析.

而.NET Core 可以使用 AngleSharp, Html Agility Pack, DotnetSpider(国产, 也支持元素抽取).等库来操作 Html 文档.

这里我先使用的是 AngleSharp, AngleSharp 的解析库可以使用标准的 W3C 规范来解析 HTML, MathML, XML, SVG 和 CSS. 它支持.NET Standard 1.0.

安装 AngleSharp

通过 Nuget 即可: https://www.nuget.org/packages/AngleSharp/

Install-Package AngleSharp

或者 dotnet-cli:

dotnet add package AngleSharp

AngleSharp 的一个简单例子

下面这个例子(1.2.2)是把页面中 h1 元素的内容显示出来.

书中 Python 的代码:

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

下面是.NET Core 的C#代码:

 public static async Task ReadWithAngleSharpAsync()
        {
            var htmlSourceCode = await SendRequestWithHttpClientAsync();
            var parser = new HtmlParser();
            var document = await parser.ParseAsync(htmlSourceCode);

            Console.WriteLine($"Serializing the (original) document: {document.QuerySelector("h1").OuterHtml}");
            Console.WriteLine($"Serializing the (original) document: {document.QuerySelector("html > body > h1").OuterHtml}");
        }

在这里 AngleSharp 首先需要创建一个可以循环使用的 HtmlParser(Html 解析器), 然后使用解析器解析 html 源码即可: parser.Parse() 或者异步版本 parser.ParseAsync().

解析返回对象的类型是 IHtmlDocument, 里面是解析好的 DOM. 其中 DOM 是和 AngleSharp 里的类这样对应的:

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

这个图其实是老一点的版本, 新版本的 DOM 模型是稍微有点不同的, 不过你只要理解这个意思就行…

AngleSharp 有很多特点, 但是最重要的特点就是它支持querySelector()querySelectorAll()方法, 就像 DOM 的方法一样.

上面这个例子里, 其 html 的结构大致如下:

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

所以针对返回的 IHtmlDocument对象document, 我们使用 document.QuerySelector(“h1”).OuterHtml, 就可以返回 h1 的 OuterHtml. 而使用 document.QuerySelector(“html > body > h1”).OuterHtml 也是同样的效果, 因为标准的 CSS 选择器是都支持的.

QuerySelector()返回的是一个/0 个元素, 相当于 Linq 的 FirstOrDefault().

其运行结果如下:

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

异常情况处理

发送 Http 请求之后, 可能会发生错误, 例如网页不存在(或者请求时出错), 服务器不存在等等.

针对这些情况, .NET Core 程序会返回 HTTP 错误, 可能是 404 也可能是 500 等. 但是所有的类型HttpClient都会抛出 HttpRequestException, 我们可以这样处理这种异常:

public static async Task ResponseWithErrorsAsync()
        {
            try
            {
                var client = new HttpClient();
                var responseBody = await client.GetStringAsync("http://notexistwebsite");
                Console.WriteLine(responseBody);
            }
            catch (HttpRequestException e)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("nException Caught!");
                Console.WriteLine("Message :{0} ", e.Message);
            }
        }

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

但是即使网页获取成功了, 网页上的内容也并非完全是我们所期待的, 仍可能会抛出异常. 比如说你想要找的标签不存在, 那么就会返回 null, 然后再调用改标签的属性, 就会发生 NullReferenceException.

所以这种情况可以捕获 NullReferenceException, 也可以使用代码判断:

        public static async Task ReadNonExistTagAsync()
        {
            var htmlSourceCode = await SendRequestWithHttpClientAsync();
            var parser = new HtmlParser();
            var document = await parser.ParseAsync(htmlSourceCode);

            var nonExistTag = document.QuerySelector("h8");
            Console.WriteLine(nonExistTag);
            Console.WriteLine($"nonExistTag is null: {nonExistTag is null}");

            try
            {
                Console.WriteLine(nonExistTag.QuerySelector("p").OuterHtml);
            }
            catch (NullReferenceException)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Tag was not found");
            }
        }

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

完整的例子:

        public static async Task RunAllAsync()
        {
            Console.ForegroundColor = ConsoleColor.Red;
            async Task<string> GetTileAsync(string uri)
            {
                var httpClient = new HttpClient();
                try
                {
                    var responseHtml = await httpClient.GetStringAsync(uri);
                    var parser = new HtmlParser();
                    var document = await parser.ParseAsync(responseHtml);
                    var tagContent = document.QuerySelector("body > h8").TextContent;
                    return tagContent;
                }
                catch (HttpRequestException e)
                {
           Console.WriteLine($"{nameof(HttpRequestException)}:");
                    Console.WriteLine("Message :{0} ", e.Message);
                    return null;
                }
                catch (NullReferenceException)
                {
                    Console.WriteLine($"{nameof(NullReferenceException)}:");
                    Console.WriteLine("Tag was not found");
                    return null;
                }
            }

            var title = await GetTileAsync("http://www.pythonscraping.com/pages/page1.html");
            if (string.IsNullOrWhiteSpace(title))
            {
                Console.WriteLine("Title was not found");
            }
            else
            {
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine(title);
            }
        }

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

首先我把请求 Http 返回 HTML 代码的那部分封装成了一个方法以便复用:

        public static async Task<string> GetHtmlSourceCodeAsync(string uri)
        {
            var httpClient = new HttpClient();
            try
            {
                var htmlSource = await httpClient.GetStringAsync(uri);
                return htmlSource;
            }
            catch (HttpRequestException e)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"{nameof(HttpRequestException)}: {e.Message}");
                return null;
            }
        }

CSS 是网络爬虫的福音, 下面这两个元素在页面中可能会出现很多次:

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

我们可以使用 AngleSharp 里面的QuerySelectorAll()方法把所有符合条件的元素都找出来, 返回到一个结果集合里.

        public static async Task FindGreenClassAsync()
        {
            const string url = "http://www.pythonscraping.com/pages/warandpeace.html";
            var html = await GetHtmlSourceCodeAsync(url);
            if (!string.IsNullOrWhiteSpace(html))
            {
                var parser = new HtmlParser();
                var document = await parser.ParseAsync(html);
                var nameList = document.QuerySelectorAll("span > .green");

                Console.WriteLine("Green names are:");
                Console.ForegroundColor = ConsoleColor.Green;
                foreach (var item in nameList)
                {
                    Console.WriteLine(item.TextContent);
                }
            }
            else
            {
                Console.WriteLine("No html source code returned.");
            }
        }

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

非常简单, 和 DOM 的标准操作是一样的.

如果只需要元素的文字部分, 那么就是用其TextContent属性即可.

再看个例子

1. 找出页面中所有的 h1, h2, h3, h4, h5, h6 元素

2. 找出 class 为 green 或 red 的 span 元素.

        public static async Task FindByAttributeAsync()
        {
            const string url = "http://www.pythonscraping.com/pages/warandpeace.html";
            var html = await GetHtmlSourceCodeAsync(url);
            if (!string.IsNullOrWhiteSpace(html))
            {
                var parser = new HtmlParser();
                var document = await parser.ParseAsync(html);

                var headers = document.QuerySelectorAll("*")
.Where(x => new[] { "h1", "h2", "h3", "h4", "h5", "h6" }.Contains(x.TagName.ToLower()));
                Console.WriteLine("Headers are:");
                PrintItemsText(headers);

                var greenAndRed = document.All
.Where(x => x.TagName == "span" && (x.ClassList.Contains("green") || x.ClassList.Contains("red")));
                Console.WriteLine("Green and Red spans are:");
                PrintItemsText(greenAndRed);

                var thePrinces = document.QuerySelectorAll("*").Where(x => x.TextContent == "the prince");
                Console.WriteLine(thePrinces.Count());
            }
            else
            {
                Console.WriteLine("No html source code returned.");
            }

            void PrintItemsText(IEnumerable<IElement> elements)
            {
                foreach (var item in elements)
                {
                    Console.WriteLine(item.TextContent);
                }
            }
        }

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

这里我们可以看到 QuerySelectorAll()的返回结果可以使用 Linq的 Where 方法进行过滤, 这样就很强大了.

TagName属性就是元素的标签名.

此外, 还有一个 document.All, All 属性是该 Document 所有元素的集合, 它同样也支持 Linq.

(该方法中使用了一个本地方法).

由于同时支持 CSS 选择器和 Linq, 所以抽取元素的工作简单多了.

导航树

一个页面, 它的结构可以是这样的:

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

这里面有几个概念:

子标签和后代标签.

子标签是父标签的下一级, 而后代标签则是指父标签下面所有级别的标签.

tr 是 table 的子标签, tr, th, td, img 都是 table 的后代标签.

使用 AngleSharp, 找出子标签可以使用.Children 属性. 而找出后代标签, 可以使用 CSS 选择器.

兄弟标签

找到前一个兄弟标签使用.PreviousElementSibling 属性, 后一个兄弟标签是.NextElementSibling 属性.

父标签

.ParentElement属性就是父标签.

        public static async Task FindDescendantAsync()
        {
            const string url = "http://www.pythonscraping.com/pages/page3.html";
            var html = await GetHtmlSourceCodeAsync(url);
            if (!string.IsNullOrWhiteSpace(html))
            {
                var parser = new HtmlParser();
                var document = await parser.ParseAsync(html);

                var tableChildren = document.QuerySelector("table#giftList > tbody").Children;
                Console.WriteLine("Table's children are:");
                foreach (var child in tableChildren)
                {
                    System.Console.WriteLine(child.LocalName);
                }

                var descendants = document.QuerySelectorAll("table#giftList > tbody *");
                Console.WriteLine("Table's descendants are:");
                foreach (var item in descendants)
                {
                    Console.WriteLine(item.LocalName);
                }

                var siblings = document.QuerySelectorAll("table#giftList > tbody > tr").Select(x => x.NextElementSibling);
                Console.WriteLine("Table's descendants are:");
                foreach (var item in siblings)
                {
                    Console.WriteLine(item?.LocalName);
                }

                var parentSibling = document.All.SingleOrDefault(x => x.HasAttribute("src") && x.GetAttribute("src") == "../img/gifts/img1.jpg")
                    ?.ParentElement.PreviousElementSibling;
                if (parentSibling != null)
                {
                    Console.WriteLine($"Parent's previous sibling is: {parentSibling.TextContent}");
                }
            }
            else
            {
                Console.WriteLine("No html source code returned.");
            }
        }

结果:

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

使用正则表达式

“如果你有一个问题打算使用正则表达式来解决, 那么现在你有两个问题了”.

这里有一个测试正则表达式的网站: https://www.regexpal.com/

目前, AngleSharp 支持通过 CSS 选择器来查找元素, 也可以使用 Linq 来过滤元素, 当然也可以通过多种方式使用正则表达式进行更复杂的查找动作.

关于正则表达式我就不介绍了. 直接看例子.

我想找到页面中所有的满足下列要求的图片, 其 src 的值以../img/gifts/img 开头并且随后跟着数字, 然后格式为.jpg 的图标.

        public static async Task FindByRegexAsync()
        {
            const string url = "http://www.pythonscraping.com/pages/page3.html";
            var html = await GetHtmlSourceCodeAsync(url);
            if (!string.IsNullOrWhiteSpace(html))
            {
                var parser = new HtmlParser();
                var document = await parser.ParseAsync(html);

                var images = document.QuerySelectorAll("img")
.Where(x => x.HasAttribute("src") && Regex.Match(x.Attributes["src"].Value, @"../img/gifts/img.*.jpg").Success);
foreach (var item in images)
                {
                    Console.WriteLine(item.Attributes["src"].Value);
                }

                var elementsWith2Attributes = document.All.Where(x => x.Attributes.Length == 2);
                foreach (var item in elementsWith2Attributes)
                {
                    Console.WriteLine(item.LocalName);
                    foreach (var attr in item.Attributes)
                    {
                        Console.WriteLine($"t{attr.Name} - {attr.Value}");
                    }
                }
            }
            else
            {
                Console.WriteLine("No html source code returned.");
            }
        }

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

这个其实没有任何难度.

但从本例可以看到, 判断元素有没有一个属性可以使用HasAttribute(“xxx”)方法, 可以通过.Attributes索引来获取属性, 其属性值就是.Attributes[“xxx”].Value.

如果不会正则表达式, 我相信多写的 Linq 的过滤代码也差不多能达到要求.

遍历单个域名

就是几个应用的例子, 直接贴代码吧.

打印出一个页面内所有的超链接地址:

        public static async Task TraversingASingleDomainAsync()
        {
            var httpClient = new HttpClient();
            var htmlSource = await httpClient.GetStringAsync("http://en.wikipedia.org/wiki/Kevin_Bacon");

            var parser = new HtmlParser();
            var document = await parser.ParseAsync(htmlSource);
            var links = document.QuerySelectorAll("a");
            foreach (var link in links)
            {
                Console.WriteLine(link.Attributes["href"]?.Value);
            }
        }

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

找出满足下列条件的超链接:

  • 在 id 为 bodyContent 的 div 里
  • url 不包括分号
  • url 以/wiki 开头
        public static async Task FindSpecificLinksAsync()
        {
            var httpClient = new HttpClient();
            var htmlSource = await httpClient.GetStringAsync("http://en.wikipedia.org/wiki/Kevin_Bacon");

            var parser = new HtmlParser();
            var document = await parser.ParseAsync(htmlSource);
            var links = document.QuerySelector("div#bodyContent").QuerySelectorAll("a")
                .Where(x => x.HasAttribute("href") && Regex.Match(x.Attributes["href"].Value, @"^(/wiki/)((?!:).)*$").Success);
            foreach (var link in links)
            {
                Console.WriteLine(link.Attributes["href"]?.Value);
            }
        }

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

随机找到页面里面一个连接, 然后递归调用自己的方法, 直到主动停止:

        private static async Task<IEnumerable<IElement>> GetLinksAsync(string uri)
        {
            var httpClient = new HttpClient();
            var htmlSource = await httpClient.GetStringAsync($"http://en.wikipedia.org{uri}");
            var parser = new HtmlParser();
            var document = await parser.ParseAsync(htmlSource);

            var links = document.QuerySelector("div#bodyContent").QuerySelectorAll("a")
                .Where(x => x.HasAttribute("href") && Regex.Match(x.Attributes["href"].Value, @"^(/wiki/)((?!:).)*$").Success);
            return links;
        }

        public static async Task GetRandomNestedLinksAsync()
        {
            var random = new Random();
            var links = (await GetLinksAsync("/wiki/Kevin_Bacon")).ToList();
            while (links.Any())
            {
                var newArticle = links[random.Next(0, links.Count)].Attributes["href"].Value;
                Console.WriteLine(newArticle);
                links = (await GetLinksAsync(newArticle)).ToList();
            }
        }

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

采集整个网站

首先要了解几个概念:

浅网 surface web: 是互联网上搜索引擎可以直接抓取到的那部分网络.

与浅网对立的就是深网 deep web: 互联网中 90%都是深网.

暗网 Darknet / dark web / dark internet: 它完全是另外一种怪兽. 它们也建立在已有的网络基础上, 但是使用 Tor 客户端, 带有运行在 HTTP 之上的新协议, 提供了一个信息交换的安全隧道. 这类网也可以采集, 但是超出了本书的范围…..

深网相对暗网还是比较容易采集的.

采集整个网站的两个好处:

  • 生成网站地图
  • 收集数据

由于网站的规模和深度, 所以采集到的超链接很多可能是重复的, 这时我们就需要链接去重, 可以使用 Set 类型的集合:

        private static readonly HashSet<string> LinkSet = new HashSet<string>();
        private static readonly HttpClient HttpClient = new HttpClient();
        private static readonly HtmlParser Parser = new HtmlParser();

        public static async Task GetUniqueLinksAsync(string uri = "")
        {
            var htmlSource = await HttpClient.GetStringAsync($"http://en.wikipedia.org{uri}");
            var document = await Parser.ParseAsync(htmlSource);

            var links = document.QuerySelectorAll("a")
                .Where(x => x.HasAttribute("href") && Regex.Match(x.Attributes["href"].Value, @"^(/wiki/)").Success);

            foreach (var link in links)
            {
                if (!LinkSet.Contains(link.Attributes["href"].Value))
                {
                    var newPage = link.Attributes["href"].Value;
                    Console.WriteLine(newPage);
                    LinkSet.Add(newPage);
                    await GetUniqueLinksAsync(newPage);
                }
            }
        }

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

(递归调用的深度需要注意一下, 不然有时候能崩溃).

收集整个网站数据

这个例子相对网站, 包括收集相关文字和异常处理等:

        private static readonly HashSet<string> LinkSet = new HashSet<string>();
        private static readonly HttpClient HttpClient = new HttpClient();
        private static readonly HtmlParser Parser = new HtmlParser();

       public static async Task GetLinksWithInfoAsync(string uri = "")
        {
            var htmlSource = await HttpClient.GetStringAsync($"http://en.wikipedia.org{uri}");
            var document = await Parser.ParseAsync(htmlSource);

            try
            {
                var title = document.QuerySelector("h1").TextContent;
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine(title);

                var contentElement = document.QuerySelector("#mw-content-text").QuerySelectorAll("p").FirstOrDefault();
                if (contentElement != null)
                {
                    Console.WriteLine(contentElement.TextContent);
                }

                var alink = document.QuerySelector("#ca-edit").QuerySelectorAll("span a").SingleOrDefault(x => x.HasAttribute("href"))?.Attributes["href"].Value;
                Console.WriteLine(alink);
            }
            catch (NullReferenceException)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Cannot find the tag!");
            }

            var links = document.QuerySelectorAll("a")
                .Where(x => x.HasAttribute("href") && Regex.Match(x.Attributes["href"].Value, @"^(/wiki/)").Success).ToList();
            foreach (var link in links)
            {
                if (!LinkSet.Contains(link.Attributes["href"].Value))
                {
                    var newPage = link.Attributes["href"].Value;
                    Console.WriteLine(newPage);
                    LinkSet.Add(newPage);
                    await GetLinksWithInfoAsync(newPage);
                }
            }
        }

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

不知前方水深的例子

第一个例子, 寻找随机外链:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using AngleSharp.Parser.Html;

namespace WebScrapingWithDotNetCore.Chapter03
{
    public class CrawlingAcrossInternet
    {
        private static readonly Random Random = new Random();
        private static readonly HttpClient HttpClient = new HttpClient();
        private static readonly HashSet<string> InternalLinks = new HashSet<string>();
        private static readonly HashSet<string> ExternalLinks = new HashSet<string>();
        private static readonly HtmlParser Parser = new HtmlParser();

        public static async Task FollowExternalOnlyAsync(string startingSite)
        {
            var externalLink = await GetRandomExternalLinkAsync(startingSite);
            if (externalLink != null)
            {
                Console.WriteLine($"External Links is: {externalLink}");
                await FollowExternalOnlyAsync(externalLink);
            }
            else
            {
                Console.WriteLine("Random External link is null, Crawling terminated.");
            }
        }

        private static async Task<string> GetRandomExternalLinkAsync(string startingPage)
        {
            try
            {
                var htmlSource = await HttpClient.GetStringAsync(startingPage);
                var externalLinks = (await GetExternalLinksAsync(htmlSource, SplitAddress(startingPage)[0])).ToList();
                if (externalLinks.Any())
                {
                    return externalLinks[Random.Next(0, externalLinks.Count)];
                }

                var internalLinks = (await GetInternalLinksAsync(htmlSource, startingPage)).ToList();
                if (internalLinks.Any())
                {
                    return await GetRandomExternalLinkAsync(internalLinks[Random.Next(0, internalLinks.Count)]);
                }

                return null;
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine($"Error requesting: {e.Message}");
                return null;
            }
        }

        private static string[] SplitAddress(string address)
        {
            var addressParts = address.Replace("http://", "").Replace("https://", "").Split("/");
            return addressParts;
        }

        private static async Task<IEnumerable<string>> GetInternalLinksAsync(string htmlSource, string includeUrl)
        {
            var document = await Parser.ParseAsync(htmlSource);
            var links = document.QuerySelectorAll("a")
                .Where(x => x.HasAttribute("href") && Regex.Match(x.Attributes["href"].Value, $@"^(/|.*{includeUrl})").Success)
                .Select(x => x.Attributes["href"].Value);
            foreach (var link in links)
            {
                if (!string.IsNullOrEmpty(link) && !InternalLinks.Contains(link))
                {
                    InternalLinks.Add(link);
                }
            }
            return InternalLinks;
        }

        private static async Task<IEnumerable<string>> GetExternalLinksAsync(string htmlSource, string excludeUrl)
        {
            var document = await Parser.ParseAsync(htmlSource);

            var links = document.QuerySelectorAll("a")
                .Where(x => x.HasAttribute("href") && Regex.Match(x.Attributes["href"].Value, $@"^(http|www)((?!{excludeUrl}).)*$").Success)
                .Select(x => x.Attributes["href"].Value);
            foreach (var link in links)
            {
                if (!string.IsNullOrEmpty(link) && !ExternalLinks.Contains(link))
                {
                    ExternalLinks.Add(link);
                }
            }
            return ExternalLinks;
        }

        private static readonly HashSet<string> AllExternalLinks = new HashSet<string>();
        private static readonly HashSet<string> AllInternalLinks = new HashSet<string>();

        public static async Task GetAllExternalLinksAsync(string siteUrl)
        {
            try
            {
                var htmlSource = await HttpClient.GetStringAsync(siteUrl);
                var internalLinks = await GetInternalLinksAsync(htmlSource, SplitAddress(siteUrl)[0]);
                var externalLinks = await GetExternalLinksAsync(htmlSource, SplitAddress(siteUrl)[0]);
                foreach (var link in externalLinks)
                {
                    if (!AllExternalLinks.Contains(link))
                    {
                        AllExternalLinks.Add(link);
                        Console.WriteLine(link);
                    }
                }

                foreach (var link in internalLinks)
                {
                    if (!AllInternalLinks.Contains(link))
                    {
                        Console.WriteLine($"The link is: {link}");
                        AllInternalLinks.Add(link);
                        await GetAllExternalLinksAsync(link);
                    }
                }
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine(e);
                Console.WriteLine($"Request error: {e.Message}");
            }
        }
    }
}

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

网络数据采集(AngleSharp)-使用 AngleSharp 做 html 解析

程序有 Bug, 您可以给解决下……

第一部分先到这….主要用的是 AngleSharp. AngleSharp 不止这些功能, 很强大的, 具体请看文档.

由于该书下一部分使用的是 Python 的 Scrapy, 所以下篇文章我也许应该使用 DotNetSpider 了, 这是一个国产的库….

项目的代码在: https://github.com/solenovex/Web-Scraping-With-.NET-Core

原文地址:https://www.cnblogs.com/cgzl/p/8970582.html

© 版权声明

☆ END ☆
喜欢就点个赞吧
点赞0 分享
图片正在生成中,请稍后...