• HtmlAgilityPack + Fizzler


    HtmlAgilityPack + Fizzler

    这两天在做个爬虫, 一次任务要下载3万多个页面, 然后从这3万多个页面提取数据.

    以前写过两年的类似的东西, 基本都是写正则表达式, 速度快, 就是写正则表达式老费劲了, 目标网页稍微改动一点就要重写正则.

    后来我用了 HtmlAgilityPack + Fizzler, 很轻松的就处理了.

    昨天, 我找了两个类似 HtmlAgilityPack 的东西:CsQuery 和 AngleSharp

    翻了翻它们的API和说明文档, CsQuery 说能实现 jQuery 的 selector 语法, 我试了试,还真是, :even 这个东西也能使用, 但是在 AngleSharp 里就不支持这个了, HtmlAgilityPack 没有试, 应该也不支持.

    看 AngleSharp , 它给出了这三个东西的性能对比:

    https://github.com/FlorianRappl/AngleSharp/wiki/Performance

    从其中列出的对比结果来看, 确实比 CsQuery 和 HtmlAgilityPack 快不少.

    于是,我放弃了 CsQuery 的牛逼的 selector 和 HtmlAgilityPack 的口碑, 直接在项目中使用了 AngleSharp.

     

    没想到它是个坑爹货:

    今天中午利用吃饭时间, 我运行了这个任务, 回来后发现 这货吃了快 11G 内存, 没看错, 11G! 因为这台电脑总共只有 12G内存! 

    为留证据, 我又跑了一下, 3分20秒, 占用内存高达 1.8G.

    怕冤枉好人, 我把所有自己写的东西都运行了一遍代码分析, 确保没有任何未释放的对象.

    结果这货还是这样吃内存.

    换成 HtmlAgilityPack 后:

    3分20秒的时候, 也不过才 270M 而已. 而且是一边增加,一边释放, 到现在运行了差不多半个小时了, 内存还在 180M 左右徘徊。

    以下是用 HtmlAgilityPack 的代码:

    复制代码
     1 public override IEnumerable<DIRTY_SCHEDULE> Fetch(string ctx, string url = "") {
     2     var doc = new HtmlDocument();
     3     doc.LoadHtml2(ctx);
     4     var root = doc.DocumentNode;
     5     var trs = root.QuerySelectorAll("#accordion2>.accordion-group>.accordion-heading>table>tbody>tr")
     6         .ToList();
     7     for (var i = 0; i < trs.Count(); i = i + 2) {
     8         var tr = trs[i];
     9         var tds = tr.QuerySelectorAll("td").ToList();
    10         var entry = new DIRTY_SCHEDULE {
    11             CARRIER = tds[0].InnerText.Clear(),
    12             ROUTE = tds[1].InnerText.Clear(),
    13             VESSEL = tds[2].InnerText.Clear(),
    14             VOYAGE = tds[3].InnerText.Clear(),
    15             ORGIN = tds[4].InnerText.Clear(),
    16             ETD = tds[5].InnerText.Clear().ToDateTime("yyyy-MM-dd", DateTime.Now),
    17             DEST = tds[6].InnerText.Clear(),
    18             ETA = tds[7].InnerText.Clear().ToDateTime("yyyy-MM-dd", DateTime.Now),
    19             TT = tds[8].InnerText.Clear().ToDecimalOrNull(),
    20             DIRTY_SCHEDULE_TRANSF = this.FetchTransf(trs[i + 1]).ToList(),
    21             SOURCE = url,
    22             APP = "Fetcher.Soushipping",
    23         };
    24 
    25         entry.UNQTAG = entry.GetUNQTag();
    26 
    27         yield return entry;
    28     }
    29 }
    30 
    31 
    32 private IEnumerable<DIRTY_SCHEDULE_TRANSF> FetchTransf(HtmlNode tr) {
    33     var tbls = tr.QuerySelectorAll("table.widget").ToList();
    34     //第一个列出的是起始地
    35     for (var i = 1; i < tbls.Count(); i++) {
    36         var rows = tbls[i].QuerySelectorAll("tr").ToList();
    37         if (rows.Count == 3)
    38             yield return new DIRTY_SCHEDULE_TRANSF {
    39                 VESSEL = rows[0].InnerText.Clear(),
    40                 AT = rows[1].QuerySelector("td").InnerText.Clear(), //rows[1].FirstChild.Text().Trim(),
    41                 VOYAGE = rows[2].InnerText.Clear(),
    42                 SEQ = i - 1
    43             };
    44     }
    45 }
    复制代码

    下面是用 AngleSharp 的代码:

    复制代码
     1 public override IEnumerable<DIRTY_SCHEDULE> Fetch(string ctx, string url = "") {
     2     var dom = DocumentBuilder.Html(ctx);
     3     //不支持 even
     4     //var trs = dom.QuerySelectorAll("#accordion2 table tbody tr:even");
     5     var trs = dom.QuerySelectorAll("#accordion2>.accordion-group>.accordion-heading>table>tbody>tr");
     6     for (var i = 0; i < trs.Length; i = i + 2) {
     7         var tr = trs[i];
     8         var tds = tr.QuerySelectorAll("td");
     9         var entry = new DIRTY_SCHEDULE {
    10             CARRIER = tds[0].Text(),
    11             ROUTE = tds[1].Text().Trim(),
    12             VESSEL = tds[2].Text().Trim(),
    13             VOYAGE = tds[3].Text().Trim(),
    14             ORGIN = tds[4].Text().Trim(),
    15             ETD = tds[5].Text().Trim().ToDateTime("yyyy-MM-dd", DateTime.Now),
    16             DEST = tds[6].Text().Trim(),
    17             ETA = tds[7].Text().Trim().ToDateTime("yyyy-MM-dd", DateTime.Now),
    18             TT = tds[8].Text().Trim().ToDecimalOrNull(),
    19             DIRTY_SCHEDULE_TRANSF = this.FetchTransf(trs[i + 1]).ToList(),
    20             SOURCE = url,
    21             APP = "Fetcher.Soushipping",
    22         };
    23 
    24         entry.UNQTAG = entry.GetUNQTag();
    25 
    26         yield return entry;
    27     }
    28 }
    29 
    30 private IEnumerable<DIRTY_SCHEDULE_TRANSF> FetchTransf(IElement tr) {
    31     var tbls = tr.QuerySelectorAll("table.widget");
    32     //第一个列出的是起始地
    33     for (var i = 1; i < tbls.Length; i++) {
    34         var rows = tbls[i].QuerySelectorAll("tr");
    35         if (rows.Length == 3)
    36             yield return new DIRTY_SCHEDULE_TRANSF {
    37                 VESSEL = rows[0].Text().Trim(),
    38                 AT = rows[1].QuerySelector("td").Text().Trim(), //rows[1].FirstChild.Text().Trim(),
    39                 VOYAGE = rows[2].Text().Trim(),
    40                 SEQ = i - 1
    41             };
    42     }
    43 }
    复制代码

    基本一模一样.

    看一下 IElement , 这货跟本就没有继承 IDisposable接口, 所以, 也就没有释放不释放这一说.

     
     
    标签: AngleSharp
  • 相关阅读:
    Docker01 centos系统安装、centos安装docker、docker安装mongoDB
    WebFlux03 SpringBoot WebFlux实现CRUD
    WebFlux02 SpringBoot WebFlux项目骨架搭建
    WebFlux01 webflux概念、异步servlet、WebFlux意义
    ReactiveStream03
    ReactiveStream02
    ReactiveStream01
    Stream03
    python爬虫2
    python爬虫1
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4261230.html
Copyright © 2020-2023  润新知