使用瀏覽器模擬器獲取動態網站數據

抓取靜態網站的數據,只是根據須要組合出合適的url列表,以後編寫方法spider獲取指定url上的數據就能夠了。但若是網站是動態的,例如在這個站點「http://www.zgyyjgw.com/front/cn/hospitalPrice」,從源代碼中咱們能夠看出,該站點使用的是javascript與css。咱們查詢「胰高血糖素試驗」的價格,首先須要在「省份」中填入對應的省份,在項目名稱中填入「胰高血糖素試驗」,點擊右側的查找,會在下側顯示查詢到的信息。javascript

 

能夠注意到,整個過程,瀏覽器地址欄中一直都是「http://www.zgyyjgw.com/front/cn/hospitalPrice」沒有發生改變,因此像靜態頁面那樣經過修改url的方法來獲取對應的信息在這裏根本就行不通了。css

雖然python中的urllib模塊中有相應的函數來處理這類動態頁面,但過於麻煩,這裏咱們選用一個簡便的方法,使用瀏覽器模擬器。java

在網上下載無界面瀏覽器PhantomJS,利用pip下載模塊selenium(這裏推薦版本2.53.6,而不是最新版本,最新版本的selenium不支持PhantomJS),創建main.py,編寫涉及和使用到的類與方法,代碼以下:python

 1 from selenium import webdriver  2 from selenium.webdriver.support.select import Select  3 from myLog import MyLog  4 import time  5 import xlwt  6 
 7 class Item(object):  8     shengfen = None         #省份
 9     xiangmubianhao = None   #項目編號
10     xiangmumingcheng = None #項目名稱
11     xiangmuneihan = None    #項目內涵
12     chuwaineirong = None    #除外內容
13     danwei = None           #單位
14     jiage = None            #價格
15     shuoming = None         #說明
16     wenhao = None           #文號
17     zhixingriqi = None      #執行日期
18 
19 class Get_medicalprice(object): 20     def __init__(self): 21         self.hospitalPriceurl = 'http://www.zgyyjgw.com/front/cn/hospitalPrice'
22         self.log = MyLog() 23         self.filename = u'醫療服務價格.xls'.encode('GBK') 24         self.namelist = self.getname('name.txt') 25         self.hospitallist = self.gethospitalprice(self.hospitalPriceurl,self.namelist) 26  self.savefiletoxls(self.filename,self.hospitallist) 27     
28     def getname(self,filename): 29         namelist = [] 30         with open(filename,'r') as fp: 31             s = fp.read() 32             for name in s.split(): 33  namelist.append(name) 34         self.log.info('open namelist success , the length of list is %d' % len(namelist)) 35         return namelist 36     
37     def gethospitalprice(self,url,namelist): 38         list_hospitalprice = [] 39         return list_hospitalprice 40                 
41     def savefiletoxls(self,filename,hospitallist): 42         self.log.info('save data to excel') 43         book = xlwt.Workbook(encoding = 'utf8',style_compression=0) 44         sheet = book.add_sheet(u'醫療服務項目收費') 45         sheet.write(0,0,u'省份'.encode('utf8')) 46         sheet.write(0,1,u'項目編號'.encode('utf8')) 47         sheet.write(0,2,u'項目名稱'.encode('utf8')) 48         sheet.write(0,3,u'項目內涵'.encode('utf8')) 49         sheet.write(0,4,u'除外內容'.encode('utf8')) 50         sheet.write(0,5,u'單位'.encode('utf8')) 51         sheet.write(0,6,u'價格'.encode('utf8')) 52         sheet.write(0,7,u'說明'.encode('utf8')) 53         sheet.write(0,8,u'文號'.encode('utf8')) 54         sheet.write(0,9,u'執行日期'.encode('utf8')) 55         for i in range(1,len(hospitallist)+1): 56             item = hospitallist[i-1] 57  sheet.write(i,0,item.shengfen) 58             sheet.write(i,1,item.xiangmubianhao) 59             sheet.write(i,2,item.xiangmumingcheng) 60             sheet.write(i,3,item.xiangmuneihan) 61             sheet.write(i,4,item.chuwaineirong) 62             sheet.write(i,5,item.danwei) 63             sheet.write(i,6,item.jiage) 64             sheet.write(i,7,item.shuoming) 65             sheet.write(i,8,item.wenhao) 66             sheet.write(i,9,item.zhixingriqi) 67  book.save(filename) 68         self.log.info('save excel success') 69         
70     
71 if __name__ == '__main__': 72     Get_medicalprice()

其中,類Item定義了咱們須要從網頁獲取到的全部信息;類Get_medicalprice爲爬蟲主程序;方法__init__定義了整個爬蟲的工做流程;方法getname從「name.txt」中獲取須要查找的醫療服務項目名稱,返回名稱列表;方法gethospitalprice爲爬蟲的關鍵,負責根據getname返回的名稱列表在頁面中查找對應的信息並保存;方法savefiletoxls負責將gethospitalprice獲取到的全部信息保存到電子錶中。web

咱們如今開始來補充方法gethospitalprice中的代碼。windows

使用selenium模擬調用瀏覽器PhantomJS,首先用「#」註釋掉__init__中的「self.savefiletoxls(self.filename,self.hospitallist)」,出於測試目的,這裏只讓瀏覽器截圖查看模擬效果而不進一步抓取數據。在gethospitalprice中增長代碼以下:瀏覽器

 1 def gethospitalprice(self,url,namelist):  2     browser = webdriver.PhantomJS()  3     list_hospitalprice = []  4  browser.get(url)  5     #browser.implicitly_wait(10)
 6     for name in namelist:  7         textelement = browser.find_element_by_id('projectname')  8         textelement.clear()                 #清除text中已輸入的項目
 9         try: 10             textelement.send_keys(name.decode('GBK'))  #text中填入項目名稱
11         except: 12             self.log.error('get data %s error ' % name) 13             continue
14         else: 15             self.log.info('get data %s \n' % name.decode('GBK')) 16         selectelement = browser.find_element_by_id('provName') 17         Select(selectelement).select_by_value(u'河南省')  #使用select控件選擇河南省
18         submitelement = browser.find_element_by_class_name('l-btn-left') 19         submitelement.click() #點擊查詢按鈕
20         time.sleep(10) 21         browser.get_screenshot_as_file('%s.png' % name) #進行查詢後讓瀏覽器截圖,以查看程序是否運行正常

在這裏,模塊selenium自己自帶的implicitly_wait效果比time裏的sleep要好,因此平時儘可能優先使用implicitly_wait,而這裏依舊使用time.sleep是爲防止被服務器攔截而強制讓程序降速。根據頁面源代碼,找到相應控件的id,如這裏的「projectname」、「provName」,利用find_element_by_id來定位。其中用於選擇省份的控件是個select,須要用到selenium.webdriver.support.select中的Select。經過send_keys來向「provName」的文本框控件中發送項目名稱時,特別須要注意的是漢字編碼,由於name.txt是在windows下建立的,編碼用的是"GBK'',因此這裏須要先用decode("GBK")把項目名稱進行反編碼。服務器

但有個問題是「查找」按鈕很差定位,沒有明顯的id和class。右鍵點擊「查找」按鈕,選擇「審查元素」,這裏能夠看到該控件的class爲「l-btn-left」(雖然那個「清空」按鈕的class也是「l-btn-left」,但並沒太大的影響,「查找」比「清空」位置靠前,find_element_by_class_name返回的是檢索到的第一個符合條件的控件),利用find_element_by_class_name('l-btn-left')來定位。app

在最後增長一條語句 browser.get_screenshot_as_file('%s.png' % name) 來讓瀏覽器自動截圖並保存成「項目名稱.png」的圖片,以便方便查看肯定程序是否在正常運行。ide

點擊run運行程序,在目錄下隨便打開一張png圖片,以下圖:

 

爬蟲程序已經成功選擇了設定的「省份」,填入了讀取到的項目名稱,併成功點擊了「查找」按鈕,並且成功地獲取到了咱們須要的信息。

好了,下面咱們就開始和之前同樣抓取頁面上的信息。

 

查看頁面源代碼,能夠看到存放數據的位置在標籤tbody下,但tbody不惟一,而上層的標籤table,class值爲「xxbTable」,經過搜索後發現是惟一的。因此這裏咱們先定位class值爲「xxbTable」的標籤table,隨後再依次定位tbody,tr,td。

 1 resultelement = browser.find_element_by_class_name('xxbTable')  2 #print resultelement.text
 3 elements=resultelement.find_elements_by_xpath('./tbody[2]/tr/td')  4 item = Item()  5 if len(elements)==0:  6     self.log.info('%s has no data' % name.decode('GBK'))  7 else:  8     self.log.info('save data %s to list' % name.decode('GBK'))  9     item.shengfen = elements[1].text 10     item.xiangmubianhao = elements[2].text 11     item.xiangmumingcheng = elements[3].text 12     item.xiangmuneihan = elements[4].text 13     item.chuwaineirong = elements[5].text 14     item.danwei = elements[6].text 15     item.jiage = elements[7].text 16     item.shuoming = elements[8].text 17     item.wenhao = elements[9].text 18     item.zhixingriqi = elements[10].text 19     list_hospitalprice.append(item)

tbody不是惟一的,因此在肯定需求的是哪一部分的時候,能夠先用定位到tbody處,使用for循環打印下此處的文本,代碼以下:

1 resultelement = browser.find_element_by_class_name('xxbTable') 2 #print resultelement.text
3 elements=resultelement.find_elements_by_xpath('./tbody') 4 for i in elements: 5     print i.text

運行結果以下:

 

tbody[0]爲空,tbody[1]是列名,tbody[2]纔是咱們須要的數據。

最終運行後,生成的結果被方法savefiletoxls保存到電子錶'醫療服務價格.xls'中

 

 如下爲「main.py」文件的完整代碼:

 1 from selenium import webdriver  2 from selenium.webdriver.support.select import Select  3 from myLog import MyLog  4 import time  5 import xlwt  6 
 7 class Item(object):  8     shengfen = None  9     xiangmubianhao = None  10     xiangmumingcheng = None  11     xiangmuneihan = None  12     chuwaineirong = None  13     danwei = None  14     jiage = None  15     shuoming = None  16     wenhao = None  17     zhixingriqi = None  18 
 19 class Get_medicalprice(object):  20     def __init__(self):  21         self.hospitalPriceurl = 'http://www.zgyyjgw.com/front/cn/hospitalPrice'
 22         self.log = MyLog()  23         self.filename = u'醫療服務價格.xls'.encode('GBK')  24         self.namelist = self.getname('name.txt')  25         self.hospitallist = self.gethospitalprice(self.hospitalPriceurl,self.namelist)  26  self.savefiletoxls(self.filename,self.hospitallist)  27     
 28     def getname(self,filename):  29         namelist = []  30         with open(filename,'r') as fp:  31             s = fp.read()  32             for name in s.split():  33  namelist.append(name)  34         self.log.info('open namelist success , the length of list is %d' % len(namelist))  35         return namelist  36     
 37     def gethospitalprice(self,url,namelist):  38         browser = webdriver.PhantomJS()  39         list_hospitalprice = []  40         n = 1
 41         self.log.info('open the link %s' % url)  42  browser.get(url)  43         #browser.implicitly_wait(10)
 44         for name in namelist:  45             textelement = browser.find_element_by_id('projectname')  46  textelement.clear()  47             try:  48                 textelement.send_keys(name.decode('GBK'))  #text中填入項目名稱
 49             except:  50                 self.log.error('get data %s error (%d)' % (name,n))  51                 n += 1
 52                 continue
 53             else:  54                 self.log.info('get data %s (%d)\n' % (name.decode('GBK'),n))  55                 n += 1
 56             selectelement = browser.find_element_by_id('provName')  57             Select(selectelement).select_by_value(u'河南省')  #省份select控件選擇河南省
 58             submitelement = browser.find_element_by_class_name('l-btn-left')  59             submitelement.click() #點擊查詢按鈕
 60             time.sleep(10)  61             #print browser.page_source
 62             #browser.get_screenshot_as_file('test.png')
 63             resultelement = browser.find_element_by_class_name('xxbTable')  64             #print resultelement.text
 65             elements=resultelement.find_elements_by_xpath('./tbody[2]/tr/td')  66             item = Item()  67             if len(elements)==0:  68                 self.log.info('%s has no data' % name.decode('GBK'))  69             else:  70                 self.log.info('save data %s to list' % name.decode('GBK'))  71                 item.shengfen = elements[1].text  72                 item.xiangmubianhao = elements[2].text  73                 item.xiangmumingcheng = elements[3].text  74                 item.xiangmuneihan = elements[4].text  75                 item.chuwaineirong = elements[5].text  76                 item.danwei = elements[6].text  77                 item.jiage = elements[7].text  78                 item.shuoming = elements[8].text  79                 item.wenhao = elements[9].text  80                 item.zhixingriqi = elements[10].text  81  list_hospitalprice.append(item)  82         return list_hospitalprice  83                 
 84     def savefiletoxls(self,filename,hospitallist):  85         self.log.info('save data to excel')  86         book = xlwt.Workbook(encoding = 'utf8',style_compression=0)  87         sheet = book.add_sheet(u'醫療服務項目收費')  88         sheet.write(0,0,u'省份'.encode('utf8'))  89         sheet.write(0,1,u'項目編號'.encode('utf8'))  90         sheet.write(0,2,u'項目名稱'.encode('utf8'))  91         sheet.write(0,3,u'項目內涵'.encode('utf8'))  92         sheet.write(0,4,u'除外內容'.encode('utf8'))  93         sheet.write(0,5,u'單位'.encode('utf8'))  94         sheet.write(0,6,u'價格'.encode('utf8'))  95         sheet.write(0,7,u'說明'.encode('utf8'))  96         sheet.write(0,8,u'文號'.encode('utf8'))  97         sheet.write(0,9,u'執行日期'.encode('utf8'))  98         for i in range(1,len(hospitallist)+1):  99             item = hospitallist[i-1] 100  sheet.write(i,0,item.shengfen) 101             sheet.write(i,1,item.xiangmubianhao) 102             sheet.write(i,2,item.xiangmumingcheng) 103             sheet.write(i,3,item.xiangmuneihan) 104             sheet.write(i,4,item.chuwaineirong) 105             sheet.write(i,5,item.danwei) 106             sheet.write(i,6,item.jiage) 107             sheet.write(i,7,item.shuoming) 108             sheet.write(i,8,item.wenhao) 109             sheet.write(i,9,item.zhixingriqi) 110  book.save(filename) 111         self.log.info('save excel success') 112         
113     
114 if __name__ == '__main__': 115     Get_medicalprice()
View Code
相關文章
相關標籤/搜索