最近,想從中國天氣網上抓取數據,其中的網頁上的實時天氣是使用javascript生成的,用簡單的標簽解析不到。原因是,那個標簽壓根就沒再網頁當中。
所以,google了下python怎么區解析動態網頁,下面文章對我很有幫助。
轉載記錄:Python在Web Page抓取、JS解析方面的介紹
因為我只希望在mac下解析,所以我並沒有使用擴平台的庫。在使用spidermonkey后,發現它還是很全面,比如document.write就無法執行(如果我的認識有錯誤,請指出,謝謝)。我將目光落在了pywebkitgtk上,可惜安裝不成功,逼迫我放棄了(我有考慮過使用pyv8,但是還是放棄了)。
在經歷了失敗后,我還是從homebrew這個神器上發現了希望。它可以幫你安裝pyqt,可能知道它是一個python的界面庫,但是它同樣擁有網絡模塊(webkit),當然也可以使用它來解析網頁。
我將分析一下我解析動態網頁的過程,此過程實現多於原理學習:
第一步:解析靜態網頁標簽
1 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 2 <html> 3 <head> 4 <title>javascript測試網頁</title> 5 </head> 6 <body> 7 <script type="text/javascript" src="./5757.js"> 8 </script> 9 </body> 10 </html>
上面是測試用的html代碼,我將解析它的title標簽,很簡單,呵呵~
1 #! /usr/bin/env python 2 3 from htmlentitydefs import entitydefs 4 from HTMLParser import HTMLParser 5 import sys,urllib2 6 7 class DataParser(HTMLParser): 8 def __init__(self): 9 self.title = None 10 self.isTag = 0 11 HTMLParser.__init__(self) 12 13 def handle_starttag(self,tag,attrs): 14 if tag == 'title': 15 self.isTag = 1 16 17 18 def handle_data(self,data): 19 if self.isTag: 20 self.title = data 21 22 def handle_endtag(self,tag): 23 if tag == 'title': 24 self.isTag = 0 25 def getTitle(self): 26 return self.title 27 28 url = 'file:///Users/myName/Desktop/pyqt/2.html' 29 #''中內容用瀏覽器打開,直接復制地址欄的內容即可 30 req = urllib2.Request(url) 31 fd = urllib2.urlopen(req) 32 parser = DataParser() 33 parser.feed(fd.read()) 34 print "Title is:",parser.getTitle()
結果是:
第二步 安裝庫
1.我假設你已經安裝了python。
2.在開始解析動態網頁之前,先要安裝pyqt,讓brew去替你安裝,能幫你節省很多精力。。。
了解更多homebrew,請訪問官網:homebrew官網
3.說明:本來pyqt是一個GUI庫,但它包含了網絡模塊webkit,這個將用於解析動態網頁。
第三步 解析javascript動態標簽
1.有很多標簽是動態添加到html網頁中的,所以有時候用python去執行javascript可能不能達到條件,比如動態添加的標簽,所以獲得執行后dom樹是一種比較通用的方法。(可能理解不正確,如果不對,請指正)。
2.來寫一個給上面html文件外部調用的js文件。
1 alert("這是被調用的語句。") 2 var o = document.body; 3 function createDIV(text) 4 { 5 var div = document.createElement("div"); 6 div.innerHTML = text; 7 o.appendChild(div); 8 } 9 createDIV("15");
3.此時,雙擊2.html,看到的效果是:
只有一個15,這就是我們要解析的數據,現在再來看下瀏覽器顯示的源碼:
是不是沒有div標簽,所以現在解析,不可能獲取到的,應為div是5757.js添加上去的(js名字亂取的)~
下面就開始解析,我的問題解決受益於這篇文章,希望大家也能看看:Scraping JavaScript webpages with webkit
我們要利用webkit獲取執行后的dom樹:
1 #! /usr/bin/env python 2 3 import sys,urllib2 4 from HTMLParser import HTMLParser 5 from PyQt4.QtCore import * 6 from PyQt4.QtGui import * 7 from PyQt4.QtWebKit import * 8 9 class Render(QWebPage): 10 def __init__(self, url): 11 self.app = QApplication(sys.argv) 12 QWebPage.__init__(self) 13 self.loadFinished.connect(self._loadFinished) 14 self.mainFrame().load(QUrl(url)) 15 self.app.exec_() 16 17 def _loadFinished(self, result): 18 self.frame = self.mainFrame() 19 self.app.quit() 20 21 url = './2.html' 22 r = Render(url) 23 html = r.frame.toHtml() 24 print html.toUtf8() 25 26 # 將執行后的代碼寫入文件中 27 f = open('./test.txt','w') 28 f.write(html.toUtf8()) 29 f.close()
我顯示print出來結果,后又將結果寫入test.txt文件。現在來看看test.txt中有什么(不要雙擊,否則只有一個15,用你的文本編輯器去查看,比如:sublime text2):
1 <html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 2 3 4 <title>javascript測試網頁</title> 5 </head> 6 <body> 7 <script type="text/javascript" src="./5757.js"> 8 </script><div>15</div> 9 10 </body></html>
看起來像html代碼,但是得到了我想要的東西,注意第八行,出現了div標簽~。
最后一步,獲取那個15。
停一下,想一下我們怎么去獲取:
1 html = r.frame.toHtml()
得到一個QString對象,它不屬於python標准庫。我想在我熟悉pyqt的始末之前,將它轉換成python對象讓我感到更加自在一點。我們可以像解析靜態網頁般區解析它,關鍵在於這一句:
1 parser.feed(fd.read())
當然既然能將它寫入到本地文件,打開文件->解析文件->獲取數據也是可以的,但我想沒人想那么麻煩。
查閱一下python的文檔:
1 HTMLParser.feed(data) 2 3 Feed some text to the parser. It is processed insofar as it consists of complete elements; incomplete data is buffered until more data is fed or close() is called.data can be either unicode or str, but passing unicode is advised.
發現只要將unicode或str傳入,我們就能順利解析,也許稍微改動下代碼即可:
1 ! /usr/bin/env python 2 3 4 import sys,urllib2 5 from HTMLParser import HTMLParser 6 from PyQt4.QtCore import * 7 from PyQt4.QtGui import * 8 from PyQt4.QtWebKit import * 9 10 class DataParser(HTMLParser): 11 def __init__(self): 12 self.div = None 13 self.isTag = 0 14 HTMLParser.__init__(self) 15 16 def handle_starttag(self,tag,attrs): 17 if tag == 'div': 18 self.isTag = 1 19 20 21 def handle_data(self,data): 22 if self.isTag: 23 self.title = data 24 25 def handle_endtag(self,tag): 26 if tag == 'div': 27 self.isTag = 0 28 def getDiv(self): 29 return self.title 30 31 32 class Render(QWebPage): 33 def __init__(self, url): 34 self.app = QApplication(sys.argv) 35 QWebPage.__init__(self) 36 self.loadFinished.connect(self._loadFinished) 37 self.mainFrame().load(QUrl(url)) 38 self.app.exec_() 39 40 def _loadFinished(self, result): 41 self.frame = self.mainFrame() 42 self.app.quit() 43 44 url = './2.html' 45 r = Render(url) 46 html = r.frame.toHtml() 47 #print html.toUtf8() 48 49 parser = DataParser() 50 parser.feed(str(html.toUtf8())) 51 print "javascript is",parser.getDiv() 52 53 54 #f = open('./test.txt','w') 55 #f.write(html.toUtf8()) 56 #f.close()
代碼做了簡單的合並,就將數據解析出來了,運行結果如下:
呵呵,雖然只有3個詞,但的確成功解析了動態標簽,呵呵~
第四步 想說的話
文章的實現多於原理,希望對閱讀文章的人提供一定的幫助。如有不對的地方也請指正。
當然,要將文章的東西直接運用到實際是不現實的,但希望這是一個好的起點。