c#實現網頁正文抓取


需要記住的,隨筆記一下

 

 

1、抓取遠程網頁源碼,這里要實現自動判斷網頁編碼,否則有可能抓到亂碼。我是先看應答的 http頭的chareset,一般這個很准,但像csdn的新聞比較變態http應答的頭里的chareset和網頁的meta里聲明的 chareset不一致,所以我手工加了一下判斷,如果不一致再在內存流里用網頁聲明的編碼讀取一遍源碼 2、把網頁分割成幾大塊。試用了一下tidy的.net包裝及HtmlParse的.net版本,都不太好用。於是我自己寫了個算法,可以把網頁里的div塊,td塊等都提取出來,支持嵌套的情況。一般只提取div的文字塊兒就行了。 3、把漢字少於200的文本塊去了,一般少於200字的文本塊不會是正文,即便是正文,一般來說也不會有太多的價值,我直接去掉。 4、 因為div支持嵌套,所以剩下的文本塊,有可能是重復的,一個是另一個的父節點,所以要把最里層的文本塊找出來,最里層的文本塊肯定是漢字最多的,而其它 文本最少的,所以要計算出剩余文本塊中漢字占所有字符比例最高的文本塊,基本上它就是正文的文本塊了。當然有的網頁正文里也可能還有div的文本塊,這時 候可能會判斷錯誤,但只要正文嵌套的Div文本塊的漢字少於200字,我的算法還是能准確提取正文文本塊的。這一步我用寫了一個自定義的方法傳遞給 List的Sort方法。 5、把<p><br>等標簽替換成特殊占位符[p][br]等,因為最終的正文需要保留段落和回車換行等格式。這一步用正則實現。 6、把最后剩下的文本塊的html標簽去掉,我用正則過濾的。 7、把[p]替換成回車換行加倆空格,把[br]替換成回車換行,這步也用正則。到此,正文提取完畢

主要代碼:

 

[csharp]  view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. <span style="font-size:18px;">public class GetMainContentHelper  
  2. {  
  3.     ///<summary>  
  4.     /// 判斷兩段兒文本里哪個中文占的比例高  
  5.     ///</summary>  
  6.     ///<param name="x"></param>  
  7.     ///<param name="y"></param>  
  8.     ///<returns></returns>  
  9.     public static int CompareDinosByChineseLength(string x, string y)  
  10.     {  
  11.         if (x == null)  
  12.         {  
  13.             if (y == null)  
  14.             {  
  15.                 return 0;  
  16.             }  
  17.             else  
  18.             {  
  19.                 return -1;  
  20.             }  
  21.         }  
  22.         else  
  23.         {  
  24.             if (y == null)  
  25.             {  
  26.                 return 1;  
  27.             }  
  28.             else  
  29.             {  
  30.                 Regex r = new Regex("[\u4e00-\u9fa5]");  
  31.                 float xCount = (float)(r.Matches(x).Count) / (float)x.Length;  
  32.                 float yCount = (float)(r.Matches(y).Count) / (float)y.Length;  
  33.   
  34.                 int retval = xCount.CompareTo(yCount);  
  35.   
  36.                 if (retval != 0)  
  37.                 {  
  38.                     return retval;  
  39.                 }  
  40.                 else  
  41.                 {  
  42.                     return x.CompareTo(y);  
  43.                 }  
  44.             }  
  45.         }  
  46.     }  
  47.   
  48.     ///<summary>  
  49.     /// 獲取一個網頁源碼中的標簽列表,支持嵌套,一般或去div,td等容器  
  50.     ///</summary>  
  51.     ///<param name="input"></param>  
  52.     ///<param name="tag"></param>  
  53.     ///<returns></returns>  
  54.     public static List<string> GetTags(string input, string tag)  
  55.     {  
  56.         StringReader strReader = new StringReader(input);  
  57.         int lowerThanCharCounter = 0;  
  58.         int lowerThanCharPos = 0;  
  59.         Stack<int> tagPos = new Stack<int>();  
  60.         List<string> taglist = new List<string>();  
  61.         int i = 0;  
  62.         while (true)  
  63.         {  
  64.             try  
  65.             {  
  66.                 int intCharacter = strReader.Read();  
  67.                 if (intCharacter == -1) break;  
  68.   
  69.                 char convertedCharacter = Convert.ToChar(intCharacter);  
  70.   
  71.                 if (lowerThanCharCounter > 0)  
  72.                 {  
  73.                     if (convertedCharacter == '>')  
  74.                     {  
  75.                         lowerThanCharCounter--;  
  76.   
  77.                         string biaoqian = input.Substring(lowerThanCharPos, i - lowerThanCharPos + 1);  
  78.                         if (biaoqian.StartsWith(string.Format("<{0}", tag)))  
  79.                         {  
  80.                             tagPos.Push(lowerThanCharPos);  
  81.                         }  
  82.                         if (biaoqian.StartsWith(string.Format("</{0}", tag)))  
  83.                         {  
  84.                             if (tagPos.Count < 1)  
  85.                                 continue;  
  86.                             int tempTagPos = tagPos.Pop();  
  87.                             string strdiv = input.Substring(tempTagPos, i - tempTagPos + 1);  
  88.                             taglist.Add(strdiv);  
  89.                         }  
  90.                     }  
  91.                 }  
  92.   
  93.                 if (convertedCharacter == '<')  
  94.                 {  
  95.                     lowerThanCharCounter++;  
  96.                     lowerThanCharPos = i;  
  97.                 }  
  98.             }  
  99.             finally  
  100.             {  
  101.                 i++;  
  102.             }  
  103.         }  
  104.         return taglist;  
  105.     }  
  106.   
  107.     ///<summary>  
  108.     /// 獲取指定網頁的源碼,支持編碼自動識別  
  109.     ///</summary>  
  110.     ///<param name="url"></param>  
  111.     ///<returns></returns>  
  112.     public static string getDataFromUrl(string url)  
  113.     {  
  114.         string str = string.Empty;  
  115.         HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);  
  116.   
  117.         //設置http頭  
  118.         request.AllowAutoRedirect = true;  
  119.         request.AllowWriteStreamBuffering = true;  
  120.         request.Referer = "";  
  121.         request.Timeout = 10 * 1000;  
  122.         request.UserAgent = "";  
  123.   
  124.         HttpWebResponse response = null;  
  125.         try  
  126.         {  
  127.             response = (HttpWebResponse)request.GetResponse();  
  128.             if (response.StatusCode == HttpStatusCode.OK)  
  129.             {  
  130.                 //根據http應答的http頭來判斷編碼  
  131.                 string characterSet = response.CharacterSet;  
  132.                 Encoding encode;  
  133.                 if (characterSet != "")  
  134.                 {  
  135.                     if (characterSet == "ISO-8859-1")  
  136.                     {  
  137.                         characterSet = "gb2312";  
  138.                     }  
  139.                     encode = Encoding.GetEncoding(characterSet);  
  140.                 }  
  141.                 else  
  142.                 {  
  143.                     encode = Encoding.Default;  
  144.                 }  
  145.   
  146.                 //聲明一個內存流來保存http應答流  
  147.                 Stream receiveStream = response.GetResponseStream();  
  148.                 MemoryStream mStream = new MemoryStream();  
  149.   
  150.                 byte[] bf = new byte[255];  
  151.                 int count = receiveStream.Read(bf, 0, 255);  
  152.                 while (count > 0)  
  153.                 {  
  154.                     mStream.Write(bf, 0, count);  
  155.                     count = receiveStream.Read(bf, 0, 255);  
  156.                 }  
  157.                 receiveStream.Close();  
  158.   
  159.                 mStream.Seek(0, SeekOrigin.Begin);  
  160.   
  161.                 //從內存流里讀取字符串  
  162.                 StreamReader reader = new StreamReader(mStream, encode);  
  163.                 char[] buffer = new char[1024];  
  164.                 count = reader.Read(buffer, 0, 1024);  
  165.                 while (count > 0)  
  166.                 {  
  167.                     str += new String(buffer, 0, count);  
  168.                     count = reader.Read(buffer, 0, 1024);  
  169.                 }  
  170.   
  171.                 //從解析出的字符串里判斷charset,如果和http應答的編碼不一直  
  172.                 //那么以頁面聲明的為准,再次從內存流里重新讀取文本  
  173.                 Regex reg =  
  174.                     new Regex(@"<meta[\s\S]+?charset=(.*)""[\s\S]+?>",  
  175.                               RegexOptions.Multiline | RegexOptions.IgnoreCase);  
  176.                 MatchCollection mc = reg.Matches(str);  
  177.                 if (mc.Count > 0)  
  178.                 {  
  179.                     string tempCharSet = mc[0].Result("$1");  
  180.                     if (string.Compare(tempCharSet, characterSet, true) != 0)  
  181.                     {  
  182.                         encode = Encoding.GetEncoding(tempCharSet);  
  183.                         str = string.Empty;  
  184.                         mStream.Seek(0, SeekOrigin.Begin);  
  185.                         reader = new StreamReader(mStream, encode);  
  186.                         buffer = new char[255];  
  187.                         count = reader.Read(buffer, 0, 255);  
  188.                         while (count > 0)  
  189.                         {  
  190.                             str += new String(buffer, 0, count);  
  191.                             count = reader.Read(buffer, 0, 255);  
  192.                         }  
  193.                     }  
  194.                 }  
  195.                 reader.Close();  
  196.                 mStream.Close();  
  197.             }  
  198.         }  
  199.         catch (Exception ex)  
  200.         {  
  201.             Trace.TraceError(ex.ToString());  
  202.         }  
  203.         finally  
  204.         {  
  205.             if (response != null)  
  206.                 response.Close();  
  207.         }  
  208.         return str;  
  209.     }  
  210.   
  211.     ///<summary>  
  212.     /// 從一段網頁源碼中獲取正文  
  213.     ///</summary>  
  214.     ///<param name="input"></param>  
  215.     ///<returns></returns>  
  216.     public static string GetMainContent(string input)  
  217.     {  
  218.         string reg1 = @"<(p|br)[^<]*>";  
  219.         string reg2 =  
  220.             @"(
    ([^=]*)(=[^
    ]*)?\][\s\S]*?
    /\1
    )|(?<lj>(?=[^\u4E00-\u9FA5\uFE30-\uFFA0,."");])<a\s+[^>]*>[^<]{2,}</a>(?=[^\u4E00-\u9FA5\uFE30-\uFFA0,."");]))|(?<Style><style[\s\S]+?/style>)|(?<select><select[\s\S]+?/select>)|(?<Script><script[\s\S]*?/script>)|(?<Explein><\!\-\-[\s\S]*?\-\->)|(?<li><li(\s+[^>]+)?>[\s\S]*?/li>)|(?<Html></?\s*[^> ]+(\s*[^=>]+?=['""]?[^""']+?['""]?)*?[^\[<]*>)|(?<Other>&[a-zA-Z]+;)|(?<Other2>\#[a-z0-9]{6})|(?<Space>\s+)|(\&\#\d+\;)";  
  221.   
  222.         //1、獲取網頁的所有div標簽  
  223.         List<string> list = GetTags(input, "div");  
  224.   
  225.         //2、去除漢字少於200字的div  
  226.         List<string> needToRemove = new List<string>();  
  227.         foreach (string s in list)  
  228.         {  
  229.             Regex r = new Regex("[\u4e00-\u9fa5]");  
  230.             if (r.Matches(s).Count < 300)  
  231.             {  
  232.                 needToRemove.Add(s);  
  233.             }  
  234.         }  
  235.         foreach (string s in needToRemove)  
  236.         {  
  237.             list.Remove(s);  
  238.         }  
  239.   
  240.         //3、把剩下的div按漢字比例多少倒序排列,  
  241.         list.Sort(CompareDinosByChineseLength);  
  242.         if (list.Count < 1)  
  243.         {  
  244.             return "";  
  245.         }  
  246.         input = list[list.Count - 1];  
  247.   
  248.         //4、把p和br替換成特殊的占位符[p][br]  
  249.         input = new Regex(reg1, RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(input, "[$1]");  
  250.   
  251.         //5、去掉HTML標簽,保留漢字  
  252.         input = new Regex(reg2, RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(input, "");  
  253.   
  254.         //6、把特殊占維護替換成回車和換行  
  255.         input = new Regex("\\[p]", RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(input, "\r\n ");  
  256.         input = new Regex("\\[br]", RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(input, "\r\n");  
  257.         return input;  
  258.     }  
  259. }  


免責聲明!

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



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