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

下面是用 AngleSharp 的代码:

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

基本一模一样.

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

 
 
标签: AngleSharp

HtmlAgilityPack + Fizzler的更多相关文章

  1. c#中的解析HTML组件 -- (HtmlAgilityPack,Jumony,ScrapySharp,NSoup,Fizzler)

    做数据抓取,网络爬虫方面的开发,自然少不了解析HTML源码的操作.那么问题来了,到底.NET如何来解析HTML,有哪些解析HTML源码的好用的,有效的组件呢?   作者在开始做这方面开发的时候就被这些 ...

  2. C#爬虫(04):HtmlAgilityPack解析html文档

    原文链接 https://www.cnblogs.com/springsnow/p/13278283.html 目录 一.爬虫概述 1.使用浏览器获取页面源码 2.HTML解析组件 二.HtmlAgi ...

  3. .NET解析HTML库集合

    CsQuery AngleSharp Jumony HtmlAgilityPack Fizzler ScrapySharp NSoup

  4. .NET下各种可用的HTML解析组件

    做数据抓取,网络爬虫方面的开发,自然少不了解析HTML源码的操作.那么问题来了,到底.NET如何来解析HTML,有哪些解析HTML源码的好用的,有效的组件呢?   作者在开始做这方面开发的时候就被这些 ...

  5. 抓取网站数据不再是难事了,Fizzler(So Easy)全能搞定

    首先从标题说起,为啥说抓取网站数据不再难(其实抓取网站数据有一定难度),SO EASY!!!使用Fizzler全搞定,我相信大多数人或公司应该都有抓取别人网站数据的经历,比如说我们博客园每次发表完文章 ...

  6. FizzlerEx —— 另一个HtmlAgilityPack的CSS选择器扩展,

    之前我介绍过HtmlAgilityPack的CSS选择器扩展——ScrapySharp,它可以非常方便的实现通过CSS选择器表达式来查询HtmlNode.今天在使用的过程中,发现它不支持nth-chi ...

  7. c# & Fizzler to crawl web page in a certain website domain

    使用fizzler [HtmlAgilityPackExtension]和c#进行网页数据提取:fizzler是HtmlAgilityPack的一个扩展,支持jQuery Selector: 提取数据 ...

  8. Fizzler

    Fizzler 抓取网站数据不再是难事了,Fizzler(So Easy)全能搞定 首先从标题说起,为啥说抓取网站数据不再难(其实抓取网站数据有一定难度),SO EASY!!!使用Fizzler全搞定 ...

  9. csharp: using HtmlAgilityPack and ScrapySharp reading Url find text

    https://github.com/exaphaser/ScrapySharp https://github.com/zzzprojects/html-agility-pack https://gi ...

随机推荐

  1. ASP.NET跨平台

    ASP.NET跨平台最佳实践 前言 八年的坚持敌不过领导的固执,最终还是不得不阔别已经成为我第二语言的C#,转战Java阵营.有过短暂的失落和迷茫,但技术转型真的没有想象中那么难.回头审视,其实单从语 ...

  2. Javascript J更深层次的理解avascript 基础知识

    eval全局函数 dojo loader会看到如下的功能    var eval_ = new Function('return eval(arguments[0]);'); //Function 函 ...

  3. OC本学习笔记Foundatio框架集

        一.OC数组         OC数组是一个类,它也分不可变数组NSArray和可变数组NSMutableArray. 1➣不可变数组的创建 // 创建一个不可变数组.元素为一个OC字符串对象 ...

  4. Caused by: org.springframework.beans.NotWritablePropertyException

    1.错误叙述性说明 usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -h ...

  5. android内置存储器memory和第三方外部存储disk管理

    缓存管理这里 http://blog.csdn.net/intbird/article/details/38338713 图片处理在这里 http://blog.csdn.net/intbird/ar ...

  6. 高通公司 MSM8K GPT异常原因分析无法开机的问题

    问题分析过程如下面: 一. MSM8916台gpt概率问题:采用QPST emmc software download下载软件工具后,无法开机.例如下面的附图: log分析是userdata分区未成功 ...

  7. SharePoint 2013 添加Ribbon菜单

    原文:SharePoint 2013 添加Ribbon菜单 前言:今天,我们尝试一下添加SharePoint2013的Ribbon菜单,这个Ribbon菜单是由XML定义,JavaScript脚本来实 ...

  8. NSIS脚本:在卸载页面收集信息

    原文 NSIS脚本:在卸载页面收集信息 此功能用于在软件卸载时收集相关信息,以便进行后续改进.实现功能如图: 以下为实现代码: 01 !include nsDialogs.nsh 02 !includ ...

  9. poj 3975&amp;&amp;hdu 1850 (nim)

    //赢得了上风 //从n几年移除堆叠一堆石头,有多少可取的石头堆 # include <stdio.h> # include <string.h> # include < ...

  10. MAC OSX在视图port哪个程序占用,杀死进程的方法

    sudo lsof -i :9000 COMMAND   PID    USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME java    6 ...