最近臨近畢業,寫畢業論文需要從知網查找大量的文獻。但去知網一條一條進去看摘要又略顯麻煩和浪費時間。於是,反手寫一個爬蟲,批量獲取基本信息,豈不美哉?
在開始這個項目之前,我抱著不重複造輪子的心態,尋思著去Github先找找。結果發現基本上都是幾年前的項目,現在早已不能使用。最後證實了,靠別人不如靠自己,擼起袖子就開幹!
1. 爬蟲基礎網絡爬蟲就是模擬瀏覽器發送網絡請求,接收請求響應,一種按照一定的規則,自動地抓取網際網路信息的程序。不同瀏覽器的初始化:
from selenium import webdriver
browser = webdriver.Chrome()
browser = webdriver.Firefox()
browser = webdriver.Edge()
browser = webdriver.Safari()
browser.get('https://www.bing.com')
find_element_by_id()
find_element_by_name()
find_element_by_class_name()
find_element_by_tag_name()
find_element_by_link_text()
find_element_by_partial_link_text()
find_element_by_xpath()
find_element_by_css_selector()
在element變成elements就是找所有滿足的條件,返回數組。
另外,我經常使用的查找元素方法為selenium中selenium.webdriver.common.by的By, 聯合隱士等待EC
用法如下:
# 單個元素
WebDriverWait( driver, 10 ).until( EC.presence_of_element_located((By.XPATH ,"") ) )
# 多個元素
WebDriverWait( driver, 10 ).until( EC.presence_of_all_elements_located( (By.CLASS_NAME ,"fz14") ) )
# 元素類型有:
CLASS_NAME = 'class name'
CSS_SELECTOR = 'css selector'
ID = 'id'
LINK_TEXT = 'link text'
NAME = 'name'
PARTIAL_LINK_TEXT = 'partial link text'
TAG_NAME = 'tag name'
XPATH = 'xpath'
element = find_element_by_id(''id)
element.send_keys('Hello') # 傳入Hello
element.clear() # 清除輸入框
element.click() # 點擊元素
element.text # 獲取元素文本信息
element.get_attribute('href') # 獲取元素屬性
input_xpath = '/html[1]/body[1]/div[1]/div[2]/div[1]/div[1]/input[1]'
button_xpath = '/html[1]/body[1]/div[1]/div[2]/div[1]/div[1]/input[2]'
/html[1]/body[1]/div[5]/div[2]/div[2]/div[2]/form[1]/div[1]/table[1]/tbody[1]/tr[1]/td[2]
即倒數第二個標籤數字代表本頁的第幾個條目,最後一個標籤 2 - 6 分別代表題目、作者、來源、發表時間和資料庫。
在當前頁面無法或者文獻的摘要信息,下載連結等等,需要進一步點擊進入相關文獻條目。
進入詳情頁面後,我們根據class name:abstract-text 能夠很容易定位到摘要的文本,class name: btn-dlcaj 定位到下載連結,其他元素同理。
完成以上知網頁面的分析後,我們就可以根據需求開始寫代碼了!
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 urllib.parse import urljoin
#get直接返回,不再等待界面加載完成
desired_capabilities = DesiredCapabilities.CHROME
desired_capabilities["pageLoadStrategy"] = "none"
# 設置谷歌驅動器的環境
options = webdriver.ChromeOptions()
# 設置chrome不加載圖片,提高速度
options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
# 設置不顯示窗口
#options.add_argument('--headless')
# 創建一個谷歌驅動器
driver = webdriver.Chrome(options=options)
# 設置搜索主題
theme = "Python"
# 設置所需篇數
papers_need = 100
# 打開頁面
driver.get("https://www.cnki.net")
# 傳入關鍵字
WebDriverWait( driver, 100 ).until( EC.presence_of_element_located( (By.XPATH ,'''//*[@id="txt_SearchText"]''') ) ).send_keys(theme)
# 點擊搜索
WebDriverWait( driver, 100 ).until( EC.presence_of_element_located( (By.XPATH ,"/html/body/div[1]/div[2]/div/div[1]/input[2]") ) ).click()
time.sleep(3)
# 點擊切換中文文獻
WebDriverWait( driver, 100 ).until( EC.presence_of_element_located( (By.XPATH ,"/html/body/div[5]/div[1]/div/div/div/a[1]") ) ).click()
time.sleep(1)
# 獲取總文獻數和頁數
res_unm = WebDriverWait( driver, 100 ).until( EC.presence_of_element_located( (By.XPATH ,"/html/body/div[5]/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} 頁。")
# 賦值序號, 控制爬取的文章數量
count = 1
# 當,爬取數量小於需求時,循環網頁頁碼
while count <= papers_need:
# 等待加載完全,休眠3S
time.sleep(3)
title_list = WebDriverWait( driver, 10 ).until( EC.presence_of_all_elements_located( (By.CLASS_NAME ,"fz14") ) )
# 循環網頁一頁中的條目
for i in range(len(title_list)):
try:
term = count%20 # 本頁的第幾個條目
title_xpath = f"/html[1]/body[1]/div[5]/div[2]/div[2]/div[2]/form[1]/div[1]/table[1]/tbody[1]/tr[{term}]/td[2]"
author_xpath = f"/html[1]/body[1]/div[5]/div[2]/div[2]/div[2]/form[1]/div[1]/table[1]/tbody[1]/tr[{term}]/td[3]"
source_xpath = f"/html[1]/body[1]/div[5]/div[2]/div[2]/div[2]/form[1]/div[1]/table[1]/tbody[1]/tr[{term}]/td[4]"
date_xpath = f"/html[1]/body[1]/div[5]/div[2]/div[2]/div[2]/form[1]/div[1]/table[1]/tbody[1]/tr[{term}]/td[5]"
database_xpath = f"/html[1]/body[1]/div[5]/div[2]/div[2]/div[2]/form[1]/div[1]/table[1]/tbody[1]/tr[{term}]/td[6]"
title = WebDriverWait( driver, 10 ).until( EC.presence_of_element_located((By.XPATH ,title_xpath) ) ).text
authors = WebDriverWait( driver, 10 ).until( EC.presence_of_element_located((By.XPATH ,author_xpath) ) ).text
source = WebDriverWait( driver, 10 ).until( EC.presence_of_element_located((By.XPATH ,source_xpath) ) ).text
date = WebDriverWait( driver, 10 ).until( EC.presence_of_element_located((By.XPATH ,date_xpath) ) ).text
database = WebDriverWait( driver, 10 ).until( EC.presence_of_element_located((By.XPATH ,database_xpath) ) ).text
# 點擊條目
title_list[i].click()
# 獲取driver的句柄
n = driver.window_handles
# driver切換至最新生產的頁面
driver.switch_to_window(n[-1])
# 開始獲取頁面信息
# title = WebDriverWait( driver, 10 ).until( EC.presence_of_element_located((By.XPATH ,"/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h1") ) ).text
# authors = WebDriverWait( driver, 10 ).until( EC.presence_of_element_located((By.XPATH ,"/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h3[1]") ) ).text
institute = WebDriverWait( driver, 10 ).until( EC.presence_of_element_located((By.XPATH ,"/html[1]/body[1]/div[2]/div[1]/div[3]/div[1]/div[1]/div[3]/div[1]/h3[2]") ) ).text
abstract = WebDriverWait( driver, 10 ).until( EC.presence_of_element_located((By.CLASS_NAME ,"abstract-text") ) ).text
try:
keywords = WebDriverWait( driver, 10 ).until( EC.presence_of_element_located((By.CLASS_NAME ,"keywords") ) ).text[:-1]
except:
keywords = '無'
url = driver.current_url
# 獲取下載連結
# link = WebDriverWait( driver, 10 ).until( EC.presence_of_all_elements_located((By.CLASS_NAME ,"btn-dlcaj") ) )[0].get_attribute('href')
# link = urljoin(driver.current_url, link)
# 寫入文件
res = f"{count}\t{title}\t{authors}\t{institute}\t{date}\t{source}\t{database}\t{keywords}\t{abstract}\t{url}".replace("\n","")+"\n"
print(res)
with open('CNKI_res.tsv', 'a', encoding='gbk') as f:
f.write(res)
except:
print(f" 第{count} 條爬取失敗\n")
# 跳過本條,接著下一個
continue
finally:
# 如果有多個窗口,關閉第二個窗口, 切換回主頁
n2 = driver.window_handles
if len(n2) > 1:
driver.close()
driver.switch_to_window(n2[0])
# 計數,判斷需求是否足夠
count += 1
if count == papers_need:break
# 切換到下一頁
WebDriverWait( driver, 10 ).until( EC.presence_of_element_located( (By.XPATH ,"//a[@id='PageNext']") ) ).click()
# 關閉瀏覽器
driver.close()
#get直接返回,不再等待界面加載完成
desired_capabilities = DesiredCapabilities.CHROME
desired_capabilities["pageLoadStrategy"] = "none"
with open('CNKI_res.tsv', 'a', encoding='gbk') as f:
f.write(res)
在數據分析中,往往花費時間的事數據的獲取和數據清洗,怎樣從網際網路海量的數據中高效獲取我們想要的部分?無疑網絡爬蟲是最佳的選擇之一。學習好爬蟲的相關庫的運用,不論是從或聯網上獲取相關的數據,還是設置某些東東提交任務,甚至是寫一些搶票軟體都不是什麼困難的事情。
另一方面,爬蟲與網站的反爬系統一直都是攻防的雙方,Selenium 這類軟體能夠直接模擬瀏覽器操作,進而繞過一些反爬機制,但並不是不能夠被網站識別。在真實的情況中,我們往往需要結合多種手段爬取網頁。
在這場爬蟲和反爬蟲的軍備競賽中;在人與計算機的競賽中,我們只有不斷地學習新的東西,才能在這場進化中不被淘汰。
寫留言