我們知道,URL 由下面幾個(gè)部分組成:
其中Query部分,中文叫做查詢(xún)參數。它在 URL 中,是由等號連接的鍵值對。這些鍵值對有一些是有效的,例如:
https://open.163.com/newview/movie/courseintro?newurl=MDAPTVFE8
這個(gè)網(wǎng)址中的newurl=MDAPTVFE8是不能修改的,一旦你改了,那就不再是這個(gè)頁(yè)面了。
但還有一些網(wǎng)址,他們的查詢(xún)參數對網(wǎng)頁(yè)的顯示沒(méi)有任何影響,例如下面兩個(gè)網(wǎng)址:
https://www.163.com/dy/article/G7NINAJS0514HDK6.html?from=nav
https://www.163.com/dy/article/G7NINAJS0514HDK6.html
當你訪(fǎng)問(wèn)這兩個(gè)網(wǎng)址,你會(huì )發(fā)現它們打開(kāi)的是同一個(gè)頁(yè)面。因為這些參數是給網(wǎng)站用的。網(wǎng)站使用這些參數來(lái)統計用戶(hù)是從哪個(gè)頁(yè)面跳轉到這個(gè)頁(yè)面的。
在我們開(kāi)發(fā)新聞通用爬蟲(chóng)的時(shí)候,這種可有可無(wú)的查詢(xún)參數會(huì )對基于 URL 的去重導致嚴重干擾。同一篇新聞,可能因為從不同的頁(yè)面跳轉過(guò)來(lái),就有不同的查詢(xún)參數,那么就可能會(huì )被當做多篇不同的新聞。
我們在對新聞進(jìn)行去重的時(shí)候,一般會(huì )有一個(gè)三級去重邏輯:基于 URL 去重,基于新聞?wù)奈淖秩ブ?,基于正文語(yǔ)義去重。他們對資源的消耗逐漸增加,因此,如果能通過(guò) URL 確認是重復的新聞,就沒(méi)有必要經(jīng)過(guò)文本去重;能夠經(jīng)過(guò)文本確認是重復的新聞,就沒(méi)有必要使用語(yǔ)義去重。這種無(wú)效的參數,會(huì )導致進(jìn)入第二級的新聞數量增加,從而消耗更多的服務(wù)器資源。
為了防止這種無(wú)效的參數干擾基于 URL 去重的邏輯,因此我們需要提前移除無(wú)效的 URL 參數。
假設現在有一個(gè)網(wǎng)址:https://www.kingname.info/article?docid=123&from=nav&output=json&ts=1849304323。我們通過(guò)人工標注,已經(jīng)知道,對于https://www.kingname.info這個(gè)網(wǎng)站,docid和output參數是有效參數,必須保留;from和ts參數是無(wú)效參數,可以移除。那么,我們如何正確移除這些不需要的參數字段呢?
肯定有同學(xué)會(huì )說(shuō)使用正則表達式來(lái)移除。那么你可以試一試,正則表達式應該怎么寫(xiě)。提示一下,有一些參數值里面也會(huì )有=符號、有一些必要字段的值,可能恰好包含無(wú)效字段的名字。
今天,我們不使用正則表達式,而使用 Python 自帶的 urllib 模塊中的幾個(gè)函數來(lái)實(shí)現安全完美的移除無(wú)效字段的方法。
這個(gè)方法,需要使用到urlparse parse_qs urlencode和urlunparse。我們來(lái)看一段代碼:
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
url = 'https://www.kingname.info/article?docid=123&from=nav&output=json&ts=1849304323'
useless_field = ['from', 'ts']
parser = urlparse(url)
query = parser.query
query_dict = parse_qs(query)
for field in useless_field:
if field in query_dict:
query_dict.pop(field)
new_query = urlencode(query_dict, doseq=True)
new_parser = parser._replace(query=new_query)
new_url = urlunparse(new_parser)
print(new_url)
運行效果如下圖所示:
其中urlparse和urlunparse是一對相反的函數,其中前者把網(wǎng)址轉成ParseResult對象,后者把ParseResult對象轉回網(wǎng)址字符串。
ParseResult對象的.query屬性,是一個(gè)字符串,格式如下:
parse_qs與urlencode也是一對相反的方法。其中前者把 .query輸出的字符串轉成字典,而后者把字段轉成.query形式的字符串:
當我們使用parse_qs把 query轉成字典以后,就可以使用字典的.pop方法,把無(wú)效的字段都移除,然后重新生成新的.query字符串。
由于ParseResult對象的.query屬性是只讀屬性,不能覆蓋,因此我們需要調用一個(gè)內部方法parser._replace把新的.query字段替換上去,生成新的 ParseResult對象。最后再把它轉回網(wǎng)址。
使用這個(gè)方法,我們就可以安全地從 URL 中移除無(wú)效字段,而不用去寫(xiě)復雜的正則表達式了。
聯(lián)系客服