GNE 預處理技術——如何移除特定標籤但是保留文字到父標籤

  • 2019 年 10 月 7 日
  • 筆記

攝影:產品經理

廚師:kingname

在開發新聞網頁正文通用抽取器 GNE的過程中,需要對目標網頁的源程式碼進行一些預處理,從而提高正文抓取的準確性。其中之一就是把 <p>標籤內部的 <span>標籤中的文本,合併到 <p>標籤中,再刪除 <span> 標籤。

例如:

<html>      <head>          <title>演示合併節點</title>      </head>      <body>          <div>              <p>你好,<span>世界;</span>你好,<span>產品經理</span></p>          </div>      </body>  </html>

需要轉換為:

<html>      <head>          <title>演示合併節點</title>      </head>      <body>          <div>              <p>你好,世界;你好,產品經理</p>          </div>      </body>  </html>

在原來做定向爬蟲的時候,這本不是什麼問題,因為使用 XPath 可以直接提取所有內容:

from lxml.html import fromstring  selector = fromstring(html)  text = ''.join(selector.xpath('//p//text()'))  print(text)

運行效果如下圖所示:

但在通用新聞抽取器裡面不能這樣寫。因為並不是所有的 <p>標籤中的內容都是新聞正文。GNE 有一套演算法來計算並尋找全部包含真正有效內容的 <p>標籤。這就要求在預處理階段,需要把 <p>標籤裡面的 <span>標籤合併到 <p>標籤裡面。

可能有人的第一反應是:先把 <p> 標籤裡面的內容提取出來,然後再把 <span> 標籤裡面的內容提取出來,並添加到 <p> 標籤中。這不就解決問題了嗎?

但實際上並沒有這麼簡單。以上面的 HTML 程式碼為了,如果按照這種簡單的解法,那麼分別提取以後會得到如下內容:

現在問題來了,你怎麼知道 <span> 標籤中提取出來的這兩個字元串 世界, 產品經理,分別應該插入到 <p> 標籤結果列表中的哪個位置?所以這種方案並不可取。

那麼又有人問,能不能使用 XPath 的 string關鍵字把 <p> 標籤下面的所有文本直接提取出來,再作處理呢?這樣不就可以忽略標籤差異了嗎?在上面的 html 程式碼中,這種方案是可行的:

但是,這種方案不能應用到 GNE 中。這是由於這種做法,會無差別移除所有的標籤。但是 <p> 標籤下面的 <a>標籤是有用的,它在用於過濾導航欄或者推薦新聞這種類型的干擾內容中會起到很大的作用。所以 <a>標籤必需保留。

那麼,本文標題提到的問題:

如何移除指定標籤,但是保留它的文本,合併到父標籤中?

應該如何解決呢?

實際上,這個問題在 lxml 中有現成的辦法解決,他就是 etree.strip_tags

使用方法如下:

from lxml.html import etree    etree.strip_tags(element, '標籤1', '標籤2', '標籤3')

在本文的例子中,解決方案如下:

from lxml.html import fromstring, etree      selector = fromstring(html)  p_tag_list = selector.xpath('//p')  for p_tag in p_tag_list:      etree.strip_tags(p_tag, 'span')    text = ''.join(selector.xpath('//p/text()'))  print(text)

運行效果如下圖所示:

需要注意的是, etree.strip_tags()會直接修改原始Dom 樹,不需要返回修改結果。

GNE 的其他關鍵技術,將會在接下來的文章中逐一放出,你也可以點擊下方閱讀原文,跳轉到 GNE 的 Github 主頁,提前閱讀項目源程式碼。

未聞Code

PYTHON乾貨日更