大家好,今天我們來講點
Selenium自動化,你是否有特别喜歡的公衆号?你有思考過如何将一個公衆号曆史文章全部文章爬下來學習嗎?現在我們以早起Python為例,使用Selenium來實作

下面就來詳細講解如何一步步操作,文末附完整代碼。
Selenium介紹
Selenium是一個用于web應用程式
自動化測試的工具,直接運作在浏覽器當中,可以通過代碼控制與頁面上元素進行互動,并擷取對應的資訊。Selenium很大的一個優點是:
不需要複雜地構造請求,通路參數跟使用浏覽器的正常使用者一模一樣,通路行為也相對更像正常使用者,不容易被反爬蟲政策命中,所見即所得。Selenium常常是面對一個奇怪反爬網站無從入手的最後一道防線。當然也有缺點:操作均需要等待頁面加載完畢後才可以繼續進行,是以速度要慢,效率不高。
需求分析和代碼實作
需求很明确:擷取早起Python公衆号全部推文的
标題、
日期、
連結。如果要擷取公衆号的相關資訊,有一個很好途徑是通過
搜狗微信檢索。但如果直接使用Requests等庫直接請求,會涉及的反爬措施有cookie設定,js加密等等,是以今天就利用Selenium大法!
首先導入所需的庫和執行個體化浏覽器對象
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
# 導入第2-4行是為了馬上會提到的 顯式等待
import time
import datetime
driver = webdriver.Chrome()
driver.get('https://weixin.sogou.com/')
上述的代碼就可以實作
打開搜狗微信搜尋的操作,接下來需要往搜尋框裡輸入文字,并且點選“搜文章”(不直接點搜公衆号是因為已經取消通過公衆号直接擷取相應文章的功能)
wait = WebDriverWait(driver, 10)
input = wait.until(EC.presence_of_element_located((By.NAME, 'query')))
input.send_keys('早起Python')
driver.find_element_by_xpath("//input[@class='swz']").click()
邏輯是設定
最長等待時間,在10s内發現了輸入框已經加載出來後就輸入“早起Python”,并且根據“搜文章”按鈕的xpath擷取該位置并點選,這裡就用到了
顯式等待。Selenium請求網頁等待響應受到網速牽制,如果元素未加載全而代碼執行過快就會意外報錯而終止,解決方式是
等待。
隐式等待是在嘗試發現某個元素的時候,如果沒能立刻發現,就等待固定長度的時間
driver.implicitly_wait(10)
,顯示等待明确了等待條件,隻有該條件觸發,才執行後續代碼,如這裡我用到的代碼,當然也可以用time子產品之間設定睡眠時間,睡完了再運作後續代碼。跳轉了下一頁後可以發現不是所有的文章都由“早起Python”公衆号推送。
另外隻能擷取前10頁100條的結果,中間需要微信掃碼登入
是以從這裡開始,代碼的
執行邏輯為:
- 先周遊前10頁100個文章的公衆号名字,如果不是“早起Python”則跳過,是則擷取對應的标題名字、釋出日期和連結
- 第10頁周遊完成後自動點選登入,此時人為掃碼确定登入
- 代碼檢測登入是否完成(可以簡化為識别“下一頁”按鈕是否出現),如果登入完成則繼續從11頁周遊到最後一頁(沒有“下一頁”按鈕)
由于涉及兩次周遊則可以将解析資訊包裝成函數
num = 0
def get_news():
global num # 放全局變量是為了給符合條件的文章記序
time.sleep(1)
news_lst = driver.find_elements_by_xpath("//li[contains(@id,'sogou_vr_11002601_box')]")
for news in news_lst:
# 擷取公衆号來源
source = news.find_elements_by_xpath('div[2]/div/a')[0].text
if '早起' not in source:
continue
num += 1
# 擷取文章标題
title = news.find_elements_by_xpath('div[2]/h3/a')[0].text
# 擷取文章發表日期
date = news.find_elements_by_xpath('div[2]/div/span')[0].text
# 文章發表的日期如果較近可能會顯示“1天前” “12小時前” “30分鐘前”
# 這裡可以用`datetime`子產品根據時間差求出具體時間
# 然後解析為`YYYY-MM-DD`格式
if '前' in date:
today = datetime.datetime.today()
if '天' in date:
delta = datetime.timedelta(days=int(date[0]))
elif '小時' in date:
delta = datetime.timedelta(hours=int(date.replace('小時前', ' ')))
else:
delta = datetime.timedelta(minutes=int(date.replace('分鐘前', ' ')))
date = str((today - delta).strftime('%Y-%m-%d'))
date = datetime.datetime.strptime(date, '%Y-%m-%d').strftime('%Y-%m-%d')
# 擷取url
url = news.find_elements_by_xpath('div[2]/h3/a')[0].get_attribute('href')
print(num, title, date)
print(url)
print('-' * 10)
for i in range(10):
get_news()
if i == 9:
# 如果周遊到第十頁則跳出循環不需要點選“下一頁”
break
driver.find_element_by_id("sogou_next").click()
接下來就是點選“登入”,然後人為掃碼登入,可以利用
while True
檢測登入是否成功,是否出現了下一頁按鈕,如果出現則跳出循環,點選“下一頁”按鈕并繼續後面的代碼,否則睡3秒後重複檢測
driver.find_element_by_name('top_login').click()
while True:
try:
next_page = driver.find_element_by_id("sogou_next")
break
except:
time.sleep(3)
next_page.click()
效果如圖:
然後就是重新周遊文章了,由于不知道最後一頁是第幾頁可以使用
while循環
反複調用解析頁面的函數半點選“下一頁”,如果不存在下一頁則結束循環
while True:
get_news()
try:
driver.find_element_by_id("sogou_next").click()
except:
break
# 最後退出浏覽器即可
driver.quit()
是不是少了點什麼?對,就是資料存儲,在爬下來資料之後和之前一樣利用
openpyxl
存儲到excel中即可
現在我們就有了該公衆号呢的全部文章标題和URL,就可以使用Pdfkit将每一個URL轉成PDF格式,本文就不再展開叙述。
注1:Selenium浏覽器自動化需要依賴ChromeDriver,詳細的配置請自行查詢注2:本文暫時不考慮火狐浏覽器,配置和代碼和Chrome浏覽器略有不同