1. 程式人生 > >爬蟲——綜合案例流程版

爬蟲——綜合案例流程版

定義 dfs 規範 地址 存在 加密 通過 編寫 agen

爬蟲綜合案例

開發步驟:

  • 導入類庫
  • 創建爬蟲通用類
  • 初始化init方法
  • 類中編寫重試下載模塊
  • 類中編寫真正下載模塊
  • 類外編寫保存函數
  • 類外編寫獲取robots.txt函數
  • 類外編寫抽取網址函數
  • 類中編寫網址正常化函數
  • 創建下載限流類
  • 爬蟲通用類封裝run方法
  • 創建爬蟲對象運行

導入類庫

  • requests:爬蟲請求類庫
  • hashlib:哈希加密類庫
  • queue:隊列
  • re:正則
  • time:時間
  • threading>Thread:多線程
  • datetime>datetime:日期時間
  • urllib>parse>urlparse,urljoin,urldefrag:網址解析、拼接、截#取
  • urllib>robotparser:robot.txt解析
  • 目錄名>文件名>MongoCache:存儲到mongodb

創建爬蟲通用類

功能:從初始網址爬取並抽取內層網址繼續爬取
技術:隨機User-Agent生成,隊列,多線程,robots解析,下載限流,mongodb存儲,爬取深度限制,重試下載,抽取特定路由,真正下載,網址正常化,功能封裝
規範:用到類屬性和類方法的方法在類裏編寫,用不到的在類外面編寫,可以建個助手目錄utils存放這些助手函數

初始化init方法

通常在開發過程中不可能一下子就把init寫得完整,先把最易用到的,已經想到的初始化,後面的在編寫其他函數想到時再來init裏編寫

  • 生成UserAgent對象,隨機生成headers
  • 保存初始網址
  • 創建隊列並放入初始網址

使用不同的隊列會造成BFS和DFS的效果 使用先進先出隊列產生廣度優先搜索,使用先進後出(棧)產生深度優先搜索

  • 創建robots解析對象傳入初始網址
  • 設置指定路由
  • 創建限流器對象並初始化間隔時間
  • 創建mongodb存儲對象
  • 設置網址訪問深度,在類外設置最大深度定量

類中編寫重試下載模塊

  • 調用retry裝飾器裝飾該函數並設置最多重試次數
  • 設置函數參數:網址,數據,請求方式,代理
  • 編寫POST和GET爬取方式
  • 插入斷言:狀態碼不為200則拋出異常
  • 返回爬取結果content


text:返回的是unicode 型的數據,一般是在網頁的header中定義的編碼形式,如果想要提取文本就用text;
content:返回的是bytes,二級制型的數據;想要提取圖片、文件,就要用到content;
.text是現成的字符串,.content還要編碼,但是.text不是所有時候顯示都正常,這是就需要用.content進行手動編碼。

類中編寫真正下載模塊

將重試下載模塊封裝在此,不對用戶展示重試下載接口

  • 函數參數:網址,數據(默認None),請求方式(默認GET),代理(默認為空)
  • 輸出一句下載信息
  • try~except 捕獲重試下載模塊的異常
  • 返回結果


類外編寫保存函數

保存函數:將爬取內容MD5加密存儲到文件中,註:使用mongodb保存結果則無需次函數

  • 創建md5加密對象
  • 加密update結果
  • 拼接保存文件路徑
  • 寫入文件

類外編寫獲取robots.txt函數

  • 創建robot文件解析對象
  • 拼接robots.txt所在完整地址
  • 獲取robots.txt文件
  • 將robot.txt文件讀取到rp對象中
  • 返回該解析對象

類外編寫抽取網址函數

方式:lxml、BeautifulSoup、正則

lxml

1 html = lxml.html.fromstring(html_content)
2 html_data = html.xpath(//a/@href)

BeautifulSoup

1 soup = BeautifulSoup(lxml)
2 a_list = soup.find_all(a)
3 for a in a_list:
4 print(a[href])

正則

1 url_regex = re.compile(<a[^>]+href=["\‘](.*?)["\‘], re.IGNORECASE)
2 return url_regex.findall(html_content.decode("utf-8"))


decode解碼問題:不同網站所使用的編碼方式有所不同—— utf-8 , gbk, gb2312, ISO-8859-1


類中編寫網址正常化函數

實現一個類方法的時候,要註意類方法是否使用了當前類的屬性或其他方法,如果未使用就說明和當前類沒有直接關系,最好獨立出來,當做工具方法

  • 以網址中的#進行分割(urldefrag),提取出網址部分和#後的轉發部分
  • 將下載地址拼接上網址部分(urljoin)
 1 urljoin:拼接網址,若參數2網址是正確格式,那麽拼接的結果只是參數2網址,參數1網址忽略;若參數2網址是錯誤格式或是後綴path,那麽和參數1網址進行拼接
 2 urljoin(http://www.baidu.com,/ljb.html/index.html)
 3 http://www.baidu.com/ljb.html/index.html
 4 
 5 urljoin(http://www.baidu.com,http://ljb.html/index.html)
 6 http://ljb.html/index.html
 7 
 8 urljoin(/www.baidu.com,http://ljb.html/index.html)
 9 http://ljb.html/index.html
10 
11 urljoin(/test.html,http://ljb.html/index.html)
12 http://ljb.html/index.html
13 
14 urljoin(http://www.baidu.com/test.html,http://ljb.html/index.html)
15 http://ljb.html/index.html
16 
17 urljoin(http://www.baidu.com/test.html,http://www.sina.com/ljb.html/index.html)
18 http://www.sina.com/ljb.html/index.html

創建下載限流類

限流:設置爬取的間隔時間是為了避免IP被封鎖,隨機間隔時間更可靠


初始化方法

  • 創建domains字典,網址為鍵,訪問時間為值
  • 傳參delay,自行設置兩次下載間隔時間

間隔方法

  • 原理:以delay作為時間間隔或超過delay才可進行訪問(爬取)
  • 第一次訪問:獲取不到網址對應的訪問時間(不執行if-else語句),以網址地址為鍵,訪問時間為值存入字典
  • 第二次訪問:獲取上次訪問對應網址的訪問時間,進行if-else判斷,計算時間差
  • 註:若delay>(這次訪問時間-上次訪問時間),說明還沒等夠,還需要繼續等待,還需等待的時間是——總等待時間delay減去已經等待的時間(就是當前時間減去本次訪問的時間)
  • 註:若delay<(這次訪問時間-上次訪問時間),說明已經等夠了,可以直接訪問
  • 得到鍵(域名):urlparse().netloc
    1 urlparse(http://www.baidu.com/index.html?x=123&y=789#13579)
    2 ParseResult(scheme=http, netloc=www.baidu.com, path=/index.html, params=‘‘, query=x=123&y=789, fragment=13579)
  • 從domains字典中get獲取上次訪問時間
  • 通過ifelse計算還需等待的時間並睡眠(time.sleep) -一次訪問後保存本次訪問到domains字典中

爬蟲通用類封裝run方法

執行流程

  • 判斷隊列中是否存在待爬取的網址
  • 判斷爬取網址是否遵守robots約定,不遵守則跳出禁止下載
  • 對當前爬取網址限流
  • 獲取當前的訪問深度
  • 判斷當前訪問深度是否在規定範圍內
  • 下載爬取網址得到爬取結果
  • 判斷爬取結果是否為空
  • 爬取結果不為空則保存
  • 獲取出爬取結果裏的所有鏈接
  • 使用過濾器篩選出指點關鍵字的鏈接
  • for循環補全每個鏈接
  • 補全同時判斷鏈接是否訪問過
  • 未被訪問過則深度加1
  • 並加入隊列

爬蟲——綜合案例流程版