抓取靜態網站的數據,只是根據需要組合出合適的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()