最近有一个项目,要把别人网站上一些数据扒下来。(其实就是一个查课表)
通过HTTP GET得到网页HTML源码倒是不难……难点在于这个网页内容非常混乱,有的时候格式都会发生变化。
我最开始用Python做,可以直接把网页建立Dom。最简单的方法还是用jQuery,轻轻松松就可以把网站上的特定内容处理掉。
但这个项目是用ASP.Net结合C#语言做的。我找了很多方案,都很费劲。
下面这几个方案都是挺费劲的示例……告诉大家不要学啊
方案一:根据网站建立对应的类。然后过XML反序列化
这个方法很作死,首先建立对应的类就够建一整天的。坑爹在C#是强类型的语言。没法直接对着这个对象操作。只能先建立类。
好像有一些库可以让这个过程稍微舒服点。比如xpath。
方案二:自己用String的substring和正则表达式这些处理HTML
这样写确实可行。但我写的正则表达式和Substring实在太难维护了。代码丑陋不堪,效率低下,还经常神之Exception。
一个比较高效的方案
最后我还是找到了一个比较可行的方法。大概方法是用到了一个叫做HSharp的库。用来以一种弱类型语言的处理方式来应用在C#,处理HTML。
HSharp在Github上的主页:https://github.com/Obisoft2017/HSharp
看上去还是蛮不错的。开源免费,源码也很短。
类似的库也有HTMLagility。那个要复杂不少。C#自带一个Document,那个比较官方,但依赖WebBrowser对象,而且……速度慢的一笔。
我主要用它进行HTML“反序列化”了。
(严格意义上它的工作方式不是强类型的反序列化,而是把各个标签都塞进了字典里。起码这样还是可以查询了)
首先在VS的PM里执行命令:
1 Install-Package Obisoft.HSharp
然后直接使用它的HtmlConvert.DeserializeHtml方法来反序列化HTML。官网的示例都挺清楚。
using Obisoft.HSharp.Models; using System; namespace Obisoft.HSharp { class Example { public static void Main(string[] args) { var NewDocument = HtmlConvert.DeserializeHtml($@" <html> <head> <meta charset={""utf-8""}> <meta name={""viewport""}> <title>Example</title> </head> <body> Some Text <table> <tr>OneLine</tr> <tr>TwoLines</tr> <tr>ThreeLines</tr> </table> Other Text </body> </html>"); Console.WriteLine(NewDocument["html"]["head"]["meta",0].Properties["charset"]); Console.WriteLine(NewDocument["html"]["head"]["meta",1].Properties["name"]); foreach (var Line in NewDocument["html"]["body"]["table"]) { Console.WriteLine(Line.Son); } } }
在上面代码中,反序列化了HTML,并对这段HTML的两个meta的charset和name属性值进行了打印。然后遍历了table的所有元素,打印了元素的内容。
输出结果:
utf-8 viewport OneLine TwoLines ThreeLines
回到项目
我试了试反序列化一个真实的网站。这里拿Obisoft的主页举例子吧。他们的网站属于很复杂很复杂的了。
这里假想要获取他们网站中随意一个数值。这里我获取一下他页面上一个普通数字:
仔细检查他们代码后,这个数字出现在第5个section标签下面N重div的迭代里的一个span里。编写代码:
var WebSiteResult = HtmlConvert.DeserializeHtml(new Uri("http://www.obisoft.com.cn/")); Console.WriteLine(NeuAaoResult["html"]["body"]["section", 5]["div"]["div"]["div"]["div"]["div"]["div", 2]["span"].Son);
果然,程序成功输出了10. 速度还是挺快的。
考虑到这个库确实挺好用而且足够强大。准备研究研究,去写一些接口。它好像还有挺多方法,应该也可以构建HTML。