前言:
數據科學越來越火了,網頁是數據很大的一個來源。最近很多人問怎么抓網頁數據,據我所知,常見的編程語言(C++,java,python)都可以實現抓網頁數據,甚至很多統計\計算的語言(R,Matlab)都有可以實現和網站交互的包。本人試過用java,python,R抓網頁,感覺語法各有差異,邏輯上是一樣的。我准備用python來大概講講抓網頁是什么概念,具體的內容要自己看手冊或者google別人的博客,這里算是拋磚引玉了。水平有限,出現錯誤或者有更好的辦法,歡迎討論。. more info on 1point3acres.com
步驟一:熟悉Python的基本語法
已經熟悉Python的直接跳到步驟二。
Python是門比較容易入門的編程語言,如何上手視編程基礎而定。
(1) 如果有一定編程的基礎,建議看google's python class,鏈接https://developers.google.com/edu/python/?hl=zh-CN&csw=1-google 1point3acres
這個是一個為期兩天的短期培訓課程(當然,是兩個全天),大概是七個視頻,每個視頻之后給編程作業,每個作業一個小時之內可以完成。這是我學習python的第二門課(第一門是codecademy的python,很早之前看的,很多內容都記不得了),當時每天看視頻+編程作業一個多小時,六天弄完,效果還不錯,用python寫基本的程序沒有問題。
(2) 如果是沒有任何編程基礎,建議看coursera上Rice University開的An Introduction to Interactive Programming in Python。這門課我沒有跟過,但是看coursetalk的評論反映非常好,地里也有同學評論(點這里),課程鏈接:https://www.coursera.org/course/interactivepython。Udacity上的CS101也是不錯的選擇,地里有相關的討論帖(點這里),而且這門課就叫做build a search engine,會專門講一些和網絡相關的module。其他學習資源還有code school和codecademy,這些資源也是挺不錯的,但是編程量太少,初學者還是系統的跟課、多練練手來打好基礎吧。
當然,每個人的偏好不同,我推薦的不一定適合你。可以先看看這個帖子【長期加分貼】介紹你上過的公開課里面其他人是怎么說的,或者上coursetalk.org看看課程評論,再決定吧。
步驟二:學會如何與網站建立鏈接,得到網頁數據。
寫腳本與網站進行交互,要熟悉python和網頁相關的幾個module(urllib,urllib2,httplib)中的一個,知道一個即可,其他的都類似的。這三個是python提供的和網頁交互的基本module,還有其他的一些,比如:mechanize和scrappy,我沒有用過,可能有更好的性能,歡迎了解的來補充。基本的網頁抓取,前面的三個module足矣。
下面的代碼演示如何用urllib2與google scholar進行交互,獲得網頁信息。
# 導入模塊 urllib2 import urllib2 # 隨便查詢一篇文章,比如On random graph。對每一個查詢google # scholar都有一個url,這個url形成的規則是要自己分析的。 query = 'On+random+graph' url = 'http://scholar.google.com/scholar?hl=en&q=' + query + '&btnG=&as_sdt=1%2C5&as_sdtp=' # 設置頭文件。抓取有些的網頁不需要專門設置頭文件,但是這里如果不設置的話,. 鐗涗漢浜戦泦,涓€浜╀笁鍒嗗湴 # google會認為是機器人不允許訪問。另外訪問有些網站還有設置Cookie,這個會相對復雜一些, # 這里暫時不提。關於怎么知道頭文件該怎么寫,一些插件可以看到你用的瀏覽器和網站交互的 # 頭文件(這種工具很多瀏覽器是自帶的),我用的是firefox的firebug插件。.鏈枃鍘熷壋鑷�1point3acres璁哄潧 header = {'Host': 'scholar.google.com', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:26.0) Gecko/20100101 Firefox/26.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive'} # 建立連接請求,這時google的服務器返回頁面信息給con這個變量,con是一個對象 req = urllib2.Request(url, headers = header) con = urllib2.urlopen( req ) # 對con這個對象調用read()方法,返回的是html頁面,也就是有html標簽的純文本 doc = con.read() # 關閉連接。就像讀完文件要關閉文件一樣,如果不關閉有時可以、但有時會有問題, # 所以作為一個守法的好公民,還是關閉連接好了。 con.close()
以上的代碼就把在google scholar上查詢On Random Graph的結果返回到doc這個變量中了,這個和你打開google scholar搜索On Random Graph,然后將網頁右鍵保存的效果是一樣的。
步驟三、解析網頁
上面的步驟得到了網頁的信息,但是包括了html標簽,你要把這些標簽去掉,然后從html文本中整理出有用的信息,
你需要解析這個網頁。
解析網頁的方法:
(1) 正則表達式。正則表達式很有用,熟悉它節省很多的時間,有時候清洗數據不用寫腳本或者在數據庫上查詢,直接在notepad++上用正則表達式組合使用就行了。如何學習正則表達式建議看:正則表達式30分鍾入門教程,鏈接:http://deerchao.net/tutorials/regex/regex.htm. more info on 1point3acres.com
(2) BeautifulSoup模塊。BeautifulSoup是一個很強大的模塊,能把html文件解析成一個對象,這個對象是一棵樹。我們都知道html文件是樹狀的,比如 body -> table -> tbody -> tr,對於tbody這個節點,有很多個tr的子節點。BeautifulSoup可以很方便的取到特定的節點,對單個節點也可以取它的sibling node。網上有很多相關的說明,這里不細說,只演示簡單的代碼:
(3) 上面兩種方法結合使用。
# 導入BeautifulSoup模塊和re模塊,re是python中正則表達式的模塊 import BeautifulSoup import re # 生成一個soup對象,doc就是步驟二中提到的 soup = BeautifulSoup.BeautifulSoup(doc) # 抓取論文標題,作者,簡短描述,引用次數,版本數,引用它的文章列表的超鏈接 # 這里還用了一些正則表達式,不熟悉的先無知它好了。至於'class' : 'gs_rt'中 # 'gs_rt'是怎么來的,這個是分析html文件肉眼看出來的。上面提到的firebug插件 # 讓這個變的很簡單,只要一點網頁,就可以知道對應的html 標簽的位置和屬性, # 相當好用。 paper_name = soup.html.body.find('h3', {'class' : 'gs_rt'}).text paper_name = re.sub(r'\[.*\]', '', paper_name) # eliminate '[]' tags like '[PDF]' paper_author = soup.html.body.find('div', {'class' : 'gs_a'}).text. from: 1point3acres.com/bbs paper_desc = soup.html.body.find('div', {'class' : 'gs_rs'}).text temp_str = soup.html.body.find('div', {'class' : 'gs_fl'}).text temp_re = re.match(r'[A-Za-z\s]+(\d*)[A-Za-z\s]+(\d*)', temp_str) citeTimes = temp_re.group(1) versionNum = temp_re.group(2) if citeTimes == '': citeTimes = '0' if versionNum == '': versionNum = '0' citedPaper_href = soup.html.body.find('div', {'class' : 'gs_fl'}).a.attrs[0][1]
這些都是我在一個分析citation network的項目的代碼。順便一提,我從google scholar上抓取paper的信息以及引用列表的信息,訪問了大概1900次左右的時候給google block了,導致這個片區的ip一時無法登陸google scholar。
步驟四:存取數據
好不容易抓了數據,現在只是存儲在內存中,必須保存起來才能利用。
(1) 最簡單的方法之把數據寫進txt文件中,Python中可以用如下代碼實現:
# 打開文件webdata.txt,生成對象file,這個文件可以是不存在的,參數a表示往里面添加。 # 還有別的參數,比如'r'只能讀但不能寫入,'w'可以寫入但是會刪除原來的記錄等等 file = open('webdata.txt','a') line = paper_name + '#' + paper_author + '#' + paper_desc + '#' + citeTimes + '\n' # 對象file的write方法將字符串line寫入file中 file = file.write(line) # 再一次的,做個隨手關閉文件的好青年 file.close()
這樣,就把從網頁上抓到並且解析了的數據存儲到本地了,是不是很簡單?
(2) 當然,你也可以不寫入txt文件中,而是直接連接數據庫,python中的MySQLdb模塊可以實現和MySQL數據庫的交互,把數據直接倒到數據庫里面,與MySQL數據庫建立鏈接的邏輯和與網站服務器建立鏈接的邏輯差不多。如果之前有學習過數據庫,學習用MySQLdb模塊實現和數據庫的交互是很簡單的;如果沒有,則要借助在coursera\stanford openEdX平台上都有開設的Introduction to Database來系統學習,w3school用來參考或者當成手冊。
Python能夠鏈接數據庫的前提是數據庫是開着的,我用的是 win7 + MySQL5.5,數據庫在本地。
%可以用cmd開啟數據庫,啟動命令是: net start mysql55 %關閉命令是: net stop mysql55
使用MySQLdb模塊代碼示例:
# 導入 MySQLdb模塊 import MySQLdb # 和服務器建立鏈接,host是服務器ip,我的MySQL數據庫搭建在本機,默認的是127.0.0.1, # 用戶、密碼、數據庫名稱對應着照輸就行了,默認的端口號是3306,charset是編碼方式, # 默認的是utf8(也有可能是gbk,看安裝的版本)。. visit 1point3acres.com for more. conn = MySQLdb.connect(host='127.0.0.1', user='root', passwd='yourPassword', db='dbname', port=3306, charset='utf8'). 鍥磋鎴戜滑@1point 3 acres # 建立cursor cur = conn.cursor()# 通過對象cur的execute()方法執行SQL語句 cur.execute("select * from citeRelation where paperName = 'On Random Graph'") # fetchall()方法獲得查詢結果,返回的是一個list,可以直接這樣查詢:list[i][j], # i表示查詢結果中的第i+1條record,j表示這條記錄的第j+1個attribute(別忘了python從0開始計數) list = cur.fetchall() # 也可以進行delete,drop,insert,update等操作,比如 sql = "update studentCourseRecord set fail = 1 where studentID = '%s' and semesterID = '%s' and courseID = '%s'" %(studentID,course[0],course[1]) cur.execute(sql) # 與查詢不同的是,執行完delete,insert,update這些語句后必須執行下面的命令才能成功更新數據庫 conn.commit() # 一如既往的,用完了之后記得關閉cursor,然后關閉鏈接 cur.close() conn.close()
這樣就實現了Python和數據庫之間的交互。除了MySQL數據庫外,python的PyGreSQL模塊可以支持postgreSQL數據庫,道理類似的。還有,如果你的網頁里面包含了中文,設置編碼格式會非常的麻煩,需要服務器、Python、數據庫和數據庫界面采用相同的編碼格式才能不出現亂碼,如果真的出現了中文亂碼的問題,請相信,你不是一個人!!去google一下吧,成千上萬的人碰到過這種問題。
關於編碼的問題,附一篇我看到的博文<python編碼問題總結>:
http://www.xprogrammer.com/1258.html
后記:
上面介紹了抓取網頁數據的方法,抓取數據只是一小步,如何分析數據就是大學問了,歡迎討論。
上面有什么地方講不清楚的,歡迎交流。
特別注意:
大規模抓取網站會給網站的服務器帶來很大的壓力,盡量選擇服務器相對輕松的時段(比如凌晨)。網站很多,不要拿一畝三分地來做試驗。
Python的time模塊的sleep()方法可以讓程序暫停一段時間,比如time.sleep(1)讓程序運行到這里的時候暫停1秒。適時地暫停可以緩解服務器的壓力,也可以保護自己的硬盤,正好碼久了睡個覺,或者去趟gym,結果就出來了。