1. 程式人生 > >(一)爬蟲之網頁下載

(一)爬蟲之網頁下載

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)