alancxx

          ------簡單就是美

          使用JRegex抽取網(wǎng)頁信息

          當(dāng)網(wǎng)絡(luò)爬蟲將網(wǎng)頁下載到磁盤上以后,需要對這些網(wǎng)頁中的內(nèi)容進(jìn)行抽取,為索引做準(zhǔn)備。一個網(wǎng)頁中的數(shù)據(jù)大部分是HTML標(biāo)簽,索引肯定不會去索引這些標(biāo)簽。也就是說,這種信息是沒有用處的信息,需要在抽取過程中過濾掉。另外,一個網(wǎng)頁中一般會存在廣告信息、錨文本信息,還有一些我們不感興趣的信息,都被視為垃圾信息,如果不加考慮這些內(nèi)容,抽取出來的信息不僅占用存儲空間,而且在索引以后,為終端用戶提供檢索服務(wù),用戶檢會索到很多無用的垃圾信息,勢必影響用戶的體驗(yàn)。

          這里,針對論壇,采用配置模板的方式來實(shí)現(xiàn)信息的抽取。使用的工具可以到http://jregex.sourceforge.net上下載,JRegex是一個基于Java的正則庫,可以通過在正則模板中指定待抽取信息的變量,在抽取過程中會將抽取到的信息賦給該變量,從而得到感興趣的信息。而且,JRegex庫支持多級分組匹配。

          為了直觀,假設(shè),有一個論壇的一個網(wǎng)頁的源代碼形如:

          <a id="anchor">標(biāo)題</a>
          <cont>
          <author>a1</author>
          <time>2009</time>
          <post>p1</post>

          <author>a2</author>
          <time>2008</time>
          <post>p2</post>

          <author>a3</author>
          <time>2007</time>
          <post>p3</post>

          <author>a4</author>
          <time>2006</time>
          <post>p4</post>

          <author>2005</author>
          <time>t5</time>
          <post>p5</post>
          </cont>

          將該網(wǎng)頁代碼文件保存為bbsPage.txt文件,準(zhǔn)備進(jìn)行處理。

          現(xiàn)在,我們的目標(biāo)是抽取標(biāo)題、作者、時間、內(nèi)容這些內(nèi)容,當(dāng)然,標(biāo)題完全可以從TITLE標(biāo)簽中獲得,但是一般網(wǎng)站的一個網(wǎng)頁,會在標(biāo)題文本的后面加上一些目錄或者網(wǎng)站名稱的信息,例如一個標(biāo)題為“品味北京奧運(yùn)中心_奧運(yùn)加油站_我行我攝_XXX社區(qū)_XXX社區(qū)是最活躍的社區(qū)之一”,一些垃圾信息占了標(biāo)題的大部分,所以我們不從TITLE標(biāo)簽中抽取標(biāo)題。

          接著,針對上面的網(wǎng)頁文件創(chuàng)建信息抽取的正則模板,如下所示:

          (?s)<a\sid="anchor">({title}.{1,100}?)</a>\s*<cont>(.{1,10240}?)</cont> <author>({name}.{1,100}?)</author>\s*<time>({when}.{1,100}?)</time>\s*<post>({content}.{1,100}?)</post>

          該模板包含兩部分:

          第一部分為(?s)<a\sid="anchor">({title}.{1,100}?)</a>\s*<cont>(.{1,10240}?)</cont>,包含兩個組,第一個組名稱為title,直接能夠抽取到網(wǎng)頁的標(biāo)題文本,并存儲到變量title中;而第二個組沒有指定組的名稱,表示在后面還存在子組,在子組中繼續(xù)進(jìn)行抽取。

          第二部分為<author>({name}.{1,100}?)</author>\s*<time>({when}.{1,100}?)</time>\s*<post>({content}.{1,100}?)</post>,恰好是父組中未指定組名稱的第二個組內(nèi)中的一個循環(huán)。

          上面兩個模板之間使用一個空格字符隔開,保存到pattern.txt文件中。

          可能,你已經(jīng)觀察到了,網(wǎng)頁的標(biāo)題只有一個,而對其他的信息正好能夠構(gòu)成一個循環(huán)組,單獨(dú)從父組中分離出來繼續(xù)進(jìn)行抽取,結(jié)構(gòu)很整齊。所以,在使用JRegex庫進(jìn)行編碼抽取的時候,主要就是針對兩個組進(jìn)行的。

          我基于上面思想和數(shù)據(jù),實(shí)現(xiàn)了信息的抽取。

          首先定義了一個鍵值對實(shí)體類,使用泛型,如下所示:

          package org.shirdrn.test;

          public class Pair<K, V> {

          private K key;
          private V value;

          public Pair(K key, V value) {
             this.key = key;
             this.value = value;
          }

          public K getKey() {
             return key;
          }
          public void setKey(K key) {
             this.key = key;
          }
          public V getValue() {
             return value;
          }
          public void setValue(V value) {
             this.value = value;
          }

          }

          進(jìn)行信息抽取的核心類為InfomationExtraction,如下所示:

          package org.shirdrn.test;

          import java.io.BufferedReader;
          import java.io.IOException;
          import java.io.InputStream;
          import java.io.InputStreamReader;
          import java.util.ArrayList;
          import java.util.List;

          import jregex.Matcher;
          import jregex.Pattern;

          public class InfomationExtraction {

          private String htmlString;
          private String patternString;
          private List<Pair<String, String>> dataList = new ArrayList<Pair<String, String>>();

          public InfomationExtraction() {
            
          }

          public InfomationExtraction(String htmlFileName, String patternFileName) {
             this.htmlString = this.readString(htmlFileName);
             this.patternString = this.readString(patternFileName);
          }

          public Pattern[] getPatternArray() {
             Pattern[] pa = new Pattern[2];
             String[] psa = this.patternString.split(" ");
             for(int i=0; i<psa.length; i++) {
              Pattern p = new Pattern(psa[i]);
              pa[i] = p;
             }
             return pa;
          }

          public void extract(Integer sgIndex) {   // 指定父組中第sgIndex個組需要在子組中繼續(xù)進(jìn)行抽取
             Pattern[] pa = this.getPatternArray();
             Pattern pBase = pa[0]; 
             Matcher mBase = pBase.matcher(this.htmlString);
             if(mBase.find()) {
              for(int i=0; i<mBase.groupCount(); i++) {
               String gn = pBase.groupName(i);
               if(gn != null) {
                String gv = mBase.group(i);
                this.dataList.add(new Pair<String, String>(gn, gv));
               }
              }
              String subText = mBase.group(sgIndex);
              if(subText != null) {
               this.dataList.addAll(this.getSubGroupDataList(pa, subText)); // 調(diào)用使用子組正則模板進(jìn)行抽取的方法
              }
             }
          }

          public List<Pair<String, String>> getSubGroupDataList(Pattern[] pa, String subText) { // 使用子組正則模板進(jìn)行抽取
             List<Pair<String, String>> list = new ArrayList<Pair<String, String>>();
             for(int i=1; i<pa.length; i++) {
              Pattern subp =pa[i];
              Matcher subm = subp.matcher(subText);
              while(subm.find()) {
               for(int k=0; k<subm.groupCount(); k++) {
                String gn = subp.groupName(k);
                if(gn != null) {
                 String gv = subm.group(k);
                 list.add(new Pair<String, String>(gn, gv));
                }
               }
              }
             }
             return list;
          }

          public String readString(String fileName) {
             InputStream in = this.getClass().getResourceAsStream("/" + fileName);
             BufferedReader reader = new BufferedReader(new InputStreamReader(in));
             StringBuffer sb = new StringBuffer();
             String line = null;
             try {
              while((line=reader.readLine()) != null) {
               sb.append(line);
              }
             } catch (IOException e) {
              e.printStackTrace();
             }
             return sb.toString();
          }

          public List<Pair<String, String>> getDataList() {
             return this.dataList;
          }

          public static void main(String[] args) {  
             InfomationExtraction ie = new InfomationExtraction("bbsPage.txt","pattern.txt");
             ie.extract(2);
             for(Pair<String, String> p : ie.getDataList()) {
              System.out.println("[" + p.getKey() + " " + p.getValue() + "]");
             }
          }

          }

          測試一下,如下所示:

          [title 標(biāo)題]
          [name a1]
          [when 2009]
          [content p1]
          [name a2]
          [when 2008]
          [content p2]
          [name a3]
          [when 2007]
          [content p3]
          [name a4]
          [when 2006]
          [content p4]
          [name 2005]
          [when t5]
          [content p5]

          至于如何組織抽取到的信息,比如你可能使用Lucene的索引,需要構(gòu)造Field和Document,那么你就要設(shè)計(jì)一個實(shí)體能夠包含一個Document的所有的Field,比如一個Document包括:URL、標(biāo)題、作者、發(fā)表時間、發(fā)表內(nèi)容這五個項(xiàng),非常容易就能做到。

          使用JRegex庫,可以非常靈活地配置模板,尤其是對多個組的設(shè)計(jì),這要根據(jù)你的需要來考慮。

          posted on 2011-04-16 20:47 蜂鳥 閱讀(394) 評論(0)  編輯  收藏 所屬分類: openSource

          公告

           掌握了XML就掌握了未來!

          導(dǎo)航

          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          統(tǒng)計(jì)

          常用鏈接

          留言簿

          隨筆檔案(1)

          文章分類(17)

          文章檔案(17)

          搜索

          最新評論

          主站蜘蛛池模板: 平湖市| 陵川县| 阜新| 桃园县| 保定市| 博乐市| 沅陵县| 宁夏| 阜城县| 东乌珠穆沁旗| 德安县| 辉县市| 梁平县| 南安市| 高唐县| 开江县| 小金县| 石城县| 龙岩市| 温宿县| 连平县| 聊城市| 裕民县| 楚雄市| 柘荣县| 疏勒县| 贵溪市| 宕昌县| 拜泉县| 台湾省| 云霄县| 定安县| 通州市| 旺苍县| 成安县| 浦江县| 浠水县| 峨边| 泾川县| 昌黎县| 分宜县|