使用webdriver+urllib爬取網頁數據
urilib是python的標準庫,當我們使用Python爬取網頁數據時,往往用的是urllib模塊,通過調用urllib模塊的urlopen(url)方法返回網頁對象,並使用read()方法獲得url的html內容,然後使用BeautifulSoup抓取某個標簽內容,結合正則表達式過濾。但是,用urllib.urlopen(url).read()獲取的只是網頁的靜態html內容,很多動態數據(比如網站訪問人數、當前在線人數、微博的點贊數等等)是不包含在靜態html裏面的,例如我要抓取這個bbs網站中點擊打開鏈接 各個板塊的當前在線人數,靜態html網頁是不包含的(不信你查看頁面源代碼試試,只有簡單的一行)。像這些動態數據更多的是由JavaScript、JQuery、PHP等語言動態生成的,因此再用抓取靜態html內容的方式就不合適了。
本文將通過selenium webdriver模塊的使用,以獲取這些動態生成的內容,尤其是一些重要的動態數據。其實selenium模塊的功能不是僅僅限於抓取網頁,它是網絡自動化測試的常用模塊,在Ruby、Java裏面都有廣泛使用,Python裏面雖然使用相對較少,但也是一個非常簡潔高效容易上手的自動化測試模塊。通過利用selenium的子模塊webdriver的使用,解決抓取動態數據的問題,還可以可以對selenium有基本認識,為進一步學習自動化測試打下基礎。
一 環境配置
1、下載geckodriver.exe:下載地址:https://github.com/mozilla/geckodriver/releases請根據系統版本選擇下載;(如Windows 64位系統)
2、下載解壓後將getckodriver.exe復制到Firefox的安裝目錄下,
如(C:\Program Files\Mozilla Firefox),並在環境變量Path中添加路徑:C:\Program Files\Mozilla Firefox
3、安裝selenium
pip install selenium
4、beautifulsoup4的安裝,Beautiful Soup 是一個可以從HTML或XML文件中提取數據的Python庫.它能夠通過你喜歡的轉換器實現慣用的文檔導航,
pip install beautifulsoup4 pip install lxml pip install html5lib
5、安裝faker
pip install faker
二 如何爬取落網某一期數據信息
落網的網址是http://www.luoo.net,我們利用火狐瀏覽器打開該網址,隨便選擇一期音樂,這裏我點擊的是搖滾,
然後點擊F12,選擇第989期,進入該頁面,我們就以爬取這一頁的內容為例:
進入之後,我們可以看到該網頁主要包括以下內容:期刊編號,期刊標題,期刊封面,期刊描述,歌單。
通過下方開發工具中的查看器,可以獲取我們感興趣數據的標簽,選擇器等信息。以歌單為例:
程序代碼如下:
# -*- coding: utf-8 -*- """ Created on Thu May 24 16:35:36 2018 @author: zy """ import os from bs4 import BeautifulSoup import random from faker import Factory import queue import threading import urllib.request as urllib from selenium import webdriver import time #"gbk" codec can‘t encode character "\xXX" in position XX : https://www.cnblogs.com/feng18/p/5646925.html #即字符編碼是utf-8的字節,但是並不能轉換成utf-8編碼的字符串 ‘‘‘ 爬取網頁信息的類 ‘‘‘ def random_proxies(proxy_ips): ‘‘‘ 從proxy_ips中隨機選取一個代理ip args: proxy_ips:list 每個元素都是一個代理ip ‘‘‘ ip_index = random.randint(0, len(proxy_ips)-1) res = { ‘http‘: proxy_ips[ip_index] } return res def fix_characters(s): ‘‘‘ 替換掉s中的一些特殊字符 args: s:字符串 ‘‘‘ for c in [‘<‘, ‘>‘, ‘:‘, ‘"‘, ‘/‘, ‘\\‘, ‘|‘, ‘?‘, ‘*‘]: s = s.replace(c, ‘‘) return s def get_static_url_response_html(url): ‘‘‘ 爬取靜態頁面數據:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#find reture: 返回html代碼 ‘‘‘ fake = Factory.create() # 這裏配置可用的代理IP,可以寫多個 proxy_ips = [ ‘183.129.151.130‘ ] headers = {‘Accept‘:‘text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8‘, ‘Accept-Charset‘:‘GB2312,utf-8;q=0.7,*;q=0.7‘, ‘Accept-Language‘:‘zh-cn,zh;q=0.5‘, ‘Cache-Control‘:‘max-age=0‘, ‘Connection‘:‘keep-alive‘, ‘Host‘:‘John‘, ‘Keep-Alive‘:‘115‘, ‘Referer‘:url, ‘User-Agent‘: fake.user_agent() #‘User-Agent‘: ‘User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36‘ } req = urllib.Request(url=url,origin_req_host=random_proxies(proxy_ips),headers = headers) #當該語句讀取的返回值是bytes類型時,要將其轉換成utf-8才能正常顯示在python程序中 response = urllib.urlopen(req).read() #需要進行類型轉換才能正常顯示在python中 response = response.decode(‘utf-8‘) return response def download(url,dstpath=None): ‘‘‘ 利用urlretrieve()這個函數可以直接從互聯網上下載文件保存到本地路徑下 args: url:網頁文件或者圖片以及其他數據路徑 dstpath:保存全路況 ‘‘‘ if dstpath is None: dstpath = ‘./code.jpg‘ try: urllib.urlretrieve(url,dstpath) print(‘Download from {} finish!‘.format(url)) except Exception as e: print(‘Download from {} fail!‘.format(url)) def get_dynamic_url_response_html(url): ‘‘‘ selenium是一個用於Web應用自動化程序測試的工具,測試直接運行在瀏覽器中,就像真正的用戶在操作一樣 selenium2支持通過驅動真實瀏覽器(FirfoxDriver,IternetExplorerDriver,OperaDriver,ChromeDriver) selenium2支持通過驅動無界面瀏覽器(HtmlUnit,PhantomJs) 1、下載geckodriver.exe:下載地址:https://github.com/mozilla/geckodriver/releases請根據系統版本選擇下載;(如Windows 64位系統) 2、下載解壓後將getckodriver.exe復制到Firefox的安裝目錄下, 如(C:\Program Files\Mozilla Firefox),並在環境變量Path中添加路徑:C:\Program Files\Mozilla Firefox return: 返回html代碼 獲取url頁面信息後關閉連接 ‘‘‘ browser = webdriver.Firefox(executable_path = r‘D:\ff\geckodriver.exe‘) #html請求 browser.get(url) html = browser.page_source time.sleep(2) #html = browser.page_source.decode(‘utf-8‘) #關閉瀏覽器 browser.quit() return html class UrlSpyder(threading.Thread): ‘‘‘ 使用Threading模塊創建線程:http://www.runoob.com/python/python-multithreading.html 使用Threading模塊創建線程,直接從threading.Thread繼承,然後重寫__init__方法和run方法: 主要思路分成兩部分: 1.用來發起http請求分析出播放列表然後丟到隊列中 2.在隊列中逐條下載文件到本地(如果需要下載文件) 一般分析列表速度較快,下載速度比較慢,可以借助多線程同時進行下載 ‘‘‘ def __init__(self, base_url, releate_urls, que=None): ‘‘‘ args: base_url:網址 releate_urls:相對於網址的網頁路徑 list集合 que:隊列 Python的queue模塊中提供了同步的、線程安全的隊列類,包括FIFO(先入先出)隊列Queue,LIFO(後入先出)隊列LifoQueue, 和優先級隊列PriorityQueue。這些隊列都實現了鎖原語,能夠在多線程中直接使用。可以使用隊列來實現線程間的同步。 ‘‘‘ threading.Thread.__init__(self) print(‘Start spider\n‘) print (‘=‘ * 50) #保存字段信息 self.base_url = base_url if queue is None: self.queue = queue.Queue() else: self.queue = que self.releate_urls = releate_urls def run(self): ‘‘‘ 把要執行的代碼寫到run函數裏面 線程在創建後會直接運行run函數 ‘‘‘ #遍歷每一個網頁 並開始爬取 for releate_url in self.releate_urls: self.spider(releate_url) print (‘\nCrawl end\n\n‘) def spider(self, releate_url): ‘‘‘ 爬取指定網頁數據,並提取我們所需要的網頁信息的函數 args: releate_url:網頁的相對路徑 ‘‘‘ url = os.path.join(self.base_url,releate_url) print (‘Crawling: ‘ + url + ‘\n‘) ‘‘‘ 解析html 針對不同的網址,解析內容也不一樣 ‘‘‘ #獲取指定網頁的html代碼 response = get_dynamic_url_response_html(url) #response = getUrlRespHtml(url) ) #使用BeautifulSoup解析這段代碼,能夠得到一個 BeautifulSoup 的對象,並能按照標準的縮進格式的結構輸出: #soup = BeautifulSoup(response, ‘lxml‘) soup = BeautifulSoup(response, ‘html.parser‘) #輸出網頁信息 #print(soup.prettify()) #with open(‘./read.html‘,‘w‘,encoding=‘utf-8‘) as f: #兩個內容是一樣的 只是一個是標準縮進格式,另一個不是 #f.write(str(soup.prettify())) #f.write(str(response)) ‘‘‘ 根據爬取網站不同,下面代碼也會不一樣 解析該網站某一期的所有音樂信息 主要包括: 期刊信息 期刊封面 期刊描述 節目清單 ‘‘‘ #<span class="vol-number rounded">989</span> num = soup.find(‘span‘,class_=‘vol-number‘).get_text() #<span class="vol-title">曙色初動</span> 解析標題 find每次只返回第一個元素 title = soup.find(‘span‘,class_=‘vol-title‘).get_text() ‘‘‘ <div class="vol-cover-wrapper" id="volCoverWrapper"> <img src="http://img-cdn2.luoo.net/pics/vol/554f999074484.jpg!/fwfh/640x452" alt="曙色初動" class="vol-cover"> <a href="http://www.luoo.net/vol/index/739" class="nav-prev" title="後一期" style="display: inline; opacity: 0;"> </a><a href="http://www.luoo.net/vol/index/737" class="nav-next" title="前一期" style="display: inline; opacity: 0;"> </a> </div> 解析對應的圖片背景 ‘‘‘ cover = soup.find(‘img‘, class_=‘vol-cover‘).get(‘src‘) ‘‘‘ <div class="vol-desc"> 本期音樂為史詩氛圍類音樂專題。<br><br>史詩音樂的美好之處在於能夠讓人有無限多的宏偉想象。就像這一首首曲子,用歲月滄桑的厚重之聲,鏗鏘有力的撞擊人的內心深處,綻放出人世間的悲歡離合與決絕!<br><br>Cover From Meer Sadi </div> 解析描述文本 ‘‘‘ desc = soup.find(‘div‘, class_=‘vol-desc‘).get_text() ‘‘‘ <a href="javascript:;" rel="nofollow" class="trackname btn-play">01. Victory</a> <a href="javascript:;" rel="nofollow" class="trackname btn-play">02. Mythical Hero</a> 解析歌單 find_all返回一個列表 所有元素 ‘‘‘ track_names = soup.find_all(‘a‘, attrs={‘class‘: ‘trackname‘}) track_count = len(track_names) tracks = [] # 12期前的音樂編號1~9是1位(如:1~9),之後的都是2位 1~9會在左邊墊0(如:01~09) for track in track_names: _id = str(int(track.text[:2])) if (int(releate_url) < 12) else track.text[:2] _name = fix_characters(track.text[4:]) tracks.append({‘id‘: _id, ‘name‘: _name}) phases = { ‘phase‘: num, # 期刊編號 ‘title‘: title, # 期刊標題 ‘cover‘: cover, # 期刊封面 ‘desc‘: desc, # 期刊描述 ‘track_count‘: track_count, # 節目數 ‘tracks‘: tracks # 節目清單(節目編號,節目名稱) } #追加到隊列 self.queue.put(phases) if __name__ == ‘__main__‘: luoo_site = ‘http://www.luoo.net/vol/index/‘ spyder_queue = queue.Queue() ## 創建新線程 luoo = UrlSpyder(luoo_site, releate_urls=[‘1372‘,‘1370‘], que=spyder_queue) luoo.setDaemon(True) #開啟線程 luoo.start() ‘‘‘ 從隊列中取數據,並進行下載 ‘‘‘ count = 1 while True: if spyder_queue.qsize() <= 0: time.sleep(10) pass else: phases = spyder_queue.get() print(phases) #下載圖片 download(phases[‘cover‘],‘%d.jpg‘%(count)) count += 1
運行結果如下:
並爬取了如下兩種圖片:
使用webdriver+urllib爬取網頁數據