本文为大大维原创,最早于博客园发表,转载请注明出处!!!

第一章 简介

本文主要介绍了在.NET下利用优秀的HTML解析组件HtmlAgilityPack开发的一个吉林大学校内通知oa.jlu.edu.cn的爬取器。尽管.Net下解析HTML文件有很多种选择,包括微软自己也提供MSHTML用于manipulate HTML文件。但是,经过我多方查阅资料和自己的尝试,Html Agility Pack逐步脱颖而出:它是Stackoverflow网站上推荐最多的C# HTML解析器。HAP开源,易用,解析速度快。因此,本人最终选择使用HAP作为爬虫的开发的HTML解析组件。

笔者实现的爬虫OAWebScraping可以实现由使用者指定时间的(从当前时间往前n天)的,对oa.jlu.edu.cn上的所有所有通知和新闻,包括时间、标题、发表部门、正文全文和附件的爬取,并且按照时间->发表部门->标题->正文及附件的树形结构将爬取的文件保存在硬盘中。以下是使用截图:

开始爬虫,输入爬取的时间范围:

爬取成功,对于没有发放通知的日期给予提示:

文件的组织结构:

爬取的文件按照树形结构

时间->发表部门->标题->正文及附件

保存在硬盘:

按时间:

按发表部门:

按标题:

正文及附件:

正文:

附件:

第二章    研究方法

(1) How to use HAP?1

1. 下载http://htmlagilitypack.codeplex.com/

2. 解压

3. 在Visual Studio Solution里,右击project -> add reference -> 选择解压文件夹里的HTMLAgilityPack.dll -> 确定

4. 代码头部加入 using HtmlAgilityPack;

Done!

(2)HAP 概述:2

在HtmlAgilityPack中常用到的类有HtmlDocument、HtmlNodeCollection、            HtmlNode和HtmlWeb等.其流程一般是先获取HTML,这个可以通过HtmlDocument的Load()或LoadHtml()来加载静态内容,或者也可以HtmlWeb的Get()或Load()方法来加载网络上的URL对应的HTML。

得到了HtmlDocument的实例之后,就可以用HtmlDocument的DocumentNode属性,这是整个HTML文档的根节点,它本身也是一个HtmlNode,然后就可以利用HtmlNode的SelectNodes()方法返回多个HtmlNode的集合对象HtmlNodeCollection,也可以利用HtmlNode的SelectSingleNode()方法返回单个HtmlNode。

(3)XPath 概述:[2]

HtmlAgilityPack是一个支持用XPath来解析HTML的类库,避免了采用正则表达式一步步将无关的HTML注释及JS代码部分删除掉,然后再用正则表达式找出需要提取的部分,可以说使用正则表达式来做是一个比较繁琐的过程,以下是一个简单的XPath介绍:XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的。
 下面列出了最有用的路径表达式:
 nodename:选取此节点的所有子节点。 
 /:从根节点选取。 
 //:从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 
 .:选取当前节点。 
 ..:选取当前节点的父节点。

(4)关键代码分析:

Main函数逻辑:通过GetItemWebs(range)找出需要爬取的url簇,然后用etItemWeb(itemWebUrl)爬取文件并下载。

拼接yyyyMMdd天的oa通知界面url。

加载Html并查找当前页,由于oa中通知跳转连接均在<div> class“li rel”下 的<a>

Class “font14” 中,爬取出其href并拼接成相关具体通知的url,压入一个List(itemWebs)中,由于有些时段没有发通知,这样会导致nodes为空,会使得nodes.Select()发生空指针异常,因此引入异常机制,保证程序正常运行,并对没有发通知的日期进行提示。

以上是GetItemWebs(int range)的主要逻辑。该函数返回一个所有需要查找的通知的url的string s数组。

在GetItemWeb(string itemWebUrl)函数中,通过url初始化uri,解析uri获得title和orgname。

解析HtmlDocument并通过<div> class ‘content_time’ 获得通知发布时间。

解析HtmlDocument中<div> class ‘content_font fontsize immmge’中的p标签,来拼接通知正文。

按照“时间->发表部门->标题->正文”树形结构将正文存储在硬盘

下载附件,并将附件和对应的正文存在一个文件中。

以上是GetItemWeb(string itemWebUrl)的主要逻辑。

第三章 数据分析和结果

由于时间限制,笔者爬取了2017/6/15-2017/6/20之间6天的所有数据,其中2017/6/18没有通知,没有数据。另外17号只有研究院发放的一条通知,其他时段基本上每天都有10个左右的部门发放15条左右的通知。查日历可知,17,18号为周末。

可见在工作日中,我校的教务工作都在积极进行,另外党委组织部和宣传部是通知发送的大头。具体可见ScrapingFiles文件夹(爬取数据的存储文件夹)。

第四章 结论

笔者在.NET下利用优秀的HTML解析组件HtmlAgilityPack开发的一个吉林大学校内通知oa.jlu.edu.cn的爬取器,测试好用并且挺实用。

第五章 参考文献

【1】Brian网络畅游的记录的博客《开源项目Html Agility Pack实现快速解析Html

http://www.cnblogs.com/GmrBrian/p/6201237.html

【2】51Ct0博主周公的博客《HTML解析利器HtmlAgilityPack》:

http://zhoufoxcn.blog.51cto.com/792419/595344/

【3】CSDN无极世界博主的博客《HtmlAgilityPack——解析html和采集网页的神兵利器http://blog.csdn.net/dalmeeme/article/details/7191793

【4】HAP官网的开发文档

http://htmlagilitypack.codeplex.com/

第六章 代码

 using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using HtmlAgilityPack; //作者:大大维
namespace OA
{
class Program
{
static void Main()
{
Console.WriteLine("用于查询距今X天的oa通知!!!欢迎使用!!!");
Console.WriteLine("请输入查询的范围(距现在多少天,0表示当天,1表示昨天+今天,以此类推):");
var range = int.Parse(Console.ReadLine());
Console.WriteLine("开始爬取!!!");
var itemWebUrls = GetItemWebs(range);
foreach (var itemWebUrl in itemWebUrls)
{
GetItemWeb(itemWebUrl);
}
Console.WriteLine("爬取完毕!!!");
Console.ReadLine();
} private static string[] GetItemWebs(int range)
{
/*在HtmlAgilityPack中常用到的类有HtmlDocument、HtmlNodeCollection、
HtmlNode和HtmlWeb等.其流程一般是先获取HTML,这个可以通过HtmlDocument的Load()或LoadHtml()来加载静态内容,
或者也可以HtmlWeb的Get()或Load()方法来加载网络上的URL对应的HTML。
得到了HtmlDocument的实例之后,就可以用HtmlDocument的DocumentNode属性,
这是整个HTML文档的根节点,它本身也是一个HtmlNode,
然后就可以利用HtmlNode的SelectNodes()方法返回多个HtmlNode的集合对象HtmlNodeCollection,
也可以利用HtmlNode的SelectSingleNode()方法返回单个HtmlNode。 */
var itemWebs = new List<string>();//由于每页网站的通知内容数目不定,采用List较好
//@忽略转义字符
var baseUrl = @"https://oa.jlu.edu.cn/defaultroot/PortalInformation!jldxList.action?channelId=179577&searchnr=";
var web = new HtmlWeb();
for (var i = range; i >= ; --i)
{
//拼接yyyyMMdd天的oa通知界面
DateTime dt = DateTime.Now.AddDays(-i);
var dtStr = string.Format("{0:yyyyMMdd}", dt);
var url = baseUrl + dtStr + "&searchlx=3";
//加载HTML静态内容
var htmlDoc = web.Load(url);
//获取每个子页面的url并存入items中
var nodes = htmlDoc.DocumentNode.SelectNodes("//div[@class='li rel']/a[@class='font14']");
try
{
var pageItemUrl = nodes.Select(node => "https://oa.jlu.edu.cn/defaultroot/"
+ node.GetAttributeValue("href", "")).ToList();
itemWebs.AddRange(pageItemUrl);
}
catch(Exception)//当当天没有通知下发时,nodes为空,在nodes.Select()中抛出空指针异常
{
Console.WriteLine(dtStr+"没有通知下发!!!");
}
}
return itemWebs.ToArray();
} private static void GetItemWeb(string itemWebUrl)
{
//uri解析
var uri = new Uri(itemWebUrl);
var title = HttpUtility.ParseQueryString(uri.Query).Get("title");//title即url的title部分
var orgname = HttpUtility.ParseQueryString(uri.Query).Get("orgname");//orgname即url的orgname部分 var web = new HtmlWeb();
var htmlDoc = web.Load(itemWebUrl); var contentTime = htmlDoc.DocumentNode.SelectSingleNode("//div[@class='content_time']").InnerText;
var indexOfBlank = contentTime.IndexOf(' ');
var time = contentTime.Substring(, indexOfBlank); var contentNode = htmlDoc.DocumentNode.SelectSingleNode("//div[@class='content_font fontsize immmge']");
var content = string.Empty;
var ps = contentNode.SelectNodes("p");
if (ps != null)
{
foreach (var p in ps)
{
content += p.InnerText.Replace("&nbsp;", " ").Replace(' ', ' ') + "\n";
}
}
else
{
content = contentNode.InnerHtml.TrimStart().Replace("&nbsp;", " ").Replace(' ', ' ').Replace("<br>", "\n");
} //创建ScrapingFiles文件夹保存文件
Directory.CreateDirectory($"../../../ScrapingFiles/{time}/{orgname}/{title}");
File.WriteAllText($"../../../ScrapingFiles/{time}/{orgname}/{title}/"+title + ".txt", content); //下载相关附件
var fileNodes = htmlDoc.DocumentNode.SelectNodes("//td[@width='93%']/span");
if (fileNodes != null)
{
var webClient = new WebClient();
foreach (var node in fileNodes)
{
var fileId = node.GetAttributeValue("id", "");//寻找文件ID
var fileTitle = node.GetAttributeValue("title", "");//寻找文件title
var articleId = HttpUtility.ParseQueryString(uri.Query).Get("id"); var res = webClient.DownloadString(
"https://oa.jlu.edu.cn/defaultroot/rd/jldx/BASEEncoderAjax.jsp?" +
$"res={fileId}@{fileTitle}@{articleId}");
res = res.Trim().Replace("\n", "");
webClient.DownloadFile("https://oa.jlu.edu.cn/defaultroot/rd/attachdownload.jsp?res=" + res,
$"../../../ScrapingFiles/{time}/{orgname}/{title}/" + fileTitle);
}
}
}
}
}

[项目记录]一个.net下使用HAP实现的吉大校园通知网爬虫工具:OAWebScraping的更多相关文章

  1. 记录一个linux下批处理的代码

    DATA_DIR=/home/liupan/.navinsight/data/dataset_rec SHELL_DIR=/home/liupan/workspace/nvi_postprocessi ...

  2. 一个tomcat下部署多个项目或一个服务器部署多个tomcat

    最近需要把两个项目同时部署到服务器上,于是研究了一下,页借鉴了很多别人的方法,把过程记录下来,以儆效尤. 目录: 1,一个tomcat下同时部署两个项目(多个项目可以参考) 1.1项目都放在webap ...

  3. 记录-Intellij Idea下以Tomcat运行Web项目时的位置问题

    今天本来准备把原来的一个Web项目导入到Idea下,之前这个项目是用eclipse写的,容器用的tomcat,首先导入前我把一些没用的配置文件都给删了,像什么.eclipse..setting什么的, ...

  4. Nginx高级配置,同1台机器部署多个tomcat、配置多个域名,每个域名指向某一个tomcat下的项目,共用Nginx80端口访问;

    需求说明: 只有一台服务器和一个公网IP,多个项目部署在这台机器上面,且每个项目使用一个单独的域名访问,域名访问时都通过Nginx的80端口访问.(如下图所示) 配置过程: 一.tomcat的serv ...

  5. elasticsearch type类型创建时注意项目,最新的elasticsearch已经不建议一个索引下多个type

    https://www.elastic.co/guide/cn/elasticsearch/guide/current/mapping.html如果有两个不同的类型,每个类型都有同名的字段,但映射不同 ...

  6. IOS客户端Coding项目记录导航

    IOS客户端Coding项目记录(一) a:UITextField设置出现清除按键 b:绘画一条下划线  表格一些设置 c:可以定义表头跟底部视图(代码接上面) d:隐藏本页的导航栏 e:UIEdge ...

  7. 用spring+hibernate+struts 项目记录以及常用的用法进等

    一.hibernate1. -----BaseDao------ // 容器注入 private SessionFactory sessionFactory; public void setSessi ...

  8. NodeJS项目迁移兼Ubuntu下NodeJS环境部署

    前言 之前做的几个项目都托管在阿里云服务器,但是最近要到期了.想着到底要不要续期,毕竟100/月.后面看着阿里云有个活动,800/三年.果断买下.环境部署折腾了一天,其中也遇到几个坑. 目录 一.安装 ...

  9. Maven Web项目部署到Tomcat下问题

    但是也遇到了很多问题,下面记录一下Web项目部署到Tomcat下的问题 1.普通的WEB项目,就是虽然是用maven搭建的,但是没有使用profiles.xml文件来配置参数.这样的项目可以通过以下的 ...

随机推荐

  1. 蓝桥杯-四平方和-java

    /* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2016, 广州科技贸易职业学院信息工程系学生 * All rights reserved. * 文件名称: ...

  2. selenium IDE的3种下载安装方式

    第一种方式: 打开firefox浏览器-----点击右上角-----附加组件----插件----搜索框输入“selenium”-----搜索的结果中下拉到页面尾部,点击“查看全部的37项结果”---进 ...

  3. IO和socket编程

    五一假期结束了,突然想到3周前去上班的路上看到槐花开的正好.放假也没能采些做槐花糕,到下周肯定就老了.一年就开一次的东西,比如牡丹,花期也就一周.而花开之时,玫瑰和月季无法与之相比.明日黄花蝶也愁.想 ...

  4. java.util.NoSuchElementException: None.get的解决方法

    在Java中用null表示无值返回.在Java 里,null 是一个关键字,不是一个对象,所以对它调用任何方法都是非法的 笔者就常在类型转换时见到空指针错误,便是由null的特殊性导致的 而在scal ...

  5. Python 基础之 异常处理

    python 基础之异常处理 说到异常处理,就得先问一下,什么是异常处理?  先来看一下,什么是异常? 异常就是:程序运行时发出的错误的信号. 异常的种类先来看一下: 一.常见的异常 Attribut ...

  6. [原创]Jquery实现表格内容点击隐藏显示内容

    1.首先看效果,点击红色字体,可是查看全部文字内容 2.JS实现 思路:将Ajax获得的数据,一份截取,一份不变,放到td里面,分别用span装着. 然后通过display属性,进行切换

  7. dubbo 入门

    1 介绍 1.1 背景 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进. 1.2 说明 DUBB ...

  8. 关于微信小程序的Request请求错误处理

    在学微信小程序的request请求的时候,一开始报“不在以下合法域名列表中,请参考文”的错误,后来又莫名其妙的报“400 Bad Request”错误,经过半天的研究,终于搞定了,把遇到的错误给大家分 ...

  9. H5水果机,一个网络版的lao hu ji

    该游戏为h5小游戏,纯属娱乐,技术探讨,相关技术在文章结尾,欢迎探讨交流 花了几天时间开发了这款水果lao hu ji,更新了几个版本,还有不足的地方,由于时间有限暂时没有继续更新新版本 未完成的功能 ...

  10. 同步文件的利器-rsync

    即使你只是个人用户而不是一个企业,备份你自己的数据也是非常重要的,我不想失去任何这些数据. rsync是同步文件的利器,一般用于多个机器之间的文件同步与备份,同时也支持在本地的不同目录之间互相同步文件 ...