你期待已久的Python網絡數據爬蟲教程來了。本文爲你演示如何從網頁裏找到感興趣的連接和說明文字,抓取並存儲到Excel。php
我在公衆號後臺,常常能夠收到讀者的留言。css
不少留言,是讀者的疑問。只要有時間,我都會抽空嘗試解答。html
可是有的留言,乍看起來就不明因此了。python
例以下面這個:git
一分鐘後,他可能以爲不妥(大概由於想起來,我用簡體字寫文章),因而又用簡體發了一遍。github
我恍然大悟。web
這位讀者覺得個人公衆號設置了關鍵詞推送對應文章功能。因此看了個人其餘數據科學教程後,想看「爬蟲」專題。shell
很差意思,當時我尚未寫爬蟲文章。數據庫
並且,個人公衆號暫時也沒有設置這種關鍵詞推送。編程
主要是由於我懶。
這樣的消息接收得多了,我也能體察到讀者的需求。不止一個讀者表達出對爬蟲教程的興趣。
以前提過,目前主流而合法的網絡數據收集方法,主要分爲3類:
前兩種方法,我都已經作過一些介紹,此次說說爬蟲。
許多讀者對爬蟲的定義,有些混淆。我們有必要辨析一下。
維基百科是這麼說的:
網絡爬蟲(英語:web crawler),也叫網絡蜘蛛(spider),是一種用來自動瀏覽萬維網的網絡機器人。其目的通常爲編纂網絡索引。
這問題就來了,你又不打算作搜索引擎,爲何對網絡爬蟲那麼熱心呢?
其實,許多人口中所說的爬蟲(web crawler),跟另一種功能「網頁抓取」(web scraping)搞混了。
維基百科上,對於後者這樣解釋:
Web scraping, web harvesting, or web data extraction is data scraping used for extracting data from websites. Web scraping software may access the World Wide Web directly using the Hypertext Transfer Protocol, or through a web browser.
看到沒有,即使你用瀏覽器手動拷貝數據下來,也叫作網頁抓取(web scraping)。是否是馬上以爲本身強大了不少?
可是,這定義還沒完:
While web scraping can be done manually by a software user, the term typically refers to automate processes implemented using a bot or web crawler.
也就是說,用爬蟲(或者機器人)自動替你完成網頁抓取工做,纔是你真正想要的。
數據抓下來幹什麼呢?
通常是先存儲起來,放到數據庫或者電子表格中,以備檢索或者進一步分析使用。
因此,你真正想要的功能是這樣的:
找到連接,得到Web頁面,抓取指定信息,存儲。
這個過程有可能會往復循環,甚至是滾雪球。
你但願用自動化的方式來完成它。
瞭解了這一點,你就不要老盯着爬蟲不放了。爬蟲研製出來,實際上是爲了給搜索引擎編制索引數據庫使用的。你爲了抓取點兒數據拿來使用,已是大炮轟蚊子了。
要真正掌握爬蟲,你須要具有很多基礎知識。例如HTML, CSS, Javascript, 數據結構……
這也是爲何我一直猶豫着沒有寫爬蟲教程的緣由。
不過這兩天,看到王爍主編的一段話,頗有啓發:
我喜歡講一個另類二八定律,就是付出兩成努力,瞭解一件事的八成。
既然咱們的目標很明確,就是要從網頁抓取數據。那麼你須要掌握的最重要能力,是拿到一個網頁連接後,如何從中快捷有效地抓取本身想要的信息。
掌握了它,你還不能說本身已經學會了爬蟲。
但有了這個基礎,你就能比以前更輕鬆獲取數據了。特別是對「文科生」的不少應用場景來講,很是有用。這就是賦能。
並且,再進一步深刻理解爬蟲的工做原理,也變得輕鬆許多。
這也算「另類二八定律」的一個應用吧。
Python語言的重要特點之一,就是能夠利用強大的軟件工具包(許多都是第三方提供)。你只須要編寫簡單的程序,就能自動解析網頁,抓取數據。
本文給你演示這一過程。
要抓取網頁數據,咱們先制訂一個小目標。
目標不能太複雜。可是完成它,應該對你理解抓取(Web Scraping)有幫助。
就選擇我最近發佈的一篇簡書文章做爲抓取對象好了。題目叫作《如何用《玉樹芝蘭》入門數據科學?》。
這篇文章裏,我把以前的發佈的數據科學系列文章作了從新組織和串講。
文中包含不少以前教程的標題和對應連接。例以下圖紅色邊框圈起來的部分。
假設你對文中提到教程都很感興趣,但願得到這些文章的連接,而且存儲到Excel裏,就像下面這個樣子:
你須要把非結構化的分散信息(天然語言文本中的連接),專門提取整理,而且存儲下來。
該怎麼辦呢?
即使不會編程,你也能夠全文通讀,逐個去找這些文章連接,手動把文章標題、連接都分別拷貝下來,存到Excel表裏面。
可是,這種手工採集方法沒有效率。
咱們用Python。
要裝Python,比較省事的辦法是安裝Anaconda套裝。
請到這個網址下載Anaconda的最新版本。
請選擇左側的 Python 3.6 版本下載安裝。
若是你須要具體的步驟指導,或者想知道Windows平臺如何安裝並運行Anaconda命令,請參考我爲你準備的視頻教程。
安裝好Anaconda以後,請到這個網址下載本教程配套的壓縮包。
下載後解壓,你會在生成的目錄(下稱「演示目錄」)裏面看到如下三個文件。
打開終端,用cd命令進入該演示目錄。若是你不瞭解具體使用方法,也能夠參考視頻教程。
咱們須要安裝一些環境依賴包。
首先執行:
pip install pipenv
複製代碼
這裏安裝的,是一個優秀的 Python 軟件包管理工具 pipenv 。
安裝後,請執行:
pipenv install
複製代碼
看到演示目錄下兩個Pipfile開頭的文件了嗎?它們就是 pipenv 的設置文檔。
pipenv 工具會依照它們,自動爲咱們安裝所須要的所有依賴軟件包。
上圖裏面有個綠色的進度條,提示所需安裝軟件數量和實際進度。
裝好後,根據提示咱們執行:
pipenv shell
複製代碼
此處請確認你的電腦上已經安裝了 Google Chrome 瀏覽器。
咱們執行:
jupyter notebook
複製代碼
默認瀏覽器(Google Chrome)會開啓,並啓動 Jupyter 筆記本界面:
你能夠直接點擊文件列表中的第一項ipynb文件,能夠看到本教程的所有示例代碼。
你能夠一邊看教程的講解,一邊依次執行這些代碼。
可是,我建議的方法,是回到主界面下,新建一個新的空白 Python 3 筆記本。
請跟着教程,一個個字符輸入相應的內容。這能夠幫助你更爲深入地理解代碼的含義,更高效地把技能內化。
準備工做結束,下面咱們開始正式輸入代碼。
讀入網頁加以解析抓取,須要用到的軟件包是 requests_html 。咱們此處並不須要這個軟件包的所有功能,只讀入其中的 HTMLSession 就能夠。
from requests_html import HTMLSession
複製代碼
而後,咱們創建一個會話(session),即讓Python做爲一個客戶端,和遠端服務器交談。
session = HTMLSession()
複製代碼
前面說了,咱們打算採集信息的網頁,是《如何用《玉樹芝蘭》入門數據科學?》一文。
咱們找到它的網址,存儲到url變量名中。
url = 'https://juejin.im/post/5b339e99f265da597d0aac5f'
複製代碼
下面的語句,利用 session 的 get 功能,把這個連接對應的網頁整個兒取回來。
r = session.get(url)
複製代碼
網頁裏面都有什麼內容呢?
咱們告訴Python,請把服務器傳回來的內容看成HTML文件類型處理。我不想要看HTML裏面那些亂七八糟的格式描述符,只看文字部分。
因而咱們執行:
print(r.html.text)
複製代碼
這就是得到的結果了:
咱們內心有數了。取回來的網頁信息是正確的,內容是完整的。
好了,咱們來看看怎麼趨近本身的目標吧。
咱們先用簡單粗暴的方法,嘗試得到網頁中包含的所有連接。
把返回的內容做爲HTML文件類型,咱們查看 links 屬性:
r.html.links
複製代碼
這是返回的結果:
這麼多連接啊!
很興奮吧?
不過,你發現沒有?這裏許多連接,看似都不徹底。例如第一條結果,只有:
'/'
複製代碼
這是什麼東西?是否是連接抓取錯誤啊?
不是,這種看着不像連接的東西,叫作相對連接。它是某個連接,相對於咱們採集的網頁所在域名(https://www.jianshu.com)的路徑。
這就好像咱們在國內郵寄快遞包裹,填單子的時候通常會寫「XX省XX市……」,前面不須要加上國家名稱。只有國際快遞,才須要寫上國名。
可是若是咱們但願得到所有能夠直接訪問的連接,怎麼辦呢?
很容易,也只須要一條 Python 語句。
r.html.absolute_links
複製代碼
這裏,咱們要的是「絕對」連接,因而咱們就會得到下面的結果:
這回看着是否是就舒服多了?
咱們的任務已經完成了吧?連接不是都在這裏嗎?
連接確實都在這裏了,但是跟咱們的目標是否是有區別呢?
檢查一下,確實有。
咱們不光要找到連接,還得找到連接對應的描述文字呢,結果裏包含嗎?
沒有。
結果列表中的連接,都是咱們須要的嗎?
不是。看長度,咱們就能感受出許多連接並非文中描述其餘數據科學文章的網址。
這種簡單粗暴直接羅列HTML文件中全部連接的方法,對本任務行不通。
那麼咱們該怎麼辦?
咱們得學會跟 Python 說清楚咱們要找的東西。這是網頁抓取的關鍵。
想一想看,若是你想讓助手(人類)幫你作這事兒,怎麼辦?
你會告訴他:
「尋找正文中所有能夠點擊的藍色文字連接,拷貝文字到Excel表格,而後右鍵複製對應的連接,也拷貝到Excel表格。每一個連接在Excel佔一行,文字和連接各佔一個單元格。」
雖然這個操做執行起來麻煩,可是助手聽懂後,就能幫你執行。
一樣的描述,你試試說給電腦聽……很差意思,它不理解。
由於你和助手看到的網頁,是這個樣子的。
電腦看到的網頁,是這個樣子的。
爲了讓你看得清楚源代碼,瀏覽器還特地對不一樣類型的數據用了顏色區分,對行作了編號。
數據顯示給電腦時,上述輔助可視功能是沒有的。它只能看見一串串字符。
那可怎麼辦?
仔細觀察,你會發現這些HTML源代碼裏面,文字、圖片連接內容先後,都會有一些被尖括號括起來的部分,這就叫作「標記」。
所謂HTML,就是一種標記語言(超文本標記語言,HyperText Markup Language)。
標記的做用是什麼?它能夠把整個的文件分解出層次來。
(圖片來源:https://goo.gl/kWCqS6)
如同你要發送包裹給某我的,能夠按照「省-市-區-街道-小區-門牌」這樣的結構來寫地址,快遞員也能夠根據這個地址找到收件人。
一樣,咱們對網頁中某些特定內容感興趣,能夠依據這些標記的結構,順藤摸瓜找出來。
這是否是意味着,你必須先學會HTML和CSS,才能進行網頁內容抓取呢?
不是的,咱們能夠藉助工具,幫你顯著簡化任務複雜度。
這個工具,Google Chrome瀏覽器自帶。
咱們在樣例文章頁面上,點擊鼠標右鍵,在出現的菜單裏面選擇「檢查」。
這時,屏幕下方就會出現一個分欄。
咱們點擊這個分欄左上角(上圖紅色標出)的按鈕。而後把鼠標懸停在第一個文內連接(《玉樹芝蘭》)上面,點擊一下。

此時,你會發現下方分欄裏面,內容也發生了變化。這個連接對應的源代碼被放在分欄區域正中,高亮顯示。
確認該區域就是咱們要找的連接和文字描述後,咱們鼠標右鍵選擇高亮區域,而且在彈出的菜單中,選擇 Copy -> Copy selector。
找一個文本編輯器,執行粘貼,就能夠看見咱們究竟複製下來了什麼內容。
body > div.note > div.post > div.article > div.show-content > div > p:nth-child(4) > a
複製代碼
這一長串的標記,爲電腦指出了:請你先找到 body 標記,進入它管轄的這個區域後去找 div.note
標記,而後找……最後找到 a 標記,這裏就是要找的內容了。
回到我們的 Jupyter Notebook 中,用剛纔得到的標記路徑,定義變量sel。
sel = 'body > div.note > div.post > div.article > div.show-content > div > p:nth-child(4) > a'
複製代碼
咱們讓 Python 從返回內容中,查找 sel 對應的位置,把結果存到 results 變量中。
results = r.html.find(sel)
複製代碼
咱們看看 results 裏面都有什麼。
results
複製代碼
這是結果:
[<Element 'a' href='https://www.jianshu.com/nb/130182' target='_blank'>]
複製代碼
results 是個列表,只包含一項。這一項包含一個網址,就是咱們要找的第一個連接(《玉樹芝蘭》)對應的網址。
但是文字描述「《玉樹芝蘭》」哪裏去了?
彆着急,咱們讓 Python 顯示 results 結果數據對應的文本。
results[0].text
複製代碼
這是輸出結果:
'玉樹芝蘭'
複製代碼
咱們把連接也提取出來:
results[0].absolute_links
複製代碼
顯示的結果倒是一個集合。
{'https://www.jianshu.com/nb/130182'}
複製代碼
咱們不想要集合,只想要其中的連接字符串。因此咱們先把它轉換成列表,而後從中提取第一項,即網址連接。
list(results[0].absolute_links)[0]
複製代碼
此次,終於得到咱們想要的結果了:
'https://www.jianshu.com/nb/130182'
複製代碼
有了處理這第一個連接的經驗,你信心大增,是吧?
其餘連接,也無非是找到標記路徑,而後照貓畫虎嘛。
但是,若是每找一個連接,都須要手動輸入上面這若干條語句,那也太麻煩了。
這裏就是編程的技巧了。重複逐條運行的語句,若是工做順利,咱們就要嘗試把它們歸併起來,作個簡單的函數。
對這個函數,只需給定一個選擇路徑(sel),它就把找到的全部描述文本和連接路徑都返回給咱們。
def get_text_link_from_sel(sel):
mylist = []
try:
results = r.html.find(sel)
for result in results:
mytext = result.text
mylink = list(result.absolute_links)[0]
mylist.append((mytext, mylink))
return mylist
except:
return None
複製代碼
咱們測試一下這個函數。
仍是用剛纔的標記路徑(sel)不變,試試看。
print(get_text_link_from_sel(sel))
複製代碼
輸出結果以下:
[('玉樹芝蘭', 'https://www.jianshu.com/nb/130182')]
複製代碼
沒問題,對吧?
好,咱們試試看第二個連接。
咱們仍是用剛纔的方法,使用下面分欄左上角的按鈕點擊第二個連接。
下方出現的高亮內容就發生了變化:
咱們仍是用鼠標右鍵點擊高亮部分,拷貝出 selector。
而後咱們直接把得到的標記路徑寫到 Jupyter Notebook 裏面。
sel = 'body > div.note > div.post > div.article > div.show-content > div > p:nth-child(6) > a'
複製代碼
用咱們剛纔編制的函數,看看輸出結果是什麼?
print(get_text_link_from_sel(sel))
複製代碼
輸出以下:
[('如何用Python作詞雲?', 'https://juejin.im/post/5b34a409f265da599f68dc3b')]
複製代碼
檢驗完畢,函數沒有問題。
下一步作什麼?
你還打算去找第三個連接,仿照剛纔的方法作?
那你還不如全文手動摘取信息算了,更省事兒一些。
咱們要想辦法把這個過程自動化。
對比一下剛剛兩次咱們找到的標記路徑:
body > div.note > div.post > div.article > div.show-content > div > p:nth-child(4) > a
複製代碼
以及:
body > div.note > div.post > div.article > div.show-content > div > p:nth-child(6) > a
複製代碼
發現什麼規律沒有?
對,路徑上其餘的標記全都是同樣的,惟獨倒數第二個標記("p")後冒號後內容有區別。
這就是咱們自動化的關鍵了。
上述兩個標記路徑裏面,由於指定了在第幾個「子」(nth-child
)文本段(paragraph,也就是"p"表明的含義)去找"a"這個標記,所以只返回來單一結果。
若是咱們不限定"p"的具體位置信息呢?
咱們試試看,此次保留標記路徑裏面其餘所有信息,只修改"p"這一點。
sel = 'body > div.note > div.post > div.article > div.show-content > div > p > a'
複製代碼
再次運行咱們的函數:
print(get_text_link_from_sel(sel))
複製代碼
這是輸出結果:
好了,咱們要找的內容,全都在這兒了。
可是,咱們的工做還沒完。
咱們還得把採集到的信息輸出到Excel中保存起來。
還記得咱們經常使用的數據框工具 Pandas 嗎?又該讓它大顯神通了。
import pandas as pd
複製代碼
只須要這一行命令,咱們就能把剛纔的列表變成數據框:
df = pd.DataFrame(get_text_link_from_sel(sel))
複製代碼
讓咱們看看數據框內容:
df
複製代碼
內容沒問題,不過咱們對錶頭不大滿意,得更換爲更有意義的列名稱:
df.columns = ['text', 'link']
複製代碼
再看看數據框內容:
df
複製代碼
好了,下面就能夠把抓取的內容輸出到Excel中了。
Pandas內置的命令,就能夠把數據框變成csv格式,這種格式能夠用Excel直接打開查看。
df.to_csv('output.csv', encoding='gbk', index=False)
複製代碼
注意這裏須要指定encoding(編碼)爲gbk,不然默認的utf-8編碼在Excel中查看的時候,有多是亂碼。
咱們看看最終生成的csv文件吧。
頗有成就感,是否是?
本文爲你展現了用Python自動網頁抓取的基礎技能。但願閱讀並動手實踐後,你能掌握如下知識點:
或許,你以爲這篇文章過於淺白,不能知足你的要求。
文中只展現瞭如何從一個網頁抓取信息,可你要處理的網頁成千上萬啊。
彆着急。
本質上說,抓取一個網頁,和抓取10000個網頁,在流程上是同樣的。
並且,從我們的例子裏,你是否是已經嘗試了抓取連接?
有了連接做爲基礎,你就能夠滾雪球,讓Python爬蟲「爬」到解析出來的連接上,作進一步的處理。
未來,你可能還要應對實踐場景中的一些棘手問題:
這些問題的解決辦法,我但願在從此的教程裏面,一一和你分享。
須要注意的是,網絡爬蟲抓取數據,雖然功能強大,但學習與實踐起來有必定門檻。
當你面臨數據獲取任務時,應該先檢查一下這個清單:
若是答案是都沒有,才須要你本身編寫腳本,調動爬蟲來抓取。
爲了鞏固學習的知識,請你換一個其餘網頁,以我們的代碼做爲基礎修改後,抓取其中你感興趣的內容。
若是能把你抓取的過程記錄下來,在評論區將記錄連接分享給你們,就更好了。
由於刻意練習是掌握實踐技能的最好方式,而教是最好的學。
祝順利!
本文主要內容講解完畢。
這裏給你提一個疑問,供你思考:
咱們解析而且存儲的連接,實際上是有重複的:
這並非咱們的代碼有誤,而是在《如何用《玉樹芝蘭》入門數據科學?》一文裏,原本就屢次引用過一些文章,因此重複的連接就都被抓取出來了。
可是你存儲的時候,也許不但願保留重複連接。
這種狀況下,你該如何修改代碼,才能保證抓取和保存的連接沒有重複呢?
你對Python爬蟲感興趣嗎?在哪些數據採集任務上使用過它?有沒有其餘更高效的方式,來達成數據採集目的?歡迎留言,把你的經驗和思考分享給你們,咱們一塊兒交流討論。
喜歡請點贊。還能夠微信關注和置頂個人公衆號「玉樹芝蘭」(nkwangshuyi)。
若是你對數據科學感興趣,不妨閱讀個人系列教程索引貼《如何高效入門數據科學?》,裏面還有更多的有趣問題及解法。