c#使⽤正则表达式提取章节⼩说正⽂全本篇这⼀节主要内容是使⽤正则表达式提取⽹站的正⽂,主要⾯向于⼩说章节⽹站。其中涉及到⼀些其他知识点,⽐如异步读取、异步流写⼊等,
代码中都会有详细的注解。现在流⾏的⽹络⽂学都是每⽇⼀更或⼏更,没有⼀个统⼀的下载⼊⼝。以下我将实现⼀个简单的章节⼩说下载器的功能,将章节⼩说以整本的形式下载保存,保守估计能下载⽹络上70%以上⼩说。
先看看⼩说⽹站的⽹页源码,天蚕⼟⾖的⼤主宰第⼀章。
正⽂正则
结果发现正⽂内容⼀般都是嵌套在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, 0, response.Length - 1);
//获取⽹页字符编码描述信息
var charSetMatch = Regex.Match(content, "<meta([^<]*)charset=([^<]*)\"", RegexOptions.IgnoreCase | RegexOptions.Multiline);
string webCharSet = charSetMatch.Groups[2].Value;
if (chartSet == null || chartSet == "")
chartSet = webCharSet;
if (chartSet != null && chartSet != "" && Encoding.GetEncoding(chartSet) != Encoding.Default)
content = Encoding.GetEncoding(chartSet).GetString(response, 0, response.Length - 1);
}
catch (Exception ex)
{
throw ex;
}
//⼩说主域名
if (webSiteDomain.Length == 0)
{
var websiteDomainMath = Regex.Match(url, "(http).*(/)", RegexOptions.IgnoreCase);
webSiteDomain = websiteDomainMath.Groups[0].Value;
}
//标题信息
var titleInfoMath = Regex.Match(content, "(<title>)([^>]*)(</title>)", RegexOptions.IgnoreCase | RegexOptions.Multiline);
title = titleInfoMath.Groups[2].Value;
content = content.Replace("'", "\"").Replace("\r\n", "");
for (int i = 0; i < contextPatterns.Length; i++)
{
var cpattern = contextPatterns[i];
if (novelInfo.Length == 0)
{
//正⽂信息
var webInfoMath = Regex.Matches(content, cpattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);
for (int j = 0; j < webInfoMath.Count; j++)
{
foreach (Group g in webInfoMath[j].Groups)
{
var value = Regex.Split(g.Value, contextNewLine, RegexOptions.IgnoreCase);
if (value.Length > 5)
{
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 = 0; i < nextPagePatterns.Length; i++)
{
if (nextPageUrl.Length == 0)
{
/
/下⼀页信息
var webNextPageMath = Regex.Match(content, nextPagePatterns[i], RegexOptions.IgnoreCase | RegexOptions.Multiline);
if (webNextPageMath.Groups.Count > 0)
{
foreach (Group g in webNextPageMath.Groups)
{
if (!g.Value.EndsWith("\""))
nextPageUrl = g.Value;
if (nextPageUrl.StartsWith("/"))
nextPageUrl = nextPageUrl.Substring(1);
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 > 0)
{
writeNovelLog("正在下载章节:" + title);
}
writeNovelLog("章节长度:" + novelInfo.Length);
cacheNovel.Add(title + novelInfo);
if (nextPageUrl.Length > 0)
{
writeNovelLog("下⼀页:" + nextPageUrl);
await downLoadNovel(bytes, nextPageUrl);
}
else
{
downloadFinish();
}
}
else
{
writeNovelLog("存在重复的章节,章节名称:" + title + " 地址:" + url); downloadFinish();
}
}
异步下载⽹页流、解析数据
最后效果
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论