本文原創發佈於微信公衆號「極客猴」,歡迎關注第一時間獲取更多原創分享python
在使用 Ajax 技術加載數據的網站中, JavaScript 發起的 HTTP 請求一般須要帶上參數,並且參數的值都是通過加密的。若是咱們想利用網站的 REST API 來爬取數據,就必須知道其使用的加密方式。破解過程須要抓包,閱讀並分析網站的 js 代碼。這整個過程可能會花費一天甚至更長的時間。git
問:那麼是否有辦法繞過這機制,直接獲取網站數據? 答:有的。使用 Selenium 庫模擬瀏覽器行爲來抓取網站數據,達到事半功倍的效果。github
本文內容是利用 Selenium 爬取網易雲音樂中的歌曲 《Five Hundred Miles》 的全部評論,而後存儲到 Mongo 數據庫。web
本文中所用到的工具比較多,因此我將其列舉出來。chrome
Selenium 是一個 Web 應用程序自動化測試的工具。它可以模擬瀏覽器進行網頁加載。因此使用其來幫助咱們解決 JavaScript 渲染問題。數據庫
接下來就是安裝 selenium, 使用 pip 安裝是最方便的。瀏覽器
pip install selenium
複製代碼
在爬取數據過程當中, 須要啓動瀏覽器來顯示頁面。所以,電腦中須要一款瀏覽器。這裏推薦使用 Chrome 瀏覽器。推薦使用 59 版本以上的 Chrome,固然能使用最新版本那最好不過,目前最新版本是 68。服務器
Webdriver 是瀏覽器驅動。selenium 經過 Webdriver 來操做瀏覽器。由於咱們使用的瀏覽器是 Chrome,因此須要下載 Chrome 瀏覽器對應的驅動。微信
下載地址:chromedriver.chromium.org/downloads網絡
webdriver 下載解壓完成以後,將其放到 Python 目錄下的 Script 文件夾中。
網易雲音樂的評論數據總數都很大,十幾萬條數據比比皆是,甚至還有上百萬條數據。因此須要將數據存儲到數據庫中,我選用的是 MongoDB。
pymongo 是 Python 操做 MongoDB 的庫。一樣使用 pip 進行安裝。
pip install pymongo
複製代碼
1)使用 Selenium 驅動 Chrome 瀏覽器打開須要爬取的頁面。 2)獲取頁面中 最新評論 標籤後面的評論總數,計算出一共有多少個分頁, 方便統計。利用總評論數除以 20(每一個頁面顯示 20 條評論),而後對結果進行向上取整。 3)爬取第一頁面的評論的數據,而後存儲到數據庫中。 4)利用 Selenium 模擬點擊下一頁按鈕,再繼續爬取該頁面的評論數據,並存儲到數據庫中。 5)一直循環點擊,直到全部分頁的數據都被爬取完成。
咱們要爬取的歌曲是 《Five Hundred Miles》,先找到其 url 地址,而後調用爬取函數。
if __name__ == '__main__':
url = 'http://music.163.com/#/song?id=27759600' # Five Hundred Miles
start_spider(url)
複製代碼
使用 selenium 啓動 Chrome 瀏覽器。
from selenium import webdriver
def start_spider(url):
""" 啓動 Chrome 瀏覽器訪問頁面 """
""" # 從 Chrome 59 版本, 支持 Headless 模式(無界面模式), 即不會彈出瀏覽器 chrome_options = webdriver.ChromeOptions() chrome_options.add_argument('--headless') brower = webdriver.Chrome(chrome_options=chrome_options) """
brower = webdriver.Chrome()
brower.get(url)
# 等待 5 秒, 讓評論數據加載完成
time.sleep(5)
# 頁面嵌套一層 iframe, 必須切換到 iframe, 才能定位的到 iframe 裏面的元素
iframe = brower.find_element_by_class_name('g-iframe')
brower.switch_to.frame(iframe)
# 獲取【最新評論】總數
new_comments = brower.find_elements(By.XPATH, "//h3[@class='u-hd4']")[1]
複製代碼
根據評論總數計算出總分頁數。
# start_spider(url)
max_page = get_max_page(new_comments.text)
def get_max_page(new_comments):
""" 根據評論總數, 計算出總分頁數 """
print('=== ' + new_comments + ' ===')
max_page = new_comments.split('(')[1].split(')')[0]
# 每頁顯示 20 條最新評論
offset = 20
max_page = ceil(int(max_page) / offset)
print('一共有', max_page, '個分頁')
return max_page
複製代碼
接着循環抓取評論數據,首先抓取第 1 頁的評論數據。
# start_spider(url)
current = 1
is_first = True
while current <= max_page:
print('正在爬取第', current, '頁的數據')
if current == 1:
is_first = True
else:
is_first = False
data_list = get_comments(is_first, brower)
def get_comments(is_first, brower):
""" 獲取評論數據 """
items = brower.find_elements(By.XPATH, "//div[@class='cmmts j-flag']/div[@class='itm']")
# 首頁的數據中包含 15 條精彩評論, 20 條最新評論, 只保留最新評論
if is_first:
items = items[15: len(items)]
data_list = []
data = {}
for each in items:
# 用戶 id
userId = each.find_elements_by_xpath("./div[@class='head']/a")[0]
userId = userId.get_attribute('href').split('=')[1]
# 用戶暱稱
nickname = each.find_elements_by_xpath("./div[@class='cntwrap']/div[1]/div[1]/a")[0]
nickname = nickname.text
# 評論內容
content = each.find_elements_by_xpath("./div[@class='cntwrap']/div[1]/div[1]")[0]
content = content.text.split(':')[1] # 中文冒號
# 點贊數
like = each.find_elements_by_xpath("./div[@class='cntwrap']/div[@class='rp']/a[1]")[0]
like = like.text
if like:
like = like.strip().split('(')[1].split(')')[0]
else:
like = '0'
# 頭像地址
avatar = each.find_elements_by_xpath("./div[@class='head']/a/img")[0]
avatar = avatar.get_attribute('src')
data['userId'] = userId
data['nickname'] = nickname
data['content'] = content
data['like'] = like
data['avatar'] = avatar
print(data)
data_list.append(data)
data = {}
return data_list
複製代碼
將第 1 頁評論數據存儲到 Mongo 數據庫中。
# start_spider(url)
save_data_to_mongo(data_list)
def save_data_to_mongo(data_list):
""" 一次性插入 20 條評論。 插入效率高, 下降數據丟失風險 """
collection = db_manager[MONGO_COLLECTION]
try:
if collection.insert_many(data_list):
print('成功插入', len(data_list), '條數據')
except Exception:
print('插入數據出現異常')
複製代碼
模擬點擊「下一頁」按鈕。
# start_spider(url)
time.sleep(1)
go_nextpage(brower)
# 模擬人爲瀏覽
time.sleep(random.randint(8, 12))
current += 1
def go_nextpage(brower):
""" 模擬人爲操做, 點擊【下一頁】 """
next_button = brower.find_elements(By.XPATH, "//div[@class='m-cmmt']/div[3]/div[1]/a")[-1]
if next_button.text == '下一頁':
next_button.click()
複製代碼
最後就一直循環爬取評論。
評論總數大概有 23W 條, 我又在代碼中增長延時操做。因此爬取全部評論大概須要 69 個小時。目前我只跑了 9 個小時,我貼下暫時爬取的結果。
這部份內容跟上述內容聯繫不大, 屬於服務器技術範疇。若是你不感興趣的話,能夠直接跳過。另外,這部份內容是本身的理解。若是有講錯的地方,還請多多指出。
咱們訪問普通網站的整個過程:
咱們訪問使用 Ajax 加載數據的網站的整個過程:
附: 程序源碼 Github 倉庫地址:163MusicCommentsCrawler
本文首發於微信公衆號,原文地址是 爬取《Five Hundred Miles》在網易雲音樂的全部評論。隨時歡迎轉載文章, 轉載請聯繫號主開通白名單,尊重做者的原創。本人微信公衆號「極客猴」,每週分享 Python 原創乾貨。涉及網絡爬蟲、數據分析、web 開發等方向。