哈嘍大家好,我是鹹魚 之前鹹魚寫過幾篇關於知網爬蟲的文章,後臺反響都很不錯。雖然但是,鹹魚還是忍不住想訴苦一下 有些小伙伴文章甚至代碼看都沒看完,就問我 ”為什麼只能爬這麼多條文獻信息?“(看過代碼的會發現我代碼裡面定義了 papers_need 變數來設置爬取篇數),”為什麼爬其他文獻不行?我想爬 ...
哈嘍大家好,我是鹹魚
之前鹹魚寫過幾篇關於知網爬蟲的文章,後臺反響都很不錯。雖然但是,鹹魚還是忍不住想訴苦一下
有些小伙伴文章甚至代碼看都沒看完,就問我 ”為什麼只能爬這麼多條文獻信息?“(看過代碼的會發現我代碼裡面定義了 papers_need
變數來設置爬取篇數),”為什麼爬其他文獻不行?我想爬 XXX 文獻“(因為代碼裡面寫的是通過【知網高級搜索中的文獻來源】來搜索文章),或者是有些小伙伴直接把代碼報錯貼給我,問我咋回事
我覺得在網上看到別人的代碼,不要一昧地拿來主義,複製粘貼就行了,你要結合你自己的本地環境對代碼做適當地修改。比如定位 Xpath 元素路徑,不通電腦或者說不同瀏覽器同一元素的 Xpath 路徑有可能不是一樣的,這個路徑在我本地運行沒問題,到了你那裡就報錯
當看別人的代碼時,最好先搞清楚:
- 別人是怎麼想的
- 別人為什麼要這麼寫
- 這麼寫的邏輯是什麼?
以我這幾篇知網爬蟲文章舉例:
- 為什麼要用 selenium 來爬取?
- 如何分析網頁?如何定位元素?(Xpath、CSS 選擇器等等)
- 如何通過 selenium 來模擬人為操作瀏覽器(滑鼠移動、點擊、滑動視窗等等)
言歸正傳,鹹魚昨天收到一位粉絲私信說能不能根據【關鍵詞】來搜索文獻
今天這篇文章著重講如何分析網頁結構然後使用 selenium 根據知網的關鍵詞來搜索文獻。至於對搜索到的文獻的爬取,本文不過多介紹,因為以前的文章已經寫過了
需求分析
我們先來看下如果要通過關鍵詞搜索文獻,該怎麼操作?
首先我們登錄網站,點擊【高級搜索】(也可以直接點擊搜索框中的【主題】下拉選擇)
然後我們點擊【主題】——>選擇【關鍵詞】
輸入要搜索的關鍵詞(例如:數字普惠金融)然後點擊【檢索】
網頁分析&元素定位
結合前面的需求分析,我們就可以對網頁進行分析並定位出對應的元素
首先是【高級搜索】,高級搜索有一個鏈接:高級檢索-中國知網 (cnki.net),這樣就能省掉一個步驟了
然後我們需要點擊 【主題】,才會出現下拉框。在分析網頁的時候我發現當出現下拉框時,標簽 <div class="sort-list" style="display: none;">"
中的 style
屬性由 "display: none;"
變成 "display: block;"
下拉框出現之後,我們需要定位到 【關鍵詞】 這個標簽
# 關鍵詞 Xpath 路徑或 CSS 選擇器
//*[@id="gradetxt"]/dd[1]/div[2]/div[1]/div[2]/ul/li[3]
li[data-val="KY"]
接著找到【搜索框】的 Xpath 路徑。這裡是一個 input 元素,用於接收來自用戶的數據
# 輸入框
//*[@id="gradetxt"]/dd[1]/div[2]/input
往輸入框傳入數據之後,我們需要點擊下麵的【檢索】按鈕
# 檢索
/html/body/div[2]/div/div[2]/div/div[1]/div[1]/div[2]/div[2]/input
點擊搜索之後我們把【文獻條數】爬取下來
# 文獻條數
/html/body/div[3]/div[2]/div[2]/div[2]/form/div/div[1]/div[1]/span[1]/em
代碼實現
selenium 是一個自動化測試工具,可以用來進行 web 自動化測試。其本質是通過驅動瀏覽器,完全模擬瀏覽器的操作(比如跳轉、輸入、點擊、下拉等)來實現網頁渲染之後的結果,可支持多種瀏覽器
爬蟲中用到 selenium 主要是為瞭解決 requests 無法直接執行 JavaScript 代碼等問題
導入相關庫
import time
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.common.action_chains import ActionChains
創建瀏覽器對象
這裡我用的是 Edge 瀏覽器
def webserver():
# get直接返回,不再等待界面載入完成
desired_capabilities = DesiredCapabilities.EDGE
desired_capabilities["pageLoadStrategy"] = "none"
# 設置微軟碟機動器的環境
options = webdriver.EdgeOptions()
# 設置瀏覽器不載入圖片,提高載入速度
options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
# 創建一個微軟碟機動器
driver = webdriver.Edge(options=options)
return driver
爬取網頁
其實邏輯並不難,就是先定位到各個元素然後用 selenium 來模擬我們人為點擊瀏覽器的操作就行了
首先打開頁面,等待個一兩秒讓網頁完全載入
driver.get("https://kns.cnki.net/kns8/AdvSearch")
time.sleep(2)
然後然下拉框顯示出來,前面我們提到:標簽 <div class="sort-list" style="display: none;">"
中的 style
屬性由 "display: none;"
變成 "display: block;"
時,就會出現下拉框
這裡我們通過執行 js 腳本來修改裡面的 style
屬性
# 修改屬性,使下拉框顯示
opt = driver.find_element(By.CSS_SELECTOR, 'div.sort-list') # 定位下拉框
# 執行 js 腳本進行屬性的修改; arguments[0]代表第一個屬性
driver.execute_script("arguments[0].setAttribute('style', 'display: block;')", opt)
下拉框顯示出來之後我們需要點擊【關鍵詞】,這樣才會切換到關鍵詞搜索
這裡需要註意的是,當我在測試的時候發現下拉框載入是有問題的,這時候代碼會報錯說Element <li data-val="KY">...</li> is not clickable at point (189, 249)
就會使得程式點擊不了【關鍵詞】
而且我還發現如果載入不完全的話,需要滑鼠移動到下拉框那裡,讓下拉框完全載入。所以這裡我使用了 selenium 中的 ActionChains
來模擬滑鼠的操作
用 selenium 做自動化,有時候會遇到需要模擬滑鼠操作才能進行的情況,比如單擊、雙擊、點擊滑鼠右鍵、拖拽等等
selenium 給我們提供了一個類來處理這類事件——ActionChains
還有一點需要註意的是:如果滑鼠只是移到【關鍵詞】,下拉框其實還是不能正確載入出來,最好是移動到下拉框的最底部或者關鍵詞後面的元素,這裡我移動到【通訊作者】
# 【通訊作者】定位
/html/body/div[2]/div/div[2]/div/div[2]/div[1]/div[1]/div[2]/ul/li[8]
li[data-val="RP"]
下拉框載入完成之後,定位到【關鍵詞】再點擊
# 滑鼠移動到下拉框
ActionChains(driver).move_to_element(driver.find_element(By.CSS_SELECTOR, 'li[data-val="RP"]')).perform()
# 找到[關鍵詞]選項並點擊
WebDriverWait(driver, 100).until(
EC.visibility_of_element_located((By.CSS_SELECTOR, 'li[data-val="KY"]'))).click()
定位出搜索框,傳入我們要搜索的關鍵詞
# 傳入關鍵字
WebDriverWait(driver, 100).until(
EC.presence_of_element_located((By.XPATH, '''//*[@id="gradetxt"]/dd[1]/div[2]/input'''))
).send_keys(keyword)
# 點擊搜索
WebDriverWait(driver, 100).until(
EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div/div[2]/div/div[1]/div[1]/div[2]/div[2]/input"))
).click()
搜索結果出來之後定位【文獻條數】,獲取對應的條數(text 標簽)
# 獲取總文獻數和頁數
res_unm = WebDriverWait(driver, 100).until(EC.presence_of_element_located(
(By.XPATH, "/html/body/div[3]/div[2]/div[2]/div[2]/form/div/div[1]/div[1]/span[1]/em"))
).text
完整代碼如下:
import time
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.common.action_chains import ActionChains
def webserver():
# get直接返回,不再等待界面載入完成
desired_capabilities = DesiredCapabilities.EDGE
desired_capabilities["pageLoadStrategy"] = "none"
# 設置微軟碟機動器的環境
options = webdriver.EdgeOptions()
# 設置瀏覽器不載入圖片,提高速度
options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
# 創建一個微軟碟機動器
driver = webdriver.Edge(options=options)
return driver
def open_page(driver, keyword):
# 打開頁面,等待兩秒
driver.get("https://kns.cnki.net/kns8/AdvSearch")
time.sleep(2)
# 修改屬性,使下拉框顯示
opt = driver.find_element(By.CSS_SELECTOR, 'div.sort-list') # 定位元素
driver.execute_script("arguments[0].setAttribute('style', 'display: block;')", opt) # 執行 js 腳本進行屬性的修改;arguments[0]代表第一個屬性
# 滑鼠移動到下拉框中的[通訊作者]
ActionChains(driver).move_to_element(driver.find_element(By.CSS_SELECTOR, 'li[data-val="RP"]')).perform()
# 找到[關鍵詞]選項並點擊
WebDriverWait(driver, 100).until(
EC.visibility_of_element_located((By.CSS_SELECTOR, 'li[data-val="KY"]'))).click()
# 傳入關鍵字
WebDriverWait(driver, 100).until(
EC.presence_of_element_located((By.XPATH, '''//*[@id="gradetxt"]/dd[1]/div[2]/input'''))
).send_keys(keyword)
# 點擊搜索
WebDriverWait(driver, 100).until(
EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div/div[2]/div/div[1]/div[1]/div[2]/div[2]/input"))
).click()
# 點擊切換中文文獻
WebDriverWait(driver, 100).until(
EC.presence_of_element_located((By.XPATH, "/html/body/div[3]/div[1]/div/div/div/a[1]"))
).click()
# 獲取總文獻數和頁數
res_unm = WebDriverWait(driver, 100).until(EC.presence_of_element_located(
(By.XPATH, "/html/body/div[3]/div[2]/div[2]/div[2]/form/div/div[1]/div[1]/span[1]/em"))
).text
# 去除千分位里的逗號
res_unm = int(res_unm.replace(",", ''))
page_unm = int(res_unm / 20) + 1
print(f"共找到 {res_unm} 條結果, {page_unm} 頁。")
if __name__ == '__main__':
keyword = "數字普惠金融"
driver = webserver()
open_page(driver, keyword)
結果如下: