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


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

 

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

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

在網上下載無界面瀏覽器PhantomJS,利用pip下載模塊selenium(這里推薦版本2.53.6,而不是最新版本,最新版本的selenium不支持PhantomJS),建立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         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獲取到的所有信息保存到電子表中。

我們現在開始來補充方法gethospitalprice中的代碼。

使用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')來定位。

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

點擊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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM