c# 使用正則表達式 提取章節小說正文全本篇


       這一節主要內容是使用正則表達式提取網站的正文,主要面向於小說章節網站。其中涉及到一些其他知識點,比如異步讀取、異步流寫入等,代碼中都會有詳細的注解。現在流行的網絡文學都是每日一更或幾更,沒有一個統一的下載入口。以下我將實現一個簡單的章節小說下載器的功能,將章節小說以整本的形式下載保存,保守估計能下載網絡上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, 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();
            }
        }
異步下載網頁流、解析數據

 

最后效果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM