一日一技:拼接個(gè)URL你也能搞錯(cuò),還寫(xiě)個(gè)爬蟲(chóng)
在寫(xiě)爬蟲(chóng)的過(guò)程中,我們經(jīng)常需要解析網(wǎng)站的列表頁(yè)。例如下面這個(gè)例子:
- <html>
- <head>
- <meta charset="utf-8">
- <title>測(cè)試相對(duì)路徑</title>
- </head>
- <body>
- <div>
- <h1>書(shū)籍列表</h1>
- <ul>
- <li><a href="http://127.0.0.1:8000/book/1.html">第一本書(shū)</a></li>
- <li><a href="http://127.0.0.1:8000/book/2.html">第二本書(shū)</a></li>
- <li><a href="http://127.0.0.1:8000/book/3.html">第三本書(shū)</a></li>
- <li><a href="http://127.0.0.1:8000/book/4.html">第四本書(shū)</a></li>
- <li><a href="http://127.0.0.1:8000/book/5.html">第五本書(shū)</a></li>
- </ul>
- </div>
- </body>
- </html>
運(yùn)行效果如下圖所示:
這種情況下,我想獲取每一項(xiàng)的URL非常簡(jiǎn)單,直接寫(xiě)一個(gè)XPath就可以了,如下圖所示:
仔細(xì)觀察你會(huì)發(fā)現(xiàn),每一個(gè)連接的URL都是以http://127.0.0.1:8000開(kāi)頭的。而當(dāng)前列表頁(yè)的地址也是http://127.0.0.1:8000。所以為了簡(jiǎn)單起見(jiàn),標(biāo)簽里面可以使用相對(duì)路徑:
- <html>
- <head>
- <meta charset="utf-8">
- <title>測(cè)試相對(duì)路徑</title>
- </head>
- <body>
- <div>
- <h1>書(shū)籍列表</h1>
- <ul>
- <li><a href="/book/1.html">第一本書(shū)</a></li>
- <li><a href="/book/2.html">第二本書(shū)</a></li>
- <li><a href="/book/3.html">第三本書(shū)</a></li>
- <li><a href="/book/4.html">第四本書(shū)</a></li>
- <li><a href="/book/5.html">第五本書(shū)</a></li>
- </ul>
- </div>
- </body>
- </html>
運(yùn)行效果如下圖所示,用XPath只能提取到半截URL:
但是瀏覽器可以正確識(shí)別這樣的相對(duì)地址,并且當(dāng)你點(diǎn)擊的時(shí)候,它能自動(dòng)跳轉(zhuǎn)到正確的地址:
相對(duì)路徑如果是以/開(kāi)頭,那么就會(huì)在相對(duì)路徑前面拼接上網(wǎng)站的主域名。
但如果當(dāng)前列表頁(yè)的地址跟鏈接的相對(duì)路徑有一部分重疊怎么辦?如下圖所示:
當(dāng)前頁(yè)面的地址是http://127.0.0.1:8000/book。而相對(duì)地址是/book/1.html。這種情況下,還可以進(jìn)一步簡(jiǎn)化,在相對(duì)路徑的前面不要加斜杠,把HTML改成:
- <html>
- <head>
- <meta charset="utf-8">
- <title>測(cè)試相對(duì)路徑</title>
- </head>
- <body>
- <div>
- <h1>書(shū)籍列表</h1>
- <ul>
- <li><a href="1.html">第一本書(shū)</a></li>
- <li><a href="2.html">第二本書(shū)</a></li>
- <li><a href="3.html">第三本書(shū)</a></li>
- <li><a href="4.html">第四本書(shū)</a></li>
- <li><a href="5.html">第五本書(shū)</a></li>
- </ul>
- </div>
- </body>
- </html>
運(yùn)行效果如下圖所示:
這種情況下,瀏覽器依然能給正確識(shí)別,如下圖所示:
瀏覽器知道,如果相對(duì)路徑?jīng)]有用/開(kāi)頭,那么它就會(huì)把當(dāng)前頁(yè)面的URL與相對(duì)路徑拼接起來(lái)。但需要注意的是,在拼接的時(shí)候,會(huì)取最右側(cè)斜杠左邊的部分。而右邊的部分會(huì)丟棄。就相當(dāng)于拼接文件地址的時(shí)候,用這個(gè)文件所在的文件夾來(lái)拼接新的地址。如下圖所示:
如果你記不住怎么區(qū)分的話(huà),你可以使用Python自帶的urllib.parse.urljoin來(lái)連接,如下圖所示:
看到這里,你可能覺(jué)得我今天又水了一篇文章。這么簡(jiǎn)單的東西也值得寫(xiě)一篇文章來(lái)講?
那么我們來(lái)看下面這個(gè)例子:
域名是http://127.0.0.1:8000/book/index.html,相對(duì)域名是1.html,但為什么瀏覽器自動(dòng)識(shí)別出來(lái)的URL是www.kingname.info/1.html?
這個(gè)問(wèn)題的關(guān)鍵,在于源代碼里面的標(biāo)簽:
- <html>
- <head>
- <meta charset="utf-8">
- <title>測(cè)試相對(duì)路徑</title>
- <base href="http://www.kingname.info">
- </head>
- <body>
- <div>
- <h1>書(shū)籍列表</h1>
- <ul>
- <li><a href="1.html">第一本書(shū)</a></li>
- <li><a href="2.html">第二本書(shū)</a></li>
- <li><a href="3.html">第三本書(shū)</a></li>
- <li><a href="4.html">第四本書(shū)</a></li>
- <li><a href="5.html">第五本書(shū)</a></li>
- </ul>
- </div>
- </body>
- </html>
如果HTML代碼頭部有標(biāo)簽,那么,它的href屬性的值,會(huì)被用來(lái)跟相對(duì)路徑拼接出一個(gè)絕對(duì)路徑,而不會(huì)再用當(dāng)前頁(yè)面的URL來(lái)拼接。
如果你不知道這一點(diǎn)的話(huà),你的爬蟲(chóng)在拼接子頁(yè)面URL的時(shí)候可能就會(huì)出問(wèn)題。網(wǎng)站也可以使用這個(gè)機(jī)制構(gòu)造出一個(gè)蜜罐,根據(jù)標(biāo)簽拼出來(lái)的URL才是真正的子頁(yè)面地址,而用當(dāng)前頁(yè)面URL去拼接的URL是蜜罐地址,爬蟲(chóng)訪(fǎng)問(wèn)進(jìn)去以后,就會(huì)抓到假數(shù)據(jù),或者被立即屏蔽。
關(guān)于標(biāo)簽的詳細(xì)說(shuō)明,大家可以閱讀:: The Document Base URL element[1]。
參考文獻(xiàn)
[1] The Document Base URL element: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base