導語:最近寫了好幾個簡單的爬蟲,踩了好幾個深坑,在這里總結(jié)一下,給大家在編寫爬蟲時候能給點思路。本次爬蟲內(nèi)容有:靜態(tài)頁面的爬取。動態(tài)頁面的爬取。web自動化終極爬蟲。 分析:數(shù)據(jù)獲?。ㄖ饕颗老x)
數(shù)據(jù)存儲(python excel存儲)
數(shù)據(jù)獲取實戰(zhàn):百度音樂(靜態(tài)網(wǎng)頁)分析步驟 2 . 打開瀏覽器調(diào)試模式F12,選擇Network+all模式
3 . 搜索框搜索歌曲(beat it),查看控制臺
4 .通過以上分析:獲取到有效信息:
5 .通過有效信息來設計爬蟲,獲取數(shù)據(jù)
代碼實現(xiàn)1 .View 提供準對參數(shù)url進行訪問并返回結(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é)果。并進行異常處理
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進行歌曲搜索并獲取搜索結(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等
# 詳細教程可訪問:http://cuiqingcai.com/1319.html《Python爬蟲利器二之Beautiful Soup的用法》
html = BeautifulSoup(content, "lxml")
# beautifulsoupzui常用方法之一: find_all( name , attrs , recursive , text , **kwargs )
# find_all() 方法搜索當前tag的所有tag子節(jié)點, 并判斷是否符合過濾器的條件
# tag標簽名為'div'的并且標簽類名為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() 方法搜索當前tag的所有tag子節(jié)點, 并返回符合過濾器的條件的第一個結(jié)點對象
song_name_str = div.find('span', class_='song-title')
singer = div.find('span', class_='singer')
album = div.find('span', class_='album-title')
# 此部分需要對html頁面進行分析,一層層剝開有用數(shù)據(jù)并提取出來
if song_name_str:
# 獲取結(jié)點對象內(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:
# 獲取標簽屬性值
# 方法二:屬性值 = 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ǎ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)加載進去的。無法獲取網(wǎng)頁數(shù)據(jù)。
方案一實現(xiàn)(通過查看訪問動態(tài)數(shù)據(jù)接口來獲取數(shù)據(jù)):
過濾請求為XHR,發(fā)現(xiàn)請求name怎么都一樣,這時候我們翻看這些name,查看到Request URL里找到關(guān)鍵字search的請求,這個請求是一個POST請求。這個應該就是獲取搜索數(shù)據(jù)的接口,通過查看response或者preview來查看請求返回結(jié)果。正是我們想要的。 我們先不要高興的太早了,目前我們還沒有搞清楚Form Data是怎么構(gòu)成的。params + encSecKey到底是怎么生成的。在看過網(wǎng)絡上有關(guān)抓取網(wǎng)易評論的爬蟲《如何爬網(wǎng)易云音樂的評論數(shù)?》,得知這個網(wǎng)易針對api做了加密處理。由于個人道行太淺參悟不透這里的加密參數(shù)順序和內(nèi)容。因此這個方案就此作罷。實在不甘心,只好換方案二。 方案二實現(xiàn):既然方案一暫時走不通,也不能影響我們的工作進度,換個思路繼續(xù)走,想到使用web自動化測試工具selenium可以實現(xiàn)模擬人工操縱瀏覽器。這樣導出網(wǎng)頁數(shù)據(jù)應該不是問題,想到立馬動手。 環(huán)境配置
2 .安裝PhantomJS
1.安裝PhantomJS 下載完成后解壓文件,可將phantomjs.exe放在pythond的目錄下(C:\Python27\phantomjs.exe)。這樣后續(xù)加載不需要指定目錄。也可以放在特定目錄,使用的時候指定phantomjs.exe路徑即可。雙擊打開phantomjs.exe驗證安裝是否成功。如果出現(xiàn)下圖,即安裝成功了。
2.代碼步驟實現(xiàn):
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'這個框架中,如果不切換,會報"元素不存在"錯誤。)
driver.switch_to.frame("g_iframe")
# 隱式等待5秒,可以自己調(diào)節(jié)
driver.implicitly_wait(5)
# 設置10秒頁面超時返回,類似于requests.get()的timeout選項,driver.get()沒有timeout選項
driver.set_page_load_timeout(10)
# 獲取網(wǎng)頁資源(獲取到的是網(wǎng)頁所有數(shù)據(jù))
html = driver.page_source
# 坑:退出瀏覽器driver,必須手動退出driver。
driver.quit()
# 返回網(wǎng)頁資源
return html
def search_163_song(song_name):
pass
同樣是通過BeautifulSoup對網(wǎng)頁資源進行對象化,在通過對對象的篩選獲取得到數(shù)據(jù)。沒想到網(wǎng)易云音樂的數(shù)據(jù)也能這樣拿到。能做到這里已經(jīng)可以對付大部分網(wǎng)站了。 選用PhantomJS看中其不需要可視化頁面,在內(nèi)存占用上比較省??墒且彩浅霈F(xiàn)問題,各位看官請繼續(xù)往下看。眼看著就要完成了。 3. spotify
解決方案:
方案實施:方案1: 采用web自動化工具獲取數(shù)據(jù):配置如同網(wǎng)易云配置,模仿用戶操作瀏覽器進行網(wǎng)頁打開,用戶登錄,進入搜索頁面,獲取頁面數(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 : 通過標簽id獲取元素對象 可在頁面中獲取到唯一一個元素,因為在html規(guī)范中。一個DOM樹中標簽id不能重復
# find_element_by_class_name : 通過標簽類名獲取元素對象,可能會重復(有坑)
# find_element_by_xpath : 通過標簽xpath獲取元素對象,類同id,可獲取唯一一個元素。
# 獲取頁面元素對象--用戶名
username = driver.find_element_by_id('login-username')
# username.clear()
# 坑:獲取頁面元素對象--密碼
# 在通過類名獲取標簽元素中,遇到了無法定位復合樣式,這時候可采用僅選取最后一個使用的樣式作為參數(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)用模擬鼠標的點擊操作,進行登錄
login_button.click()
# 休息一下等待網(wǎng)頁加載
driver.implicitly_wait(3)
# 搜索打開歌曲url
driver.get(url)
time.sleep(5)
# 搜索獲取網(wǎng)頁代碼
html = driver.page_source
return html
點擊運行之后,一切都風平浪靜。突然代碼報錯了(如下圖)。查完資料也做了代碼的修改。
網(wǎng)絡提供方案
方案實施: 方案1: 在獲取了對象之后添加對該對象的清除方法(username.clear()、password.clear()) 實施結(jié)果 方案1失敗。原因不明了,多半是webdriver對PhantomJS兼容性不好。 方案2: 更換瀏覽器,本次選擇使用chrome瀏覽器進行自動化操作。 安裝chrome自動化控制插件。
本以為這樣就可以獲取到數(shù)據(jù)了。燃鵝,還是沒有獲取到,又報錯了(如下圖)
到這里:就應該查看請求了,找到token是什么。并嘗試添加token到請求頭中。
查看cookies
可是在我們登錄后的cookies列表中卻沒有這個cookie!
預測這個cookie應該是在web播放器加載時種下的。驗證一下:
由上表可知。該token在加載播放器的時候種下的。 到這里問題,解決一大半了。 |
|
|