日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

爬蟲實(shí)戰(zhàn):爬蟲之 web 自動化終極殺手 ( 上)

 飲茶仙人 2017-09-25


導(dǎo)語:

最近寫了好幾個簡單的爬蟲,踩了好幾個深坑,在這里總結(jié)一下,給大家在編寫爬蟲時候能給點(diǎn)思路。本次爬蟲內(nèi)容有:靜態(tài)頁面的爬取。動態(tài)頁面的爬取。web自動化終極爬蟲。

分析:

數(shù)據(jù)獲取(主要靠爬蟲)

  • 靜態(tài)網(wǎng)頁爬取
  • 動態(tài)網(wǎng)頁爬取

數(shù)據(jù)存儲(python excel存儲)

  • Python Excel操作,保存結(jié)果

數(shù)據(jù)獲取實(shí)戰(zhàn):

百度音樂(靜態(tài)網(wǎng)頁)

分析步驟
1 . 打開百度音樂:http://music.baidu.com/

2 . 打開瀏覽器調(diào)試模式F12,選擇Network+all模式

3 . 搜索框搜索歌曲(beat it),查看控制臺

  • 過濾請求:ctrl + f 輸入搜索關(guān)鍵字
  • 依照請求接口的特點(diǎn),查看主請求
  • 分析請求(reques headers、Query string、response)

4 .通過以上分析:獲取到有效信息:

  • 歌曲搜索請求接口為http://music.baidu.com/search?key=歌曲名
  • 獲取請求方式(post、get)百度音樂搜索歌曲為get請求
  • 請求headers(偽裝瀏覽器,避免被拒絕請求)
  • 請求返回結(jié)果(html or json)百度音樂的返回結(jié)果為html。

5 .通過有效信息來設(shè)計(jì)爬蟲,獲取數(shù)據(jù)

  • 偽裝瀏覽器。需要導(dǎo)入requests庫,比起urllib、urllib2等庫更加方便,這里不做贅述。要點(diǎn)是添加請求頭(User-Agent、Host等)
  • 發(fā)起get請求
  • 等待請求返回
  • 處理返回?cái)?shù)據(jù)。因?yàn)榘俣纫魳凡庞玫氖莌tml作為返回?cái)?shù)據(jù)。因此要祭出我們的BeautifulSoup(SoupBeautiful Soup 是一個可以從HTML或XML文件中提取數(shù)據(jù)的Python庫.它能夠通過你喜歡的轉(zhuǎn)換器實(shí)現(xiàn)慣用的文檔導(dǎo)航,查找,修改文檔的方式.Beautiful Soup會幫你節(jié)省數(shù)小時甚至數(shù)天的工作時間。這可是爬蟲的福音,終于不用再寫那么復(fù)雜的正則表達(dá)式了—-引用,詳細(xì)教程可訪問SoupBeautiful Soup 教程進(jìn)行學(xué)習(xí),本次使用的方法不多,會在后邊介紹。)
  • 將解析到的有用數(shù)據(jù)進(jìn)行保存。

代碼實(shí)現(xiàn)

1 .View 提供準(zhǔn)對參數(shù)url進(jìn)行訪問并返回結(jié)果的方法

def view(url):

     '''
     :param url: 待爬取的url鏈接
     :return:
     '''
     # 從url中獲取host
     protocol, s1 = urllib.splittype(url)
     host, s2 = urllib.splithost(s1)

     # 偽裝瀏覽器,避免被kill
     headers = {
         'Host': host,
         'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36',
         'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
         'Accept-Encoding': 'gzip, deflate',
         'Accept-Language': 'zh-CN,zh;q=0.8',
     }

     # 代理
     proxies = {
         "http": "dev-proxy.oa.com:8080",
         "https": "dev-proxy.oa.com:8080",
     }
     # 利用requests封裝的get方法請求url,并添加請求頭和代理,獲取并返回請求結(jié)果。并進(jìn)行異常處理
     try:
         res = requests.get(url, headers=headers, proxies=proxies)
         res.encoding = 'utf-8'

         if res.status_code == 200:
             # 請求成功
             # 獲取網(wǎng)頁內(nèi)容,并返回
             content = res.text
             return content
         else:
             return None
     except requests.RequestException as e:
         # 異常處理
         print(e)
         return None

2 .search_baidu_song 提供對參數(shù)song_name進(jìn)行歌曲搜索并獲取搜索結(jié)果

def search_baidu_song(song_name):
     '''
     獲取百度音樂搜索歌曲結(jié)果
     :param song_name: 待搜索歌曲名
     :return: 搜索結(jié)果
     '''

     def analyse():
         '''
         靜態(tài)網(wǎng)頁分析,利用BeautifulSoup,輕松獲取想要的數(shù)據(jù)。需要對html有了解。
         :return:
         '''

         # 初始化BeautifulSoup對象,并指定解析器為 lxml。還有其他的解析器:html.parser、html5lib等
         # 詳細(xì)教程可訪問:http://cuiqingcai.com/1319.html《Python爬蟲利器二之Beautiful Soup的用法》
         html = BeautifulSoup(content, "lxml")
         # beautifulsoupzui常用方法之一: find_all( name , attrs , recursive , text , **kwargs )
         # find_all() 方法搜索當(dāng)前tag的所有tag子節(jié)點(diǎn), 并判斷是否符合過濾器的條件
         # tag標(biāo)簽名為'div'的并且標(biāo)簽類名為class_參數(shù)(可為 str、 list、 tuple),
         search_result_divs = html.find_all('div', class_=['song-item clearfix ', 'song-item clearfix yyr-song'])

         for div in search_result_divs:
             # find() 方法搜索當(dāng)前tag的所有tag子節(jié)點(diǎn), 并返回符合過濾器的條件的第一個結(jié)點(diǎn)對象
             song_name_str = div.find('span', class_='song-title')
             singer = div.find('span', class_='singer')
             album = div.find('span', class_='album-title')

             # 此部分需要對html頁面進(jìn)行分析,一層層剝開有用數(shù)據(jù)并提取出來
             if song_name_str:
                 # 獲取結(jié)點(diǎn)對象內(nèi)容,并清洗
                 song_name_str = song_name.text.strip()
             else:
                 song_name_str = ''
             if singer:
                 singer = singer.text.strip()
             else:
                 singer = ''
             if album:
                 album = album.find('a')
                 if album:
                     # 獲取標(biāo)簽屬性值
                     # 方法二:屬性值 = album['屬性名']
                     album = album.attrs.get('title')
                     if album and album != '':
                         album = album.strip()
                 else:
                     album = ''
             else:
                 album = ''
             # print song_name + " | " + singer + " | " + album
             songInfoList.append(SongInfo(song_name_str, singer, album))

     songInfoList = []
     url = urls.get('baidu_song')
     url1 = url.format(song_name=song_name, start_idx=0)
     content = self.view(url1)
     if not content:
         return []
     analyse(content)
     url2 = url.format(song_name=song_name, start_idx=20)
     content = self.view(url2)
     analyse(content)
     return songInfoList[0:30]

就這樣我們獲取到了百度網(wǎng)頁歌曲搜索結(jié)果的數(shù)據(jù)。然后就是保存數(shù)據(jù),這個我們最后再談?wù)劇?/p>

網(wǎng)易云音樂 (動態(tài)網(wǎng)頁)

在我們以上一種靜態(tài)網(wǎng)頁獲取數(shù)據(jù)方式來獲取網(wǎng)易云音樂的數(shù)據(jù)的時候,可能會遇到這樣的問題:網(wǎng)頁查看源代碼并沒有可用的數(shù)據(jù),僅僅只有網(wǎng)頁的骨架。數(shù)據(jù)完全找不到,可是打開開發(fā)者工具查看DOM樹卻能找到想要的數(shù)據(jù),這時候我們是遇到了動態(tài)網(wǎng)頁,數(shù)據(jù)是在動態(tài)加載進(jìn)去的。無法獲取網(wǎng)頁數(shù)據(jù)。

目前解決方案有兩種:

  1. 通過查看訪問動態(tài)數(shù)據(jù)接口來獲取數(shù)據(jù)。
  2. 通過web自動化工具來獲取網(wǎng)頁源代碼以獲取數(shù)據(jù)。
    (目前網(wǎng)易云簡單通過訪問url已經(jīng)不能獲取到數(shù)據(jù)了,我們可以采用web自動化工具selenium和PhantomJS來實(shí)現(xiàn)網(wǎng)頁源代碼的獲?。?/li>

方案一實(shí)現(xiàn)(通過查看訪問動態(tài)數(shù)據(jù)接口來獲取數(shù)據(jù)):

  1. 打開網(wǎng)易云音樂:http://music.163.com/
  2. 打開瀏覽器調(diào)試模式F12,選擇Network+all模式
  3. 搜索框搜索歌曲(beat it),查看控制臺

過濾請求為XHR,發(fā)現(xiàn)請求name怎么都一樣,這時候我們翻看這些name,查看到Request URL里找到關(guān)鍵字search的請求,這個請求是一個POST請求。這個應(yīng)該就是獲取搜索數(shù)據(jù)的接口,通過查看response或者preview來查看請求返回結(jié)果。正是我們想要的。

我們先不要高興的太早了,目前我們還沒有搞清楚Form Data是怎么構(gòu)成的。params + encSecKey到底是怎么生成的。在看過網(wǎng)絡(luò)上有關(guān)抓取網(wǎng)易評論的爬蟲《如何爬網(wǎng)易云音樂的評論數(shù)?》,得知這個網(wǎng)易針對api做了加密處理。由于個人道行太淺參悟不透這里的加密參數(shù)順序和內(nèi)容。因此這個方案就此作罷。實(shí)在不甘心,只好換方案二。

方案二實(shí)現(xiàn):

既然方案一暫時走不通,也不能影響我們的工作進(jìn)度,換個思路繼續(xù)走,想到使用web自動化測試工具selenium可以實(shí)現(xiàn)模擬人工操縱瀏覽器。這樣導(dǎo)出網(wǎng)頁數(shù)據(jù)應(yīng)該不是問題,想到立馬動手。

環(huán)境配置

  1. 安裝selenium
    推薦使用python包管理工具自動: pip install -y selenium
    其他方式可參考:selenium + python自動化測試環(huán)境搭建

2 .安裝PhantomJS

PhantomJS是一個基于webkit的JavaScript API。它使用QtWebKit作為它核心瀏覽器的功能,使用webkit來編譯解釋執(zhí)行JavaScript代碼。任何你可以在基于webkit瀏覽器做的事情,它都能做到。它不僅是個隱形的瀏覽器,提供了諸如CSS選擇器、支持Web標(biāo)準(zhǔn)、DOM操作、JSON、HTML5、Canvas、SVG等,同時也提供了處理文件I/O的操作,從而使你可以向操作系統(tǒng)讀寫文件等。PhantomJS的用處可謂非常廣泛,諸如網(wǎng)絡(luò)監(jiān)測、網(wǎng)頁截屏、無需瀏覽器的 Web 測試、頁面訪問自動化等。

下載PhantomJS

目前官方支持三種操作系統(tǒng),包括windows\Mac OS\Linux這三大主流的環(huán)境。你可以根據(jù)你的運(yùn)行環(huán)境選擇要下載的包

1.安裝PhantomJS

下載完成后解壓文件,可將phantomjs.exe放在pythond的目錄下(C:\Python27\phantomjs.exe)。這樣后續(xù)加載不需要指定目錄。也可以放在特定目錄,使用的時候指定phantomjs.exe路徑即可。雙擊打開phantomjs.exe驗(yàn)證安裝是否成功。如果出現(xiàn)下圖,即安裝成功了。

2.代碼步驟實(shí)現(xiàn):

  • 初始化瀏覽器獲取網(wǎng)頁數(shù)據(jù)
def dynamic_view(url):
     '''
     使用自動化工具獲取網(wǎng)頁數(shù)據(jù)
     :param url: 待獲取網(wǎng)頁url
     :return: 頁面數(shù)據(jù)
     '''

     # 初始化瀏覽器driver
     driver = webdriver.PhantomJS()
     # 瀏覽器driver訪問url
     driver.get(url)
     # 坑:不同frame間的轉(zhuǎn)換(網(wǎng)易云在數(shù)據(jù)展示中會將數(shù)據(jù)動態(tài)添加到'g_iframe'這個框架中,如果不切換,會報(bào)"元素不存在"錯誤。)
     driver.switch_to.frame("g_iframe")
     # 隱式等待5秒,可以自己調(diào)節(jié)
     driver.implicitly_wait(5)
     # 設(shè)置10秒頁面超時返回,類似于requests.get()的timeout選項(xiàng),driver.get()沒有timeout選項(xiàng)
     driver.set_page_load_timeout(10)
     # 獲取網(wǎng)頁資源(獲取到的是網(wǎng)頁所有數(shù)據(jù))
     html = driver.page_source
     # 坑:退出瀏覽器driver,必須手動退出driver。
     driver.quit()
     # 返回網(wǎng)頁資源
     return html

 

  • 解析網(wǎng)頁獲取數(shù)據(jù),同百度音樂(省略)
def search_163_song(song_name):
     pass

同樣是通過BeautifulSoup對網(wǎng)頁資源進(jìn)行對象化,在通過對對象的篩選獲取得到數(shù)據(jù)。沒想到網(wǎng)易云音樂的數(shù)據(jù)也能這樣拿到。能做到這里已經(jīng)可以對付大部分網(wǎng)站了。

選用PhantomJS看中其不需要可視化頁面,在內(nèi)存占用上比較省??墒且彩浅霈F(xiàn)問題,各位看官請繼續(xù)往下看。眼看著就要完成了。

3. spotify

  • 使用搜索功能,需要登錄賬戶(比較不好申請,申請好幾次都沒有成功)
  • 登錄成功后,天不遂愿啊,打開spotify搜索頁面,竟然沒辦法查看網(wǎng)頁原代碼。單獨(dú)通過請求搜索url: https://open./search/recent 也沒辦發(fā)獲取網(wǎng)頁數(shù)據(jù),會報(bào)出權(quán)限問題。后邊依次解決。

解決方案:

  1. 通過使用web自動化獲取數(shù)據(jù)。
  2. 通過請求動態(tài)數(shù)據(jù)接口來獲取數(shù)據(jù)

方案實(shí)施:

方案1:

采用web自動化工具獲取數(shù)據(jù):配置如同網(wǎng)易云配置,模仿用戶操作瀏覽器進(jìn)行網(wǎng)頁打開,用戶登錄,進(jìn)入搜索頁面,獲取頁面數(shù)據(jù)

def spotify_view(url):
     '''
     使用自動化工具獲取網(wǎng)頁數(shù)據(jù)
     :param url: 待獲取網(wǎng)頁url
     :return: 頁面數(shù)據(jù)
     '''
     spotify_name = 'manaxiaomeimei'
     spotify_pass = 'dajiagongyong'
     spotify_login = 'https://accounts./en/login'
     # 初始化瀏覽器driver
     driver = webdriver.PhantomJS()

     # 模擬用戶登錄()
     # 瀏覽器driver訪問登錄url
     driver.get(spotify_login)
     # 休息一下等待網(wǎng)頁加載。(還有另一種方式:driver.implicitly_wait(3))
     time.sleep(3)

     # 獲取頁面元素對象方法(本次使用如下):
     #   find_element_by_id : 通過標(biāo)簽id獲取元素對象 可在頁面中獲取到唯一一個元素,因?yàn)樵趆tml規(guī)范中。一個DOM樹中標(biāo)簽id不能重復(fù)
     #   find_element_by_class_name : 通過標(biāo)簽類名獲取元素對象,可能會重復(fù)(有坑)
     #   find_element_by_xpath : 通過標(biāo)簽xpath獲取元素對象,類同id,可獲取唯一一個元素。
     # 獲取頁面元素對象--用戶名
     username = driver.find_element_by_id('login-username')
     # username.clear()
     # 坑:獲取頁面元素對象--密碼
     #   在通過類名獲取標(biāo)簽元素中,遇到了無法定位復(fù)合樣式,這時候可采用僅選取最后一個使用的樣式作為參數(shù),即可(穩(wěn)定性不好不建議使用。盡量使用by_id)
     # password = driver.find_element_by_class_name('form-control input-with-feedback ng-dirty ng-valid-parse ng-touched ng-empty ng-invalid ng-invalid-required')
     password = driver.find_element_by_class_name('ng-invalid-required')
     # password.clear()
     # 獲取頁面元素對象--登錄按鈕
     login_button = driver.find_element_by_xpath('/html/body/div[2]/div/form/div[3]/div[2]/button')
     # 通過WebDriver API調(diào)用模擬鍵盤的輸入用戶名
     username.send_keys(spotify_name)
     # 通過WebDriver API調(diào)用模擬鍵盤的輸入密碼
     password.send_keys(spotify_pass)
     # 通過WebDriver API調(diào)用模擬鼠標(biāo)的點(diǎn)擊操作,進(jìn)行登錄
     login_button.click()
     # 休息一下等待網(wǎng)頁加載
     driver.implicitly_wait(3)

     # 搜索打開歌曲url
     driver.get(url)
     time.sleep(5)
     # 搜索獲取網(wǎng)頁代碼
     html = driver.page_source
     return html

 

點(diǎn)擊運(yùn)行之后,一切都風(fēng)平浪靜。突然代碼報(bào)錯了(如下圖)。查完資料也做了代碼的修改。

網(wǎng)絡(luò)提供方案

  1. 添加對待輸入元素的clear(),清除原有的字符。
  2. 更換瀏覽器

方案實(shí)施:

方案1:

在獲取了對象之后添加對該對象的清除方法(username.clear()、password.clear())

實(shí)施結(jié)果

方案1失敗。原因不明了,多半是webdriver對PhantomJS兼容性不好。

方案2:

更換瀏覽器,本次選擇使用chrome瀏覽器進(jìn)行自動化操作。

安裝chrome自動化控制插件。

  • 下載ChromeDriver插件。
  • 將WebDriver復(fù)制到python的安裝目錄
  • 雙擊驗(yàn)證安裝
  • 修改代碼:
  • # 初始化瀏覽器driver
    driver = webdriver.Chrome()
  • 再次執(zhí)行代碼
  • 實(shí)施結(jié)果
    成功打開可視化chrome頁面并登錄成功。

本以為這樣就可以獲取到數(shù)據(jù)了。燃鵝,還是沒有獲取到,又報(bào)錯了(如下圖)

到這里:就應(yīng)該查看請求了,找到token是什么。并嘗試添加token到請求頭中。

查看cookies

可是在我們登錄后的cookies列表中卻沒有這個cookie!

預(yù)測這個cookie應(yīng)該是在web播放器加載時種下的。驗(yàn)證一下:

由上表可知。該token在加載播放器的時候種下的。

到這里問題,解決一大半了。


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多