(一)爬蟲之網頁下載
1,相關知識
robots.txt: 一些網站會定義robots.txt檔案(https://www.example.com/robots.txt),規定了網頁爬取的相關限制,檢視其內容,遵守規則可以避免過早IP被封。
下面為知乎robots.txt部分內容(https://www.zhihu.com/robots.txt)。(disallow 表示不允許爬取的url;Crawl-delay:10,表示兩次抓取之間需要10秒延遲)
sitemap:有的robots.txt的html原始碼中會給出網站的sitemap,獲得網站的sitemap,可以瞭解網站整體架構和各url路徑格式。
網站大小估計: 利用谷歌搜尋 site:example.com,根據顯示結果估計。如下圖23條結果,說明該域名下大概有23個子網頁。
識別網站所用技術:利用python第三方模組builtwith能夠返回網站使用相關技術。(安裝:pip install builtwith)
下圖檢視知乎使用的技術:builtwith.parse('https://www.zhihu.com')
檢視網站所有者:利用python第三方模組python-whois, 返回伺服器,郵箱等相關資訊。(pip install python-whois)
使用:whois.whois('https://www.zhihu.com')
2.網頁下載器和url佇列
網頁下載器:應該支援重試下載,使用者代理(user-agent),proxy代理等。程式碼如下:
def download(url,user_agent=None,proxies=None,num_retries=3): #支援user-agent和proxy #proxies = {"http": "http://10.10.1.10:3128", "https": "http://10.10.1.10:1080",} response=requests.get(url,headers={'User-Agent':user_agent},proxies=proxies) if response.status_code and 500<=response.status_code<600: # 出現伺服器端錯誤時重試三次 if num_retries > 0: response = download(url,user_agent,proxies,num_retries-1) return response
url佇列:管理需要下載的url。即從下載的網頁中提取出有用的url加入佇列,從佇列中取出下一個url進行爬取。需要實現url去重,下載延遲等。程式碼如下:
#coding:utf-8 import requests import re import urlparse from datetime import datetime import time def link_carwl(start_url,link_regex,max_depth): #link_regex 正則表示式,提取感興趣的url url_queue = [start_url] seen = set(url_queue) while url_queue: url = url_queue.pop() throttle =Throttle(3) #相同域名延遲3秒訪問 throttle.wait(url) response = download(url) for link in get_links(response.text): if re.match(link_regex,link): #urlparse.urljoin(url,link) #link可能為相對路徑 if link not in seen: #不訪問重複的url seen.add(url) url_queue.append(link) #url提取 def get_links(html): webpage_regex = re.compile('<a[^>]+href=["\'](.*?)["\']',re.IGNORECASE) #["\']匹配單引號或雙引號 return webpage_regex.findall(html) #同一個域名的下載延遲 class Throttle(object): def __init__(self,delay): self.delay = delay self.domains={} def wait(self,url): domain = urlparse.urlparse(url).netloc #提取網址的域名 last_accessed = self.domains.get(domain) if self.delay>0 and last_accessed!=None: sleep_secs = self.delay-(datetime.now()-last_accessed).seconds if sleep_secs>0: time.sleep(sleep_secs) self.domains[domain]=datetime.now()
如果需要設定網頁爬取深度,對於上面的link_carwl()方法可以改進如下:
#深度設定,防止爬蟲陷阱(同一個域名下的網頁連結,一直向下訪問下去) def link_carwl(start_url,link_regex,max_depth=5): #設定最大深度為5 url_queue = [start_url] seen = {start_url:0} while url_queue: url = url_queue.pop() throttle =Throttle(3) #相同域名延遲3秒訪問 throttle.wait(url) response = download(url) depth = seen[url] if depth<max_depth: for link in get_links(response.text): if re.match(link_regex,link): #urlparse.urljoin(url,link) #link可能為相對路徑 if link not in seen: #不訪問重複的url seen[link] =depth+1 #在url的深度基礎上加一 url_queue.append(link)