爬蟲——綜合案例流程版
阿新 • • 發佈:2018-09-26
定義 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
- 並加入隊列
爬蟲——綜合案例流程版