这一节主要内容是使用正则表达式提取网站的正文,主要面向于小说章节网站。其中涉及到一些其他知识点,比如异步读取、异步流写入等,代码中都会有详细的注解。现在流行的网络文学都是每日一更或几更,没有一个统一的下载入口。以下我将实现一个简单的章节小说下载器的功能,将章节小说以整本的形式下载保存,保守估计能下载网络上70%以上小说。

先看看小说网站的网页源码,天蚕土豆的大主宰第一章。

http://www.biquge.com/4_4606/991334.html 笔趣网

http://www.fqxsw.com/html/11739/4636404.html 番茄小说网

 正文正则

结果发现正文内容一般都是嵌套在div中,样式表可能会略有不同,所以正则表达式可以这样表示

(<div).*</div>

当然有div标签的不一定是正文内容,还有可能是其中不相关的数据。那么按照一般小说的规律,我们指定一个匹配符。

<br\\s*>

只有当匹配符超过5个以上的,我们才认为这是正文内容。

下一页正则

再来找下一页的链接。下一页的链接的格式一般存在两种格式

或是

所以正则表达式可以这样表示

<a.*href=(")(([^<]*[^"])[^>])(\s*)?>.*((→)|(下一页))

异步读取网页流

读取网页数据使用HttpClient异步方法,在读取过程中将主控制权返回到UI层,不会阻塞界面。具体原理请查看我上一篇文章

await httpClient.GetByteArrayAsync(url);

配置文件

为了匹配更多的网站信息,我把正则表达式存在一个ini文件中,在需要的时候可以继续扩充。

核心代码

    private async Task downLoadNovel(byte[] bytes, string url)
{
title = string.Empty;
nextPageUrl = string.Empty;
content = string.Empty;
novelInfo = string.Empty; try
{
byte[] response = bytes;
if (bytes == null)
{
response = await httpClient.GetByteArrayAsync(url);
}
content = Encoding.Default.GetString(response, , response.Length - );
//获取网页字符编码描述信息
var charSetMatch = Regex.Match(content, "<meta([^<]*)charset=([^<]*)\"", RegexOptions.IgnoreCase | RegexOptions.Multiline); string webCharSet = charSetMatch.Groups[].Value;
if (chartSet == null || chartSet == "")
chartSet = webCharSet; if (chartSet != null && chartSet != "" && Encoding.GetEncoding(chartSet) != Encoding.Default)
content = Encoding.GetEncoding(chartSet).GetString(response, , response.Length - );
}
catch (Exception ex)
{
throw ex;
}
//小说主域名
if (webSiteDomain.Length == )
{
var websiteDomainMath = Regex.Match(url, "(http).*(/)", RegexOptions.IgnoreCase);
webSiteDomain = websiteDomainMath.Groups[].Value;
} //标题信息
var titleInfoMath = Regex.Match(content, "(<title>)([^>]*)(</title>)", RegexOptions.IgnoreCase | RegexOptions.Multiline);
title = titleInfoMath.Groups[].Value; content = content.Replace("'", "\"").Replace("\r\n", ""); for (int i = ; i < contextPatterns.Length; i++)
{
var cpattern = contextPatterns[i];
if (novelInfo.Length == )
{
//正文信息
var webInfoMath = Regex.Matches(content, cpattern, RegexOptions.IgnoreCase | RegexOptions.Multiline); for (int j = ; j < webInfoMath.Count; j++)
{
foreach (Group g in webInfoMath[j].Groups)
{
var value = Regex.Split(g.Value, contextNewLine, RegexOptions.IgnoreCase);
if (value.Length > )
{
novelInfo = g.Value;
foreach (var pattern in filterPatterns)
novelInfo = Regex.Replace(novelInfo, pattern, new MatchEvaluator(p => null)); novelInfo = Regex.Replace(novelInfo, contextNewLine, new MatchEvaluator(p => "\r\n"));
break;
}
}
} }
else
break;
} bytes = null; for (int i = ; i < nextPagePatterns.Length; i++)
{
if (nextPageUrl.Length == )
{
//下一页信息
var webNextPageMath = Regex.Match(content, nextPagePatterns[i], RegexOptions.IgnoreCase | RegexOptions.Multiline);
if (webNextPageMath.Groups.Count > )
{
foreach (Group g in webNextPageMath.Groups)
{
if (!g.Value.EndsWith("\""))
nextPageUrl = g.Value;
if (nextPageUrl.StartsWith("/"))
nextPageUrl = nextPageUrl.Substring();
if (!nextPageUrl.StartsWith("http", true, null) && (Regex.IsMatch(nextPageUrl, "[a-z]") || Regex.IsMatch(nextPageUrl, "[0-9]")) && !url.EndsWith(nextPageUrl))
{
nextPageUrl = webSiteDomain + nextPageUrl;
}
try
{
bytes = await httpClient.GetByteArrayAsync(nextPageUrl);
break;
}
catch
{
continue;
}
} }
}
else
break;
}
bool isAdd = false;
cacheNovel.ForEach(p =>
{
if (p == (title + novelInfo))
{
isAdd = true;
}
}); if (!isAdd)
{
if (title.Length > )
{
writeNovelLog("正在下载章节:" + title);
} writeNovelLog("章节长度:" + novelInfo.Length); cacheNovel.Add(title + novelInfo); if (nextPageUrl.Length > )
{
writeNovelLog("下一页:" + nextPageUrl); await downLoadNovel(bytes, nextPageUrl);
}
else
{
downloadFinish();
}
}
else
{
writeNovelLog("存在重复的章节,章节名称:" + title + " 地址:" + url);
downloadFinish();
}
}

异步下载网页流、解析数据

最后效果

c# 使用正则表达式 提取章节小说正文全本篇的更多相关文章

  1. 将RegEx(正则表达式提取器)与JMeter一起使用

    JMeter的,最流行的开源性能测试工具,可以工作正则表达式,用正则表达式提取.正则表达式是一种用于通过使用高级操作提取文本的必需部分的工具.正则表达式在测试Web应用程序时很流行,因为它们可用于验证 ...

  2. JMeter中的关联-正则表达式提取(2)

    JMeter获取正则表达式中的提取的所有关联值的解决方法: 需求如下: { : ", : "results": : [ : : { : : : "total_e ...

  3. JMeter中的关联-正则表达式提取(1)

    运用Jmeter正则提取器,可以从请求的响应结果中取到需要的内容,从而实现关联. jmeter之关联 的个人理解: 关联是请求与请求之间存在数据依赖关系,需要从上一个请求获取下一个请求需要回传回去的数 ...

  4. asp.net正则表达式提取网页网址、标题、图片实例以及过滤所有HTML标签实例

    无论你用什么语言,正则表达式的处理方法都是非常灵活.高效的,尤其是对某些字符串的抓取.过滤方面,更显其优势. 正则表达式的写法通常比较简单,几行短代码便能轻松完成看似很复杂的事情,更值得称赞的是,它的 ...

  5. Jmeter—5 关联 响应数据传递-正则表达式提取器

    在测试过程中,遇到一个问题:用户登录成功后服务器会返回一个登录凭证,之后所有的操作都需要带上此凭证.我们怎么获取登录凭证并传递给后续的操作? Jmeter提供了正则表达式提取器,用变量提取参数,后续通 ...

  6. JMeter学习-011-JMeter 后置处理器实例之 - 正则表达式提取器(三)多参数获取进阶引用篇

    前两篇文章分表讲述了 后置处理器 - 正则表达式提取器概述及简单实例.多参数获取,相应博文敬请参阅 简单实例.多参数获取. 此文主要讲述如何引用正则表达式提取器获取的数据信息.其实,正则表达式提取器获 ...

  7. JMeter学习-009-JMeter 后置处理器实例之 - 正则表达式提取器(二)多参数获取

    前文简述了通过后置处理器 - 正则表达式提取器 获取 HTTP请求 响应结果中的特定数据,未看过的亲,敬请参阅 JMeter学习-008-JMeter 后置处理器实例之 - 正则表达式提取器(一). ...

  8. JMeter学习-008-JMeter 后置处理器实例之 - 正则表达式提取器(一)概述及简单实例

    上文我们讲述了如何对 HTTP请求 的响应数据进行断言,以判断响应是否符合我们的预期,敬请参阅:JMeter学习-007-JMeter 断言实例之一 - 响应断言 那么我们如何获取 HTTP请求 响应 ...

  9. Jmeter正则表达式提取器的使用方法(转)

    下面简单介绍一下Jmeter正则表达式提取器的使用方法. 1.添加Jmeter正则表达式提取器:在具体的Request下添加Jmeter正则表达式提取器(Jmeter正则表达式在“后置处理器”下面)  ...

随机推荐

  1. Cocos2d-x 3.0修改Android平台帧率fps - 解决游戏运行手机发热发烫问题

    使用Cocos2d-x 3.0开发游戏之后,发现游戏在android手机上发热非常严重,在魅族2上,几乎担心手机会爆炸了~~~采取的一个措施就是降低帧率,因为游戏对于帧率要求不是非常高. 做过coco ...

  2. 第14章 使用DHCP动态管理主机地址

    章节简述: DHCP协议服务能够自动化的管理局域网内的主机IP地址,有效的提升IP地址使用率,提高配置效率,减少管理与维护成本. 学习dhcpd服务程序的使用方法并逐条讲解配置参数,完整演示自动化分配 ...

  3. Numeric Validation

    Numeric Inputs Numbers are even easier to validate than text. For number input types, the HTML5 spec ...

  4. linux awk 内置函数详细介绍(实例)

    这节详细介绍awk内置函数,主要分以下3种类似:算数函数.字符串函数.其它一般函数.时间函数 一.算术函数: 以下算术函数执行与 C 语言中名称相同的子例程相同的操作: 函数名 说明 atan2( y ...

  5. 【Django】如何自定义manage.py命令? 达到启动后台进程的目的?

    代码: #-*- coding:utf- -*- """ The handle active user mail send """ from ...

  6. 【云计算】docker build如何支持参数化构建?

    docker 1.9.0版本之后,已经支持docker build参数化构建. docker 版本更新记录: github讨论: 参开资料: https://github.com/docker/doc ...

  7. CentOS6.5以runlevel 3开机时自动连接某无线设置示例

    [参考]http://blog.csdn.net/simeone18/article/details/8580592 [方法一] 假设无线网卡代号为wlan0,无线AP的essid为:TheWiFi, ...

  8. VIM中的折叠命令

    参考:http://blog.csdn.net/bruce0532/article/details/8497284 za:在折叠与展开间自由转换 zf:创建折叠 示例:zf 3j    #本行及以下3 ...

  9. sphinx 增量索引与主索引使用测试

    2013年10月28日 15:01:16 首先对新增的商品建立增量索引,搜索时只使用增量索引: array (size=1) 0 => array (size=6) 'gid' => st ...

  10. Java for LeetCode 200 Number of Islands

    Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surro ...