爬蟲(17) – 面試(2) | 爬蟲面試題庫

1.什麼是爬蟲

爬蟲就是爬取網頁數據,只要網頁上有的,都可以通過爬蟲爬取下來,比如圖片、文字評論、商品詳情等。

一般二言,Python爬蟲需要以下幾步:

  1. 找到網頁URL,發起請求,等待服務器響應
  2. 獲取服務器響應內容
  3. 解析內容(正則表達式、xpath、bs4等)
  4. 保存數據(本地文件、數據庫等)

 

2.爬蟲的基本流程

  1. 找到網頁URL,發起請求,等待服務器響應
  2. 獲取服務器響應內容
  3. 解析內容(正則表達式、xpath、bs4等)
  4. 保存數據(本地文件、數據庫等)

 

3.正則表達式貪婪與非貪婪模式的區別

1)什麼是貪婪匹配與非貪婪匹配

  • 貪婪匹配在匹配字符串時總是嘗試匹配儘可能多的字符
  • 與貪婪匹配相反,非貪婪匹配在匹配字符串時總是嘗試匹配儘可能少的字符

2)區別

  • 在形式上,非貪婪模式有一個「?」作為該部分的結束標誌
  • 在功能上,貪婪模式是儘可能多的匹配當前正則表達式,可能會包含好幾個滿足正則表達式的字符串;非貪婪模式,在滿足所有正則表達式的情況下儘可能少的匹配當前正則表單式

3)拓展

  • *? 重複任意次,但儘可能少重複
  • +? 重複1次或更多次,但儘可能少重複
  • ?? 重複0次或1次,但儘可能少重複
  • {n,m}? 重複n到m次,但儘可能少重複
  • {n,}? 重複n次以上,但儘可能少重複

 

4.re模塊中match與search的區別

1)相同點:都是在一個字符串中尋找子字符串,如果能找到,就返回一個Match對象,如果找不到,就返回None。

2)不同點:mtach() 方法是從頭開始匹配,而 search() 方法,可以在字符串的任一位置查找。

 

5.正則表達式如何實現字符串的查找和替換

1)查找:findall()函數

re.findall(r"目標字符串",「原有字符串」)
re.findall(r"張三",「I love 張三」)[0]

2)替換:sub()函數

re.sub(r"要替換原字符",「要替換新字符」,「原始字符串」)
re.sub(r"李四",「python」,「I love 李四」)

 

6.寫出匹配ip的正則表達式

1)ip 地址格式:(1-255).(0-255).(0-255).(0-255)

2)對應的正則表達式

  • “^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.”
  • +”(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.”
  • +”(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.”
  • +”(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$”

3)解析

  • \d 表示 0~9 的任何一個數字
  • {2} 表示正好出現兩次
  • [0-4] 表示 0~4 的任何一個數字
  • | 或者
  • 小括號( )不能少,是為了提取匹配的字符串,表達式中有幾個()就表示有幾個相應的匹配字符串
  • 1\d{2}:100~199之間的任意一個數字
  • 2[0-4]\d:200~249之間的任意一個數
  • 25[0-5]:250~255之間的任意一個數字
  • [1-9]\d:10~99之間的任意一個數字
  • [1-9]:1~9之間的任意一個數字
  • \.:轉義點號.

 

7.寫一個郵箱地址的正則表達式

[A-Za-z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$

 

8.group和groups的區別

  • 1)m.group(N) 返回第N組匹配的字符
  • 2)m.group() == m.group(0) == 所有匹配的字符
  • 3)m.groups() 返回所有匹配的字符,以tuple(元組)格式
  • m.groups() == (m.group(0), m.group(1),……)

 

9.requests請求返回的content和text的區別

1)返回類型不同

response.text 返回的是 Unicode 型數據,response.content 返回的是 bytes 類型,也就是二進制數據

2)使用場景不同

獲取文本使用,response.text,獲取圖片和文件,使用 response.content

3)修改編碼方式不同

  • response.text類型:str,根據 HTTP 頭部對響應的編碼作出有根據的推測
    • 修改編碼方式:response.encoding=」gbk」
  • response.content類型:bytes,沒有指定解碼類型
    • 修改編碼方式:response.content.decode(「utf8」)

 

10.urllib和requests模塊的區別

1)urllib是python內置的包,不需要單獨安裝

2)requests是第三方庫,需要單獨安裝

3)requests庫是在urllib的基礎上封裝的,比urllib模塊更加好用

4)requests可以直接發起get、post請求,urllib需要先構建請求,然後再發起請求

 

11.為什麼requests請求需要帶上header?

1)原因:模擬瀏覽器,欺騙服務器,獲取和瀏覽器一致的內容

2)header格式:字典

headers = {「User-Agent」: 「Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36」}

3)用法:requests.get(url, headers=headers)

 

12.requests模塊有哪些使用小技巧?

1)reqeusts.util.dict_from_cookiejar 把 cookie 對象轉化為字典

2)設置請求不用 SSL 證書驗證

response = requests.get("//www.12306.cn/mormhweb/ ", verify=False)

3)設置超時

response = requests.get(url, timeout=10)

4)配合狀態碼判斷是否請求成功

response.status_code == 200

 

13.json模塊dumps、loads與dump、load方法的區別

  • json.dumps(),將 python 的 dict 數據類型編碼為 json 字符串
  • json.loads(),將 json 字符串解碼為 dict 的數據類型
  • json.dump(x, y),x 是 json 對象,y 是文件對象,最終是將 json 對象寫入到文件中
  • json.load(y),從文件對象 y 中讀取 json 對象

 

14.常見的HTTP請求方法有哪些?

  • GET:請求指定的頁面信息,返回實體主體
  • HEAD:類似於get請求,只不過返回的響應中沒有具體的內容,用於捕獲報頭
  • POST:向指定資源提交數據進行處理請求(比如表單提交或者上傳文件),數據被包含在請求體中
  • PUT:從客戶端向服務端傳送數據取代指定的文檔的內容
  • DELETE:請求刪除指定的頁面
  • CONNNECT:HTTP1.1協議中預留給能夠將連接方式改為管道方式的代理服務器
  • OPTIONS:允許客戶端查看服務器的性能; TRACE:回顯服務器的請求,主要用於測試或者診斷

 

15.HTTPS協議有什麼優點和缺點?

1)優點

  • 使用 HTTPS 協議可認證用戶和服務器,確保數據發送到正確的客戶機和服務器
  • HTTPS 協議是由 SSL+HTTP 協議構建的可進行加密傳輸、身份認證的網絡協議,要比 http協議安全,可防止數據在傳輸過程中不被竊取、改變,確保數據的完整性
  • HTTPS 是現行架構下最安全的解決方案,雖然不是絕對安全,但它大幅增加了中間人攻擊的成本

2)缺點

  • HTTPS 協議的加密範圍也比較有限,在黑客攻擊、拒絕服務攻擊、服務器劫持等方面幾乎起不到什麼作用
  • HTTPS 協議還會影響緩存,增加數據開銷和功耗,甚至已有安全措施也會受到影響也會因此而受到影響
  • SSL 證書需要錢,功能越強大的證書費用越高,個人網站、小網站沒有必要一般不會用
  • HTTPS 連接服務器端資源佔用高很多,握手階段比較費時對網站的相應速度有負面影響
  • HTTPS 連接緩存不如 HTTP 高效

 

16.HTTP通信組成

HTTP通信由兩部分組成:客戶端請求消息與服務器響應消息。

 

17.robots.txt協議文件有什麼作用?

搜索引擎訪問一個網站的時候,最先訪問的文件就是robots.txt,網站通過 Robots 協議告訴搜索引擎哪些頁面可以抓取,哪些頁面不能抓取。

 

18.robots.txt文件放在哪裡?

此文件需放置在網站的根目錄,且對字母大小寫有限制,文件名必須為小寫字母。所有的命令第一個字母需大寫,其餘的小寫,且命令之後要有一個英文字符空格。

 

19.怎麼書寫Robots協議?

  • User-agent:表示定義哪個搜索引擎,如User-agent:Baiduspider,定義百度
  • Disallow:表示禁止訪問
  • Allow:表示運行訪問

 

20.談談你對多進程,多線程,以及協程的理解,項目中是否使用?

  • 進程:一個運行的程序(代碼)就是一個進程,沒有運行的代碼叫程序,進程是系統資源分配的最小單位,進程擁有自己獨立的內存空間,所以進程間數據不共享,開銷大。
  • 線程:調度執行的最小單位,也叫執行路徑,不能獨立存在,依賴進程存在一個進程至少有一個線程,叫主線程,而多個線程共享內存(數據共享,共享全局變量),從而極大地提高了程序的運行效率。
  • 協程:是一種用戶態的輕量級線程,協程的調度完全由用戶控制。協程擁有自己的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧,直接操作棧則基本沒有內核切換的開銷,可以不加鎖的訪問全局變量,所以上下文的切換非常快。
  • 項目中有使用多線程爬取數據,提升效率。

 

21.線程的執行順序

  • 新建:線程創建(t=threading.Thread(target=方法名)或者線程類)
  • 就緒:當啟動線程後,線程就進入就緒狀態,就緒狀態的線程會被放到一個CPU調度隊列裏面,cpu會負責讓其中的線程運行,變為運行狀態
  • 運行狀態:CPU調度一個就緒狀態的線程,該線程就變為運行狀態
  • 阻塞狀態:當運行狀態的線程被阻塞變為阻塞狀態,阻塞狀態的線程就會重新變為就緒狀態才能繼續運行
  • 死亡狀態:線程執行完畢

 

22.多線程和多進程的優缺點

1)多線程的優點

  • 程序邏輯和控制方式複雜
  • 所有線程可以直接共享內存和變量
  • 線程方式消耗的總資源比進程方式好

2)多線程缺點

  • 每個線程與主程序共用地址空間,受限於2GB地址空間
  • 線程之間的同步和加鎖控制比較麻煩
  • 一個線程的崩潰可能影響到整個程序的穩定性

3)多進程優點

  • 每個進程互相獨立,不影響主程序的穩定性,子進程崩潰沒關係
  • 通過增加CPU,就可以容易擴充性能
  • 每個子進程都有2GB地址空間和相關資源,總體能夠達到的性能上限非常大

4)多進程缺點

  • 邏輯控制複雜,需要和主程序交互
  • 需要跨進程邊界,如果有大數據量傳送,就不太好,適合小數據量傳送、密集運算
  • 多進程調度開銷比較大

在實際開發中,選擇多線程和多進程應該從具體實際開發來進行選擇,最好是多進程和多線程結合。

 

23.寫爬蟲是用多進程好?還是多線程好?為什麼?

IO 密集型代碼(文件讀寫處理、網絡爬蟲等),多線程能夠有效地提升效率。單線程下有 IO 操作會進行 IO 等待,造成不必要的時間浪費,而開啟多線程能在線程 A 等待時,自動切換到線程 B,可以不浪費 CPU等資源,從而能提升程序執行效率。

在實際的數據採集過程中,既考慮網速和響應的問題,也需要考慮自身機器的硬件情況,來設置多進程或多線程。

多進程適合在 CPU 密集型操作(cpu 操作指令比較多,如位數多的浮點運算)。多線程適合在 IO 密集型操作(讀寫數據操作較多的,比如爬蟲)。

 

24.什麼是多線程競爭?

線程是非獨立的,同一個進程里線程是數據共享的,當各個線程訪問數據資源時會出現競爭狀態,即數據幾乎同步會被多個線程佔用,造成數據混亂,即所謂的線程不安全。

那麼怎麼解決多線程競爭問題? ——鎖。

 

25.什麼是鎖,以及鎖的優劣之處

鎖(Lock)是 Python 提供的對線程控制的對象。

  • 鎖的好處:確保了某段關鍵代碼(共享數據資源)只能由一個線程從頭到尾完整地執行,能解決多線程資源競爭的問題。
  • 鎖的壞處:阻止了多線程並發執行,包含鎖的某段代碼實際上只能以單線程模式執行,效率就大大地下降了。
  • 鎖的致命問題:死鎖。

 

26.什麼是死鎖?

若干子線程在系統資源競爭時,都在等待對方對某部分資源解除佔用狀態,結果是誰也不願先解鎖,互相干等着,程序無法執行下去,這就是死鎖。

 

27.XPath解析數據使用什麼庫

XPath解析數據需要依賴於lxml庫。

 

28.用於網絡爬蟲的XPath的主要語法

  • . 選取當前節點
  • … 選取當前節點的父節點
  • @ 選取屬性
  • * 任意匹配,//div/*,div下的任意元素
  • // 從匹配選擇的當前節點選擇文檔中的節點,不考慮他們的位置
  • //div 選取所有的div
  • //div[@class=」demo」] 選取class為demo的div節點

 

29.Mysql,Mongodb,redis三個數據庫的理解

1)MySQL 數據庫:開源免費的關係型數據庫,需要實現創建數據庫、數據表和表的字段,表與表之間可以進行關聯(一對多、多對多),是持久化存儲

2)Mongodb 數據庫:是非關係型數據庫,數據庫的三元素是,數據庫、集合、文檔,可以進行持久化存儲,也可作為內存數據庫,存儲數據不需要事先設定格式,數據以鍵值對的形式存儲。

3)redis 數據庫:非關係型數據庫,使用前可以不用設置格式,以鍵值對的方式保存,文件格式相對自由,主要用與緩存數據庫,也可以進行持久化存儲

 

30.MongoDB常見命令

  • use yourDB; 切換/創建數據庫
  • show dbs; 查詢所有數據庫
  • db.dropDatabase(); 刪除當前使用數據庫
  • db.getName(); 查看當前使用的數據庫
  • db.version(); 當前 db 版本
  • db.addUser(「name」); 添加用戶,db.addUser(「userName」, 「pwd123」, true);
  • show users; 顯示當前所有用戶
  • db.removeUser(「userName」); 刪除用戶
  • db.collectionName.count(); 查詢當前集合的數據條數
  • db.collectionName.find({key:value}); 查詢數據

 

31.爬蟲項目為什麼使用MongoDB,而不使用MySQL數據庫

1)MySQL屬於關係型數據庫,它具有以下特點:

  • 在不同的引擎上有不同的存儲方式
  • 查詢語句是使用傳統的sql語句,擁有較為成熟的體系,成熟度很高
  • 開源數據庫的份額在不斷增加,MySQL的份額也在持續增長
  • 處理海量數據的效率會顯著變慢

2)Mongodb屬於非關係型數據庫,它具有以下特點:

  • 數據結構由鍵值對組成
  • 存儲方式:虛擬內存+持久化
  • 查詢語句是獨特的Mongodb的查詢方式
  • 具備高可用性
  • 數據是存儲在硬盤上的,只不過需要經常讀取的數據會被加載到內存中,將數據存儲在物理內存中,從而達到高速讀寫

 

32.MongoDB的優點

面向文件、高性能、高可用、易擴展、可分片、對數據存儲友好。

 

33.MongoDB支持哪些數據類型?

  • String、Integer、Double、Boolean
  • Object、Object ID
  • Arrays
  • Min/Max Keys
  • Code、Regular Expression等

 

34.MongoDB “Object ID”由哪些部分組成?

“Object ID”數據類型用於存儲文檔id

一共有四部分組成:時間戳、客戶端ID、客戶進程ID、三個位元組的增量計數器

 

35.Redis數據庫支持哪些數據類型?

1)String:String 是 Redis 最為常用的一種數據類型,String 的數據結構為 key/value 類型,String 可以包含任何數據。常用命令有set、get、decr、incr、mget等。

2)Hash:Hash 類型可以看成是一個 key/value 都是 String 的 Map 容器。常用命令有hget、hset、hgetall等。

3)List:List 用於存儲一個有序的字符串列表,常用的操作是向隊列兩端添加元素或者獲得列表的某一片段。常用命令有lpush、rpush、lpop、rpop、lrange等。

4)Set:Set 可以理解為一組無序的字符集合,Set 中相同的元素是不會重複出現的,相同的元素只保留一個。常用命令有sadd、spop、smembers、sunion等。

5)Sorted Set(有序集合):有序集合是在集合的基礎上為每一個元素關聯一個分數,Redis 通過分數為集合中的成員進行排序。常用命令有zadd、zrange、zrem、zcard等。

 

36.Redis有多少個庫?

Redis 一個實例下有 16 個庫,默認使用 0 庫,select 1 可以切換到 1 庫。

 

37.談談Selenium框架

Selenium 是一個 Web 的自動化測試工具,可以根據我們的指令,讓瀏覽器自動加載頁面,獲取需要的數據,甚至頁面截屏,或者判斷網站上某些動作是否發生。Selenium 自己不帶瀏覽器,不支持瀏覽器的功能,它需要與Firefox、chrome等第三方瀏覽器結合在一起才能使用,需要先下載瀏覽器的Driver。

 

38.selenium有幾種元素定位方式,你最常用哪種?

1)selenium有八種定位方式

  • 和name有關的:ByName、ByClassName、ByTagName
  • 和link有關的:ByLinkText、ByPartialLinkText
  • 和id有關的:ById
  • 全能的:ByXpath和ByCssSelector

2)最常用的是ByXpath,因為很多情況下,html標籤的屬性不夠規範,無法通過單一的屬性定位,這個時候使用xpath可以去重實現定位唯一元素;事實上定位最快的應當屬於ById,因為id是唯一的,然而大多數網頁元素並沒有設置id。

 

39.selenium使用過程中你都遇到過哪些坑,都是如何解決的?

程序運行不穩定,有時運行失敗抓取不到數據

解決方法:

  • 添加元素智能等待時間 driver.implicitly_wait(30)
  • 添加強制等待時間(比如python中寫time.sleep())
  • 多用 try 捕捉,處理異常,多種方式進行定位,如果第一種失敗可以自動嘗試第二種

 

40.driver對象有哪些常用屬性和方法?

  • driver.page_source:當前標籤頁瀏覽器渲染之後的網頁源代碼
  • driver.current_url:當前標籤頁的url
  • driver.close() :關閉當前標籤頁,如果只有一個標籤頁則關閉整個瀏覽器
  • driver.quit():關閉瀏覽器
  • driver.forward():頁面前進
  • driver.back():頁面後退
  • driver.screen_shot(img_name):頁面截圖

 

41.談談你對 Scrapy的理解

Scrapy框架,只需要實現少量代碼,就能夠快速的抓取到數據內容。Scrapy 使用了 Twisted異步網絡框架來處理網絡通訊,可以加快下載速度,不用自己去實現異步框架,並且包含各種中間件接口,可以靈活的完成各種需求。

scrapy 框架的工作流程:

  • 1)首先Spiders(爬蟲)將需要發送請求的 url(requests)經ScrapyEngine(引擎)交給Scheduler(調度器)。
  • 2)Scheduler(排序,入隊)處理後,經ScrapyEngine,交給Downloader。
  • 3)Downloader向互聯網發送請求,並接收下載響應(response),將響應(response)經ScrapyEngine交給Spiders。
  • 4)Spiders處理response,提取數據並將數據,經ScrapyEngine交給ItemPipeline 保存(可以是本地,可以是數據庫),提取url重新經ScrapyEngine交給Scheduler進行下一個循環,直到無Url請求程序結束。

 

42.Scrapy框架的基本結構

1)引擎(Scrapy):用來處理整個系統的數據流處理, 觸發事務(框架核心)。

2)調度器(Scheduler):用來接受引擎發過來的請求, 壓入隊列中, 並在引擎再次請求的時候返回。可以想像成一個URL(抓取網頁的網址或者說是鏈接)的優先隊列, 由它來決定下一個要抓取的網址是什麼, 同時去除重複的網址。

3)下載器(Downloader):用於下載網頁內容, 並將網頁內容返回給蜘蛛(Scrapy下載器是建立在twisted這個高效的異步模型上的)。

4)爬蟲(Spiders):爬蟲是主要幹活的, 用於從特定的網頁中提取自己需要的信息, 即所謂的實體(Item)。用戶也可以從中提取出鏈接,讓Scrapy繼續抓取下一個頁面

5)項目管道(Pipeline):負責處理爬蟲從網頁中抽取的實體,主要的功能是持久化實體、驗證實體的有效性、清除不需要的信息。當頁面被爬蟲解析後,將被發送到項目管道,並經過幾個特定的次序處理數據。

6)下載器中間件(Downloader Middlewares):位於Scrapy引擎和下載器之間的框架,主要是處理Scrapy引擎與下載器之間的請求及響應。

7)爬蟲中間件(Spider Middlewares):介於Scrapy引擎和爬蟲之間的框架,主要工作是處理蜘蛛的響應輸入和請求輸出。

8)調度中間件(Scheduler Middewares):介於Scrapy引擎和調度之間的中間件,從Scrapy引擎發送到調度的請求和響應。

 

43.Scrapy框架去重原理

需要將dont_filter參數設置為False,開啟去重。

對於每一個url的請求,調度器都會根據請求的相關信息加密得到一個指紋信息,並且將指紋信息和set()集合中得指紋信息進行比對,如果set()集合中已經存在這個數據,就不在將這個Request放入隊列中。如果set()集合中沒有,就將這個Request對象放入隊列中,等待被調度。

 

44.Scrapy框架的優缺點

1)優點:

  • Scrapy 是異步的,他的異步機制是基於 twisted 異步網絡框架處理的,在 settings.py文件里可以設置具體的並發量數值(默認並發量 16)
  • 採取可讀性更強的xpath代替正則
  • 強大的統計和log系統
  • 同時在不同的url上爬行
  • 支持shell方式,方便獨立調試
  • 寫middleware,方便寫一些統一的過濾器
  • 通過管道的方式存入數據庫

2)缺點:

  • 不能實現分佈式爬取
  • 異步框架出錯後不會停掉其他任務,數據出錯後難以察覺

 

45.Scrapy和requests的區別

1)scrapy 是封裝起來的框架,包含了下載器、解析器、日誌及異常處理,基於多線程, twisted 的方式處理,可以加快我們的下載速度,不用自己去實現異步框架。擴展性比較差,不夠靈活。

2)requests 是一個 HTTP 庫, 它只是用來進行請求,對於 HTTP 請求,他是一個強大的庫,下載、解析全部自己處理,靈活性更高,高並發與分佈式部署也非常靈活。

 

46.Scrapy和Scrapy-Redis的區別

Scrapy是一個Python爬蟲框架,爬取效率極高,具有高度定製性,但是不支持分佈式。

而Scrapy-Redis一套基於redis數據庫、運行在Scrapy框架之上的組件,可以讓Scrapy支持分佈式策略,Slaver端共享Master端Redis數據庫里的item隊列、請求隊列和請求指紋集合。

 

47.分佈式爬蟲為什麼選擇Redis數據庫?

因為Redis支持主從同步,而且數據都是緩存在內存中的,所以基於Redis的分佈式爬蟲,對請求和數據的高頻讀取效率非常高。

Redis的優點:

  • 數據讀取快,因為數據都放在內存上
  • 支持事務機制
  • 數據持久化,支持快照和日誌,方便恢複數據
  • 擁有豐富的數據類型:list,string,set,qset,hash
  • 支持主從複製,可以進行數據備份
  • 豐富的特性:可以作為緩存,消息隊列,設置過期時間,到期自動刪除

 

48.分佈式爬蟲主要解決什麼問題?

IP、帶寬、CPU、io等問題。

 

49.Scrapy框架如何實現分佈式抓取?

可以藉助scrapy_redis類庫來實現。

在分佈式爬取時,會有master機器和slave機器,其中,master為核心服務器,slave為具體的爬蟲服務器。

在master服務器上安裝Redis數據庫,並將要抓取的url存放到redis數據庫中,所有的slave爬蟲服務器在抓取的時候從redis數據庫中去鏈接,由於scrapy-redis自身的隊列機制,slave獲取的url不會相互衝突,然後抓取的結果最後都存儲到數據庫中。master的redis數據庫中還會將抓取過的url的指紋存儲起來,用來去重。