基於selenium+phantomJS的動態網站全站爬取

来源:https://www.cnblogs.com/NosenLiu/archive/2018/08/12/9465105.html
-Advertisement-
Play Games

由於需要在公司的內網進行神經網路建模試驗(https://www.cnblogs.com/NosenLiu/articles/9463886.html),為了更方便的在內網環境下快速的查閱資料,構建深度學習模型,我決定使用爬蟲來對深度學習框架keras的使用手冊進行爬取。 keras中文文檔的地址是 ...


 

由於需要在公司的內網進行神經網路建模試驗(https://www.cnblogs.com/NosenLiu/articles/9463886.html),為了更方便的在內網環境下快速的查閱資料,構建深度學習模型,我決定使用爬蟲來對深度學習框架keras的使用手冊進行爬取。

keras中文文檔的地址是 http://keras-cn.readthedocs.io/en/latest/ ,是基於英文原版使用手冊https://keras.io/,由國內眾多學者進行翻譯所得,方便大家在學習和工作中快速的進行查閱。

 

在編寫爬蟲之前,我們需要對網站的源碼進行分析,以確定抓取策略。

首先,網頁分為左右兩個部分,並且網站的大部分有效地址基本上都是集中在頁面左側的索引中,以<li class="toctree-l1 "></li>標簽進行包圍。

根據網站的這個特征,我們可以不使用傳統的 URL管理器+網頁下載器+解析器 的傳統遞歸爬取模式,化繁為簡,一次性的獲取索引中所有的待爬取url。

其次,該頁面的url不同於我們平時所瀏覽的.html或.jsp文件,通過瀏覽器的查看元素操作,可以知道該url所對應的是一個事件。應該是類似於一個action指令,伺服器根據這個傳入參數,來動態的返回頁面。

為了正確的獲取動態頁面的內容,我們設計使用基於selenium+phantomJS的python語言爬蟲來完成全站爬取任務。

selenium 是一個用於Web應用程式測試的工具。Selenium測試直接運行在瀏覽器中,就像真正的用戶在操作一樣。支持的瀏覽器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等[1]。

phantomJS是一個基於 WebKit(WebKit是一個開源的瀏覽器引擎,Chrome,Safari就是用的這個瀏覽器引擎) 的伺服器端 JavaScript API,python可以使用selenium執行javascript,selenium可以讓瀏覽器自動載入頁面,獲取需要的數據。

關於selenium與phantomJS的用法在網上有很多講解,本文不再贅述,僅針對該全站爬取任務進行闡述。

動態頁面與靜態頁面的分析

不同於單個網頁的下載,全站爬取的難點在於如何在爬取之後保存網頁之間的正確調用關係(即點擊超鏈接能夠正確的進行頁面跳轉)。在目標網站 keras中文文檔中,伺服器通過傳遞進來的action,使用servlet進行應答,返回對應的頁面(筆者web開發的功底不牢固,只能描述大概流程,伺服器運行具體細節難以描述清楚  =。=#  )。而將這些動態頁面的信息以靜態方式進行存儲後,只有把它們放在正確的相對路徑下,才能夠在流量器中正常使用,因此在下載頁面的時候,需要完成以下兩個工作: 

工作1.獲取頁面所在的相對路徑,並且給頁面命名。通過對頁面的源代碼進行瀏覽,我們可以發現,每個頁面的url就是它以/latest/為根目錄的相對路徑。

圖1 網站主頁面上的序貫模型url (相對路徑)

圖2 序貫模型頁面的真實url (絕對路徑)

根據這個特征,我們可以設計相對的函數,來獲取所有待爬取頁面的真實url。此外,為了能夠對頁面進行正確的保存,需要給文件進行命名,這裡將所有頁面名稱定位info.html。例如,序貫模型的頁面在本地就存儲在  ./latest/getting_started/sequential_model/info.html 文件中。

工作2.將頁面存儲到本地時,將其中的超鏈接地址改為目標靜態頁面的相對路徑。例如,對於主頁 http://keras-cn.readthedocs.io/en/latest/,它的序貫模型索引的url如下:

而對於我們所爬取下來的靜態主頁 ./latest/info.html 來說,它的序貫模型索引的url如下:

我們需要精確的指向該頁面在本地目錄中所保存的地址。

註意:我們只修改以<li class="toctree-l1 "></li>標簽進行環繞的超鏈接<a>,其他類似href=”#keras-cn”的鏈接只是JavaScript的一個位置移動操作,並不會對新的頁面進行載入(這一點我花了好久時間才看懂,之前一直以為需要對 #keras-cn新建一個路徑,再對其頁面進行靜態保存……)。

 

做完了上述工作,就可以對網頁進行爬取了,但此時,爬取出的網頁大概是這個樣子:

這是因為我們此時並沒有下載網頁的樣式文件.css與.js文件,導致一片白板。繼續觀察網頁源碼,發現該網站下所有的頁面,其.css文件與.js文件路徑都在頁面的<head>標簽內進行規定,且均指向/lastest/css/文件夾與/latest/js/文件夾。因此我們只要在存儲網站主頁的時候,對.css與.js文檔存儲一次即可。

 

整個網站爬取的流程如下:

①使用selenium+phantomJS打開根頁面,獲取頁面左側索引的全部url,將其存儲在url_list中。

②調用頁面保存函數,對根頁面進行保存。

③下載<head>標簽內的 .css 與 .js 文件。

④迴圈遍歷url_list中的頁面地址,使用selenimu的webdriver進行打開,調用頁面保存函數對頁面內容進行保存。

 

註意事項:

1.獲取索引URL時,由於href給出的是相對路徑,需要將相對url拼接為絕對url再存入url_list。

2.存取網頁時,根據<head>中的<meta charset="utf-8">,需要將頁面使用utf-8編碼進行保存。具體語法如下:

1 with open(save_path+page_name,'wb') as f_in:
2     f_in.write(driver.page_source.encode('utf-8'))

3.每爬取一個頁面,暫停一段時間,這既是互聯網上的禮節禮貌問題,也降低了自己被反爬措施限制的風險。

time.sleep(3)  # 勿頻繁訪問,以防網站封禁

 在我第一次爬取tensorflow手冊時,沒有設置訪問延遲,被網站鎖了一個星期不能訪問,都是血淚教訓~。

4.通過代碼下載的.css和.js文件有可能不全,我通過右鍵網頁→頁面另存為,獲取了完整的js和css文件,將其移動到對應的/latest/css/和/latest/js/路徑下即可。

 

具體實現:

①獲取絕對url函數:

 1 def get_abs_url(url,href):     
 2     if '../' in href:
 3         count = 0
 4         while('../' in href):
 5             count += 1
 6             href = href[3:]
 7         for i in range(count):
 8             if url[-1]=='/':     # 去除掉url最後一個 '/'
 9                 url = url[:-1]
10             rare = url.split('/')[-1]
11             url = url.split(rare)[0]
12         if href[-1]=='/':
13             return url+href[:-1]
14         else:
15             return url+href
16     elif './' in href:             
17         href = href[2:]

使用該函數,對不同類型的相對路徑進行解析,獲取能正確訪問對應頁面的絕對url。

 

②保存數據函數(主要用於保存css文件和js文件)

 1 def save_data(driver, path):   # 這個path是指/latest/路徑之後的path。 頁面的話要加上  路徑/info.html
 2     if path[-4:]=='.ico':
 3         with open('./latest/'+path,'wb') as f_in:
 4             f_in.write(driver.page_source)
 5     elif path[-4:]=='.css' or path[-3:]=='.js':
 6         with open('./latest/'+path,'wb') as f_in:
 7             f_in.write(driver.page_source.encode('utf-8'))
 8     else:
 9         with open('./latest/'+path+'/info.html','wb') as f_in:
10             f_in.write(driver.page_source.encode('utf-8'))

 

③保存頁面函數,根據路徑和頁面內容,來對頁面進行保存。並且對頁面中的url地址進行修改,以便正確的調用靜態頁面。

 1 def save_page(driver,save_path):
 2     with open(save_path+page_name,'wb') as f_in:
 3         f_in.write(driver.page_source.encode('utf-8'))
 4     temp_file_lines = []
 5     # 下麵這一步把頁面中的 'layers/pooling_layer/' 改為 './layers/pooling_layer/info.html'  以方便靜態調用
 6     with open(save_path+page_name,'r', encoding="utf-8") as f_in:   
 7         f_lines = f_in.readlines()
 8         for i in range(len(f_lines)):
 9             if 'toctree-l1' in f_lines[i] and "href=\".\"" not in f_lines[i+1]:
10                 temp_loc = f_lines[i+1].split('"')[3]
11                 new_loc = './'+temp_loc+page_name
12                 f_lines[i+1] = f_lines[i+1].split(temp_loc)[0] + new_loc + f_lines[i+1].split(temp_loc)[1]
13             temp_file_lines.append(f_lines[i].encode('utf-8'))  
14     with open(save_path+page_name,'wb') as f_in:
15         f_in.writelines(temp_file_lines)

 

④文件路徑獲取函數

1 def get_save_path(url):     # 將url變為相對的文件保存路徑。
2     if url[-1]!='/':
3         url = url+'/'
4     rare = url.split(root_url)[1]
5     path = root_dir+rare
6     return path

通過該函數獲取靜態頁面存儲路徑(相對路徑)。

 

另外還有一些邏輯直接寫在了main函數里,如通過BeautifulSoup解析url地址:

1 driver = webdriver.PhantomJS()
2 driver.get(root_url)
3 li_list = BeautifulSoup(driver.page_source,'html.parser').find_all('li',class_='toctree-l1')

通過<head>標簽解析.css與.js文件地址:

 1 # TODO 在head標簽中尋找 .css 及 .js
 2     link_list = BeautifulSoup(driver.page_source,'html.parser').find('head').find_all('link')
 3     script_list = BeautifulSoup(driver.page_source,'html.parser').find('head').find_all('script')
 4     css_list = []   # 存儲css文件
 5     for link in link_list:
 6         href = link['href']
 7         if 'https://' in href:
 8             css_list.append(href)
 9         else:
10             css_list.append(get_abs_url(root_url,href))
11     js_list = []    # 存儲 js 文件
12     for js in script_list:
13         try:
14             href = js['src']
15         except:
16             continue
17         if 'https://' in href:
18             js_list.append(href)
19         else:
20             js_list.append(get_abs_url(root_url,href))

 

 

具體的代碼可從我的GitHub上進行下載。

https://github.com/NosenLiu/crawler_keras

其中的main.py便是程式代碼,python3實現。

 

 

 

[1] https://blog.csdn.net/qq_29186489/article/details/78661008 selenium用法詳解

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 又有時間寫東西了,最近深感世事並不以人的美好願望而改變,還是要以積極地心態來適應新變化,多多關心身邊的人。 圖釘畫中一個圖釘代表一個像素,所以關鍵在於像素渣化,降低解析度,圖釘的色彩有限,還需要降低圖片的色彩數量,統計各種色彩的數量及位置。 以上都可以用Pillow完成,Pillow是Python中 ...
  • 網路上兩台主機的交互 ①根據IP找到對方主機 ②數據發送到對方指定的應用程式上,為了表示這些應用程式,引入了埠的概念。 常用埠: wed埠80 MySQL埠3306 有效埠 0~65535 ③定義通信規則,稱為協議。國際組織定義了通用協議TCP/IP協議 本地迴環地址:127.0.0.1( ...
  • 恢復內容開始 python爬蟲學習從0開始 第一次學習了python語法,迫不及待的來開始python的項目。首先接觸了爬蟲,是一個簡單爬蟲。個人感覺python非常簡潔,相比起java或其他面向對象的編程語言,動態語言不需要聲明函數或變數類型。python有20年的發展歷史,以簡潔高效聞名,pyt ...
  • 說概率前複習下歷史函數create_rand_list() #創建一個含有指定數量元素的listsum_fun() #累加len_fun() #統計個數multiply_fun() #累乘sum_mean_fun() #算數平均數sum_mean_rate() #算數平均數計算回報median_fu ...
  • 一、為什麼要用synchronized關鍵字 首先多線程中多個線程運行面臨共用數據同步的問題。 多線程正常使用共用數據時需要經過以下步驟: 1.線程A從共用數據區中複製出數據副本,然後處理。 2.線程A將處理好的數據副本寫入共用數據區。 3.線程B從共用數據區中複製出數據副本。 如此迴圈,直到線程結 ...
  • 最近維護一個項目,裡面用到ClientDataSet,由於之前接觸ClientDataSet比較少,所以這個星期補了一下關於ClientDataSet的知識,併在此記錄下我所瞭解到的並應用到實際項目中的ClientDataSet的知識。 項目新需求:1.從別的資料庫導入物料資料,並允許操作員做修改後 ...
  • 在最近的秋招中,阿裡和多益網路都問到了這個問題,雖然很簡單,但是我還是想總結一下,感興趣的可以看一下我的 "個人博客網站(Spring+MyBatis+redis+nginx+mysql)" (適合菜鳥),最近會抽空把最近面試遇到的問題總結一下。 本文針對問題:深克隆和淺克隆的區別和實現方式?(阿裡 ...
  • 1、數據結構 從不同的角度,可以分為邏輯結構和物理結構 邏輯結構:是數據元素之間的相互關係 集合結構 線性結構 樹形結構 圖形結構 物理結構:數據的邏輯結構在電腦的存儲形式 順序存儲結構:數據間的邏輯關係和物理關係一致 鏈式存儲結構 2、演算法時間複雜度 時間複雜度T(n)=O(f(n));f(n) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...