1. 程式人生 > >使用webdriver+urllib爬取網頁數據

使用webdriver+urllib爬取網頁數據

環境 都是 mac net www med har turn 當我

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 = rD:\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;">&nbsp;</a><a href="http://www.luoo.net/vol/index/737" class="nav-next" title="前一期" style="display: inline; opacity: 0;">&nbsp;</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爬取網頁數據