一日一技:使用Python翻譯HTML中的文字字串
攝影:產品經理
麻婆豆腐和紅莧菜
相信大家都用過瀏覽器的翻譯網頁功能,例如對於下圖這個英文網頁:
一鍵翻譯成中文以後是這樣的:
你可能會覺得這個功能很簡單,不就是字串替換嗎?那你可以試一試把下面這個HTML片段中的 <p>
標籤下面的英文翻譯成中文。其它標籤中的不要改動:
<div> <p>if you want to parse date and time, your could use <em>datetime</em>, by use this library, you can generate now time by one line code <span>datetime.datetime.now()</span> this is so easy.</p> </div>
在 <em>
標籤中的 datetime
和 <span>
標籤中的 datetime.datetime.now()
不需要翻譯。
你一拍腦袋,馬上寫出了下面這幾行程式碼(假設你已經有了一個現成的 translate()
函式,傳入英文,輸出中文):
from lxml.html import fromstring source = '''<div> <p>if you want to parse date and time, your could use <em>datetime</em>, by use this library, you can generate now time by one line code <span>datetime.datetime.now()</span> this is so easy.</p> </div> ''' selector = fromstring(source) text_list = selector.xpath('//p/text()') for text in text_list: chinese = translate(text) ...
當你寫到這裡,你應該會愣一下。因為你突然發現一個問題,怎麼把中文替換回去?
不用嘗試去百度了。在今天(2022-06-20)之前,整個中文網路裡面,你找不到解決方法。
一個比較笨的辦法是直接對原始的HTML字串進行文字替換:
for text in text_list: chinese = translate(text) source = source.replace(text, chinese)
但這樣做,效率非常低。因為你要不停掃描整個HTML字串。一般一箇中型網站的HTML就有幾千上萬行,十幾二十萬個字元。你每翻譯一小段就全文替換一次,這個時間會非常漫長。
那有沒有辦法只對當前這一個 <p>
標籤裡面的文字進行替換呢?關鍵的問題來了,你替換可以,但是怎麼才能不影響這個 <p>
標籤下面的兩個子標籤?要保證文字和子標籤的相對位置不改變。
如果 <p>
標籤下面只有一段文字,沒有子標籤,那麼非常簡單,如下圖所示:
但現在的問題是, <p>
標籤下面有三段文字。每段文字之間還插入了其它的子標籤。我們怎麼樣對每一段文字進行替換,但是又保持文字的相對順序,並且還不能影響子標籤?
p.text
這種寫法首先就可以排除了,因為它沒有辦法指定替換第幾段文字。
你之所以會覺得這個問題很難解決,是因為你有一個錯覺,請看上面這張截圖,我列印了 text_list
。打印出來是一個包含字串的列表。所以你可能會覺得。使用lxml寫Xpath的時候, /text()
返回的總是包含字串的列表。
但實際上,返回的列表裡面的元素並不是字串,而是 _ElementUnicodeResult
物件。如下圖所示:
不是字串就簡單了,那麼我們可以獲取每一個文字物件的父標籤。然後修改父標籤下面的文字就可以了。
看到這裡,你肯定會問,這三個文字節點的父標籤,不都是同一個 <p>
嗎?如果你覺得是,那你就犯了想當然的錯誤。我們用程式碼來看看:
其實只有第一段文字的父標籤是 <p>
。第二段文字的父標籤,竟然是 <p>
的子標籤 <em>
。第三段文字的父標籤,是 <span>
。
等等,如果第二段文字的父標籤是 <em>
,那麼 <em>datetime</em>
裡面的 datetime
的父標籤是什麼?它的父標籤也是 <em>
!那麼問題來了, <em>
的 text()
文字節點,怎麼可能又是 datetime
,又是 <p>
下面的第二段文字呢?
實際上, <em>
的 text()
始終都是 datetime
。如下圖所示:
那麼, <p>
的第二段文字跟這個 <em>
標籤是什麼關係?實際上,這個關係叫做 tail
。如下圖所示:
在一個標籤裡面,只有第一段 text
是它真正的 text()
,如果這個標籤有子標籤,那麼位於子標籤後面的文字,是這個子標籤的 tail
。只不過當我們在正則表示式裡面寫 /text()
的時候,lxml會幫我們把所有子標籤的 tail
都算作當前標籤的text。
我們可以使用文字節點的 .is_text
和 .is_tail
來判斷它屬於哪種文字。最終執行效果如下圖所示:
END
我的爬蟲架構課開課啦!
爬蟲架構進階就在這裡
送未聞Code知識星球一年訂閱!
一二線大廠在職員工
十多年碼齡的程式設計老鳥
國內外高校在讀學生
中小學剛剛入門的新人
在 “未聞 Code技術交流群” 等你來!
入群方式:新增微信“mekingname”,備註“粉絲群”(謝絕廣告黨,非誠勿擾!)
好文和朋友一起看~
- 長見識,讓大家看看什麼是垃圾程式碼
- 一日一技:用一個奇技淫巧把字串轉成特定型別
- 最適合小白的Python學習神器!
- 【粉絲投稿】機器馬大佬的微軟面經
- 統計千行程式碼Bug率,有沒有意義?
- 一日一技:二分偏左,二分搜尋在分散式系統裡面也有用?
- 一日一技:使用Python翻譯HTML中的文字字串
- 一日一技:如何讓自己的工具函式在Python全域性可用?
- 一日一技:Any與TypeVar,讓IDE的自動補全更好用
- 一日一技:用Python做遊戲有多簡單
- 一日一技:如何批量給PDF新增水印?
- 一日一技:拋掉JavaScript,用HTML和Python做網站
- 一個讓我感到 "細思極恐" 的開源專案!
- 一日一技:FastAPI 介面限流
- 5 分鐘,使用內網穿透快速實現遠端手機桌面!
- Python Delorean 優秀的時間格式智慧轉換工具
- 寫在公眾號粉絲2w時
- 一日一技:協程與多程序的完美結合
- 一個 "喪心病狂" 的開源專案
- python中如何優雅的實現程式碼與敏感資訊分離?