網頁正文及內容圖片提取算法

ygp8 9年前發布 | 18K 次閱讀 算法

問題:如何提取任意(尤其是新聞、資訊類)網頁的正文內容,提取與文章內容相關的圖片,源碼可見:extractor.py

抓取單個網站網頁內容時通常采用正則匹配的方式,但不同網站之間結構千奇百怪,很難用統一的正則表達式進行匹配。《基于行塊分布函數的通用網頁正文抽取算法》的作者總結了一般從網頁中提取文章正文的方法,提出基于行塊分布的正文抽取算法,并給出了 PHP 、Java 等實現。這一算法的主要原理基于兩點:

  1. 正文區密度:在去除HTML中所有tag之后,正文區字符密度更高,較少出現多行空白;
  2. 行塊長度:非正文區域的內容一般單獨標簽(行塊)中較短。
  3. </ol>

    算法步驟如下:

    • 去除所有tag,包括樣式、Js腳本內容等,但保留原有的換行符\n:
      reCOMM = r'<!--.?-->'
      reTRIM = r'<{0}.
      ?>([\s\S]?)<\/{0}>'
      reTAG = r'<[\s\S]
      ?>|[ \t\r\f\v]'

      def processTags(body=""):
      body = re.sub(reCOMM, "", body) body = re.sub(reTRIM.format("script"), "" ,re.sub(reTRIM.format("style"), "", body)) body = re.sub(reTAG, "", body) return body</pre>

      • 將網頁內容按行分割,定義行塊 blocki 為第 [i,i+blockSize] 行文本之和并給出行塊長度基于行號的分布函數:
      • </ul>

        def processBlocks(body=""):
        ctexts = body.split("\n") textLens = [len(text) for text in ctexts] cblocks = [0] * (len(ctexts) - blockSize)

        lines = len(ctexts) for i in range(blockSize): cblocks = list(map(lambda x,y: x+y, textLens[i : lines-1-blockSize+i], cblocks)) return cblocks</pre>
        正文出現在最長的行塊,截取兩邊至行塊長度為 0 的范圍:

        def getContext(ctexts, cblocks):
        maxTextLen = max(cblocks)

        start = end = cblocks.index(maxTextLen) while start > 0 and cblocks[start] > min(textLens): start -= 1 while end < lines - blockSize and cblocks[end] > min(textLens): self.end += 1

        return "".join(ctexts[start:end])</pre>
        如果需要提取正文區域出現的圖片,只需要在第一步去除tag時保留<img>標簽的內容:

        reIMG  = re.compile(r'<img[\s\S]*?src=[\'|"]([\s\S]*?)[\'|"][\s\S]*?>')  
        def processImages(body):  
          return reIMG.sub(r'{{\1}}', body)

        總結

        以上算法基本可以應對大部分(中文)網頁正文的提取,針對有些網站正文圖片多于文字的情況,可以采用保留<img>標簽中圖片鏈接的方法,增加正文密度。目前少量測試發現的問題有:1)文章分頁或動態加載的網頁;2)評論長度過長喧賓奪主的網頁。

        參考

 本文由用戶 ygp8 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!