程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 抓取網站數據不再是難事了,Fizzler(So Easy)全能搞定,fizzlereasy

抓取網站數據不再是難事了,Fizzler(So Easy)全能搞定,fizzlereasy

編輯:C#入門知識

抓取網站數據不再是難事了,Fizzler(So Easy)全能搞定,fizzlereasy


       首先從標題說起,為啥說抓取網站數據不再難(其實抓取網站數據有一定難度),SO EASY!!!使用Fizzler全搞定,我相信大多數人或公司應該都有抓取別人網站數據的經歷,比如說我們博客園每次發表完文章都會被其他網站給抓取去了,不信你們看看就知道了。還有人抓取別人網站上的郵箱、電話號碼、QQ等等有用信息,這些信息抓取下來肯定可以賣錢或者干其他事情,我們每天都會時不時接到垃圾短信或郵件,可能就這麼回事了,有同感吧,O(∩_∩)O哈哈~。

       本人前段時間了寫了兩個程序,一個程序是抓取某彩票網站的數據(雙色球),一個是抓取求職網站(獵聘、前程無憂、智聯招聘等等)的數據,當時在寫這兩個程序的時候顯示尤為棘手,看到一堆的HTML標簽真的是想死。首先來回顧一下之前我是如何解析HTML的,非常常規的做法,通過WebRequest拿到HTML內容,再通過HTML標簽一步一步截取你想要的內容,以下代碼就是截取雙色球的紅球和籃球的代碼。一旦網站的標簽發生一點變化可能面臨的就是要重新改程序了,使用起來非常不方便。

      下面是我在解析雙色球的紅球和籃球的代碼,做得最多的是截取(正則表達式)標簽相應的內容,也許這段代碼顯得還不是很復雜,因為這個截取的數據有限,而且非常有規律所以顯得比較簡單。

 1         #region * 在一個TR中,解析TD,獲取一期的號碼
 2         /// <summary>
 3         /// 在一個TR中,解析TD,獲取一期的號碼
 4         /// </summary>
 5         /// <param name="wn"></param>
 6         /// <param name="trContent"></param>
 7         private void ResolveTd(ref WinNo wn, string trContent)
 8         {
 9             List<int> redBoxList = null;
10             //匹配期號的表達式
11             string patternQiHao = "<td align=\"center\" title=\"開獎日期";
12             Regex regex = new Regex(patternQiHao);
13             Match qhMatch = regex.Match(trContent);
14             wn.QiHao = trContent.Substring(qhMatch.Index + 17 + patternQiHao.Length, 7);
15             //匹配藍球的表達式
16             string patternChartBall02 = "<td class=\"chartBall02\">";
17             regex = new Regex(patternChartBall02);
18             Match bMatch = regex.Match(trContent);
19             wn.B = Convert.ToInt32(trContent.Substring(bMatch.Index + patternChartBall02.Length, 2));
20             //存放匹配出來的紅球號碼
21             redBoxList = new List<int>();
22             //匹配紅球的表達式
23             string patternChartBall01 = "<td class=\"chartBall01\">";
24             regex = new Regex(patternChartBall01);
25             MatchCollection rMatches = regex.Matches(trContent);
26             foreach (Match r in rMatches)
27             {
28                 redBoxList.Add(Convert.ToInt32(trContent.Substring(r.Index + patternChartBall01.Length, 2)));
29             }
30             //匹配紅球的表達式
31             string patternChartBall07 = "<td class=\"chartBall07\">";
32             regex = new Regex(patternChartBall07);
33             rMatches = regex.Matches(trContent);
34             foreach (Match r in rMatches)
35             {
36                 redBoxList.Add(Convert.ToInt32(trContent.Substring(r.Index + patternChartBall07.Length, 2)));
37             }
38             //排序紅球號碼
39             redBoxList.Sort();
40             //第一個紅球號碼
41             wn.R1 = redBoxList[0];
42             //第二個紅球號碼
43             wn.R2 = redBoxList[1];
44             wn.R3 = redBoxList[2];
45             wn.R4 = redBoxList[3];
46             wn.R5 = redBoxList[4];
47             wn.R6 = redBoxList[5];
48         }

      下面這塊的代碼是某招聘網站的截取數據,就是一串的截取HTML標簽的內容,哈哈,當時在寫這個時候相當的頭痛,不知有做個這方法工作的人是不是有同感,當你解析比較多網站的數據就更加了(我寫了抓取前程無憂、獵聘網、前程無憂和拉勾網的數據),O(∩_∩)O哈哈~想死呀,使用正則表達是去截取數據,再去提取相應信息的工作。

// 正則表達式過濾:正則表達式,要替換成的文本
        private static readonly string[][] Filters =
        {
            new[] { @"(?is)<script.*?>.*?</script>", "" },
            new[] { @"(?is)<style.*?>.*?</style>", "" },
            new[] { @"(?is)<!--.*?-->", "" },    // 過濾Html代碼中的注釋
            new[] { @"(?is)<footer.*?>.*?</footer>",""},
            //new[] { "(?is)<div class=\"job-require bottom-job-require\">.*?</div></div>",""}
            new[] { @"(?is)<h3>常用鏈接:.*?</ul>",""}
        };

        private void GetJobInfoFromUrl(string url)
        {
            try
            {
                JobInfo info = new JobInfo();
                //--
                string pageStr = GetHtmlCode.GetByget(url, "utf-8");
                if (string.IsNullOrEmpty(pageStr))
                {
                    return;
                }
                //--
                pageStr = pageStr.Replace("\r\n", "");//替換換行符
                // 獲取html,body標簽內容
                string body = string.Empty;
                string bodyFilter = @"(?is)<body.*?</body>";
                Match m = Regex.Match(pageStr, bodyFilter);
                if (m.Success)
                {
                    body = m.ToString().Replace("<tr >", "<tr>").Replace("\r\n", "");
                }
                // 過濾樣式,腳本等不相干標簽
                foreach (var filter in Filters)
                {
                    body = Regex.Replace(body, filter[0], filter[1]);
                }
                //--
                if (!string.IsNullOrEmpty(mustKey) && !body.Contains(mustKey))
                {
                    return;
                }
                body = Regex.Replace(body, "\\s", "");

                info.Url = url;

                string basicInfoRegexStr0 = "<h1title=([\\s\\S]+?)>(.*?)</h1>"; //職位名稱
                string position = Regex.Match(body, basicInfoRegexStr0).Value;
                info.Position = string.IsNullOrEmpty(position) ? "" : position.Substring(position.IndexOf(">") + 1, position.IndexOf("</") - position.IndexOf(">") - 1);//職位名稱

                string basicInfoRegexStr1 = "</h1><h3>(.*?)</h3>";//公司名稱
                string company = Regex.Match(body, basicInfoRegexStr1).Value;
                info.Company = string.IsNullOrEmpty(company) ? "" : company.Substring(company.IndexOf("<h3>") + 4, company.IndexOf("</h3>") - company.IndexOf("<h3>") - 4);//公司名稱

                string basicInfoRegexStr2 = "<divclass=\"resumeclearfix\"><span>(.*?)</span>";//工作地點
                string address = Regex.Match(body, basicInfoRegexStr2).Value;
                info.Address = string.IsNullOrEmpty(address) ? "" : address.Substring(address.IndexOf("<span>") + 6, address.IndexOf("</") - address.IndexOf("<span>") - 6);//工作地點

                string basicInfoRegexStr3 = "<li><span>企業性質:</span>(.*?)</li>";//公司性質
                string nature = Regex.Match(body, basicInfoRegexStr3).Value;
                info.Nature = string.IsNullOrEmpty(nature) ? "" : nature.Substring(nature.IndexOf("</span>") + 7, nature.IndexOf("</li>") - nature.IndexOf("</span>") - 7);//公司性質

                if (string.IsNullOrEmpty(info.Nature))
                {
                    string basicInfoRegexStr3_1 = "<br><span>性質:</span>(.*?)<br>";
                    string nature_1 = Regex.Match(body, basicInfoRegexStr3_1).Value;
                    info.Nature = string.IsNullOrEmpty(nature_1) ? "" : nature_1.Substring(nature_1.IndexOf("</span>") + 7, nature_1.LastIndexOf("<br>") - nature_1.IndexOf("</span>") - 7);//公司性質
                }

                string basicInfoRegexStr4 = "<li><span>企業規模:</span>(.*?)</li>";//公司規模
                string scale = Regex.Match(body, basicInfoRegexStr4).Value;
                info.Scale = string.IsNullOrEmpty(scale) ? "" : scale.Substring(scale.IndexOf("</span>") + 7, scale.IndexOf("</li>") - scale.IndexOf("</span>") - 7);//公司規模

                if (string.IsNullOrEmpty(info.Scale))
                {
                    string basicInfoRegexStr4_1 = "<br><span>規模:</span>(.*?)<br>";
                    string scale_1 = Regex.Match(body, basicInfoRegexStr4_1).Value;
                    info.Scale = info.Nature = string.IsNullOrEmpty(scale_1) ? "" : scale_1.Substring(scale_1.IndexOf("</span>") + 7, scale_1.LastIndexOf("<br>") - scale_1.IndexOf("</span>") - 7);//公司規模
                }

                string basicInfoRegexStr5 = "<spanclass=\"noborder\">(.*?)</span>";//工作經驗
                string experience = Regex.Match(body, basicInfoRegexStr5).Value;
                info.Experience = string.IsNullOrEmpty(experience) ? "" : experience.Substring(experience.IndexOf(">") + 1, experience.IndexOf("</") - experience.IndexOf(">") - 1);//工作經驗

                string basicInfoRegexStr6 = "</span><span>(.*?)</span><spanclass=\"noborder\">";//最低學歷
                string education = Regex.Match(body, basicInfoRegexStr6).Value;
                info.Education = string.IsNullOrEmpty(education) ? "" : education.Substring(education.IndexOf("<span>") + 6, education.IndexOf("</span><spanclass=") - education.IndexOf("<span>") - 6);//最低學歷

                string basicInfoRegexStr7 = "<pclass=\"job-main-title\">(.*?)<";//月薪
                string salary = Regex.Match(body, basicInfoRegexStr7).Value;
                info.Salary = string.IsNullOrEmpty(salary) ? "" : salary.Substring(salary.IndexOf(">") + 1, salary.LastIndexOf("<") - salary.IndexOf(">") - 1);//月薪

                string timeInfoRegexStr = "<pclass=\"release-time\">發布時間:<em>(.*?)</em></p>";//發布時間
                string time = Regex.Match(body, timeInfoRegexStr).Value;
                info.Time = string.IsNullOrEmpty(time) ? "" : time.Substring(time.IndexOf("<em>") + 4, time.IndexOf("</em>") - time.IndexOf("<em>") - 4);//發布時間

                if (GetJobEnd != null)
                {
                    GetJobEnd(pageStr, info);
                }
            }
            catch (Exception exMsg)
            {
                throw new Exception(exMsg.Message);
            }
        }
    }

       從以上代碼可以看出都是在截取(正則表達式)相應內容,非常復雜,稍微一不注意就截取不到網站數據,寫起來相當的費勁,最後通過QQ群(186841119)裡的朋友的介紹采用了Fizzler來提取網站數據,一下子感覺就容易多了,下面著中來介紹一下Fizzler這個工具(好像這個是開源的),相關介紹可以去網站查詢到。

首先提供這個工具的下載地址:Fizzler

這個裡面包括三個文件:Fizzler.dll、Fizzler.Systems.HtmlAgilityPack.dll、HtmlAgilityPack.dll三個文件,在VS2010裡引用裡直接進行引用就可以了。

完成以上即完成了對Fizzler的引用。

using HtmlAgilityPack;
using Fizzler;
using Fizzler.Systems.HtmlAgilityPack;

以上就可以在CS裡進行了引用,

下面來進行代碼的實現,

        private static WebDownloader m_wd = new WebDownloader();
        /// <summary>
        /// 獲取HTML內容
        /// </summary>
        /// <param name="Url">鏈接</param>
        /// <param name="Code">字符集</param>
        /// <returns></returns>
        public static string GetHtml(string Url, Encoding Code)
        {
            return m_wd.GetPageByHttpWebRequest(Url, Code);
        }

        public string GetPageByHttpWebRequest(string url, Encoding encoding)
        {
            Stream sr = null;
            StreamReader sReader = null;
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.Method = "Get";
                request.Timeout = 30000;

                HttpWebResponse response = request.GetResponse() as HttpWebResponse;
                if (response.ContentEncoding.ToLower() == "gzip")//如果使用了GZip則先解壓
                {
                    sr = new GZipStream(response.GetResponseStream(), CompressionMode.Decompress);
                }
                else
                {
                    sr = response.GetResponseStream();
                }
                sReader = new StreamReader(sr, encoding);
                return sReader.ReadToEnd();
            }
            catch
            {
                return null;
            }
            finally
            {
                if (sReader != null)
                    sReader.Close();
                if (sr != null)
                    sr.Close();
            }
        }

以上即實現抓取HTML數據代碼,,以上代碼基本上也沒啥區別,就是普通抓取數據的方法。

        /// <summary>
        /// 獲取相應的標簽內容
        /// </summary>
        /// <param name="Url">鏈接</param>
        /// <param name="CSSLoad">CSS路徑</param>
        /// <param name="Code">字符集</param>
        /// <returns></returns>
        public static IEnumerable<HtmlNode> GetUrlInfo(string Url, string CSSLoad, Encoding Code)
        {
            HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument
            {
                OptionAddDebuggingAttributes = false,
                OptionAutoCloseOnEnd = true,
                OptionFixNestedTags = true,
                OptionReadEncoding = true
            };

            htmlDoc.LoadHtml(GetHtml(Url, Code));
            IEnumerable<HtmlNode> NodesMainContent = htmlDoc.DocumentNode.QuerySelectorAll(CSSLoad);//查詢的路徑
            return NodesMainContent;
        }

        /// <summary>
        /// 獲取相應的標簽內容
        /// </summary>
        /// <param name="html">html內容</param>
        /// <param name="CSSLoad">CSS路徑</param>
        /// <returns></returns>
        public static IEnumerable<HtmlNode> GetHtmlInfo(string html, string CSSLoad)
        {
            HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument
            {
                OptionAddDebuggingAttributes = false,
                OptionAutoCloseOnEnd = true,
                OptionFixNestedTags = true,
                OptionReadEncoding = true
            };

            htmlDoc.LoadHtml(html);
            IEnumerable<HtmlNode> NodesMainContent = htmlDoc.DocumentNode.QuerySelectorAll(CSSLoad);//查詢的路徑
            return NodesMainContent;
        }

        以上兩個方法即實現對相應路徑標簽數據的抓取,一個方法是根據URL進行抓取,一個是根據HTML內容去抓取相應的數據,下面著重介紹CSSLoad的獲取方法,這個需要安裝火狐浏覽器即可,火狐浏覽器需要安裝FireBug插件進行查詢,如下圖(網站工具欄):

再點擊像蜘蛛一樣的圖標,這樣可以看到如下:

這樣可以看到所有的HTML標簽,那麼緊接著如何去獲取CSS路徑呢,那相對來說就非常簡單了。

點擊藍色的箭頭選取網站相關的內容,

這樣相應的HTML同樣選中了,這樣離我們拿到CCS路徑更近一步了,緊接著點擊右鍵即可復制CCS路徑即可。如下:

點擊復制CSS路徑就可以了,復制出CSS路徑如下:

html body#Posts form#frmMain table#BodyTable tbody tr td#Body div#Main div#Editor_Edit div#Editor_Edit_Contents div#edit_container div#Editor_Edit_APOptions div#Editor_Edit_APOptions_Contents.Edit div.edit_option div#Editor_Edit_APOptions_Advancedpanel1 div#Editor_Edit_APOptions_Advancedpanel1_Header.subCollapsibleTitle

看到這一串路徑別急,我們也不需要把這串路徑全部復制到我們程序裡,否則這樣顯得太負責了,我們只需要將最後的部分節點放到上面的方法裡面,我們就能讀取到HTML標簽相應的內容,下面舉一個簡單例子就進行說明。

  1 /// <summary>
  2         /// 解析每一條招聘信息
  3         /// </summary>
  4         /// <param name="Url"></param>
  5         private void GetJobInfoFromUrl(object Url)
  6         {
  7             try
  8             {
  9                 JobInfo info = new JobInfo();
 10                 info.Url = Url.ToString();
 11                 //--獲取HTML內容
 12                 string html =AnalyzeHTML.GetHtml(Url.ToString(), Encoding.UTF8);
 13                 if (string.IsNullOrEmpty(html)) { return; }
 14                 //--職位名稱
 15                 IEnumerable<HtmlNode> NodesMainContent1 = AnalyzeHTML.GetHtmlInfo(html, "div.title-info h1");
 16                 if(NodesMainContent1.Count()>0)
 17                 {
 18                     info.Position = NodesMainContent1.ToArray()[0].InnerText;
 19                 }
 20                 //--公司名稱
 21                 IEnumerable<HtmlNode> NodesMainContent2 = AnalyzeHTML.GetHtmlInfo(html, "div.title-info h3");
 22                 if (NodesMainContent2.Count() > 0)
 23                 {
 24                     info.Company = NodesMainContent2.ToArray()[0].InnerText;
 25                 }
 26                 //--公司性質/公司規模
 27                 IEnumerable<HtmlNode> NodesMainContent4 = AnalyzeHTML.GetHtmlInfo(html, "div.content.content-word ul li");
 28                 if (NodesMainContent4.Count() > 0)
 29                 {
 30                     foreach (var item in NodesMainContent4)
 31                     {
 32                         if (item.InnerHtml.Contains("企業性質"))
 33                         {
 34                             string nature = item.InnerText;
 35                             nature = nature.Replace("企業性質:", "");
 36                             info.Nature = nature;
 37                         }
 38                         if (item.InnerHtml.Contains("企業規模"))
 39                         {
 40                             string scale = item.InnerText;
 41                             scale = scale.Replace("企業規模:", "");
 42                             info.Scale = scale;
 43                         }
 44                     }
 45                 }
 46                 else//第二次解析企業性質和企業規模
 47                 {
 48                     IEnumerable<HtmlNode> NodesMainContent4_1 = AnalyzeHTML.GetHtmlInfo(html, "div.right-post-top div.content.content-word");
 49                     if (NodesMainContent4_1.Count() > 0)
 50                     {
 51                         foreach (var item_1 in NodesMainContent4_1)
 52                         {
 53                             string[] arr = item_1.InnerText.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
 54                             if (arr != null && arr.Length > 0)
 55                             {
 56                                 foreach (string str in arr)
 57                                 {
 58                                     if (str.Trim().Contains("性質"))
 59                                     {
 60                                         info.Nature = str.Replace("性質:", "").Trim();
 61                                     }
 62                                     if (str.Trim().Contains("規模"))
 63                                     {
 64                                         info.Scale = str.Replace("規模:", "").Trim();
 65                                     }
 66                                 }
 67                             }
 68                         }
 69                     }
 70                 }
 71                 //--工作經驗
 72                 IEnumerable<HtmlNode> NodesMainContent5 = AnalyzeHTML.GetHtmlInfo(html, "div.resume.clearfix span.noborder");
 73                 if (NodesMainContent5.Count() > 0)
 74                 {
 75                     info.Experience = NodesMainContent5.ToArray()[0].InnerText;
 76                 }
 77                 //--公司地址/最低學歷
 78                 IEnumerable<HtmlNode> NodesMainContent6 = AnalyzeHTML.GetHtmlInfo(html, "div.resume.clearfix");
 79                 if (NodesMainContent6.Count() > 0)
 80                 {
 81                     foreach (var item in NodesMainContent6)
 82                     {
 83                         string lable = Regex.Replace(item.InnerHtml, "\\s", "");
 84                         lable = lable.Replace("<span>", "");
 85                         string[] arr = lable.Split("</span>".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
 86                         if (arr != null && arr.Length > 2)
 87                         {
 88                             info.Address = arr[0];//公司地址
 89                             info.Education = arr[1];//最低學歷
 90                         }
 91                     }
 92                 }
 93                 //--月薪
 94                 IEnumerable<HtmlNode> NodesMainContent7 = AnalyzeHTML.GetHtmlInfo(html, "div.job-title-left p.job-main-title");
 95                 if (NodesMainContent7.Count() > 0)
 96                 {
 97                     info.Salary = NodesMainContent7.ToArray()[0].InnerText;
 98                 }
 99                 //--發布時間
100                 IEnumerable<HtmlNode> NodesMainContent8 = AnalyzeHTML.GetHtmlInfo(html, "div.job-title-left p.release-time em");
101                 if (NodesMainContent8.Count() > 0)
102                 {
103                     info.Time = NodesMainContent8.ToArray()[0].InnerText;
104                 }
105                 //--
106                 if (GetJobEnd != null)
107                 {
108                     GetJobEnd("", info);
109                 }
110             }
111             catch (Exception exMsg)
112             {
113                 throw new Exception(exMsg.Message);
114             }
115         }

       以上這個方法也是解析某招聘網站標簽的內容,但已經看不到復雜的正則表達式去截取HTML標簽了,這樣顯得代碼更加干練、簡單,再整一個配置頁面既可應付抓取網站標簽經常變化的難題,這樣就顯得抓取別人網站數據就是一件非常簡單的事情了,O(∩_∩)O哈哈~是不是啦!!!

       以上只代表個人觀點!!!

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved