1. 程式人生 > >2.3 基於寬度優先搜索的網頁爬蟲原理講解

2.3 基於寬度優先搜索的網頁爬蟲原理講解

什麽 每一個 empty 目錄 except open 要求 and ref

上一節我們下載並使用了寬度優先的爬蟲,這一節我們來具體看一下這個爬蟲的原理。

首先,查看HTML.py的源代碼。

第一個函數:

def get_html(url):
    try:
        par = urlparse(url)
        Default_Header = {X-Requested-With: XMLHttpRequest,
                          Referer: par[0] + :// + par[1],
                          User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36
, Host: par[1]} html = requests.get(url, headers=Default_Header, timeout=10) if html.status_code != 200: return None return html.content except Exception as e: print(e) return None

這個函數的作用是抓取url的內容(二進制內容,可以直接傳進beautifulsoup裏分析)。之所以顯得比較復雜,是因為加入了一些異常處理,使得函數的可靠性更強一些。另外也加入了一些反爬蟲的考慮,盡量模擬真實的瀏覽器(Referer和User-Agent等參數)。

第二個函數:

def full_link(url1, url2, flag_site=True):
    try:
        if url2[0] == #:
            return None
        filepat = re.compile(r(.*?)\.(.*?))
        htmpat = re.compile(r(.*?)\.htm$|(.*?)\.html$|(.*?)\.php$|(.*?)\.aspx$)
        u1 = urlparse(url1)
        if filepat.match(u1.path) and
not htmpat.match(u1.path): return None if url1[-1] == /: url1 = url1+"index.html" elif filepat.match(u1.path) is None: url1 = url1+"/index.html" url2 = urljoin(url1,url2) u2 = urlparse(url2) if u1.netloc!=u2.netloc and flag_site: return None return url2 except Exception as e: print(e) return None

這個函數其實是一個很關鍵的函數。因為寬度優先要想讓while循環運轉起來,就需要對隊列的每一個元素都有一個通用的處理方法。這也是這個函數很關鍵的原因。它的作用是對於已知url1頁面中,有一個<a>標簽的href屬性裏面是url2,返回url2的真正完整鏈接是什麽。當然,如果url2本身是一個完整鏈接,就直接返回它本身。但是如果它只是一個相對路徑的鏈接,就需要經過處理之後再返回。比如,http://www.cnblogs.com/itlqs頁面裏面,鏈接出了一個./p/136810721.html,那麽經過這個函數處理之後,返回的就是http://www.cnblogs.com/itlqs/p/6810721.html。其實Python自帶的urljoin函數做的就是這個事情,但是經過試驗發現內置的這個不是很完善,所以在這裏修改了一下。在遇到異常,或者鏈接不符合要求的時候會返回None。

第三個函數:

def premake(url):  # 建立url所需要的目錄
    if url[-1] == /:
        url = url[:-1]
    up = urlparse(url)
    pat = re.compile(r(.*?)\.htm$|(.*?)\.html$|(.*?)\.php$|(.*?)\.aspx$)
    path = up.path.split(/)
    name = index.html
    if pat.match(up.path) is not None:
        name = path[-1]
        path = path[:-1]
    dirn = /.join(path)
    if up.query!=‘‘:
        name = up.query+ - +name
    os.makedirs(up.netloc + dirn, exist_ok=True)
    return up.netloc + dirn + / + name

這個函數的作用是建立url所需要的本地文件夾,這一步主要是為了在本地也保存原始的目錄結構。而且把query的信息也在文件名中體現出來了。

第四個函數:

def save(url):
    url = url.replace(\n,‘‘)
    fn = premake(url)
    html = get_html(url)
    if html is not None:
        with open(fn, wb) as f:
            f.write(html)
    return html

把一個鏈接抓取並保存到本地。就是在前面三個函數的基礎上寫的。

這就是HTML.py。下面再來看一下crawler.py。前面的一些設置參數的部分就不看了,直接看寬搜的核心代碼。

now = 0
while not q.empty():
    try:
        front = q.get()
        link = front[0]
        depth = front[1]
        print(crawling:, link)
        html = HTML.save(link)
        if html is None:
            continue
        soup = BeautifulSoup(html, html.parser, from_encoding=gb18030)
        for a in soup.find_all(a):
            try:
                url2 = a[href]
                fl = HTML.full_link(link, url2, flag_site)
                if fl is None:
                    continue
                if (fl not in pool) and (depth + 1 <= flag_depth):
                    pool.add(fl)
                    q.put((fl, depth + 1))
                    print(in queue:, fl)
            except Exception as e:
                print(e)
        now += 1
        if now >= flag_most:
            break
    except Exception as e:
        print(e)

其實有了上面四個函數作為基礎,就很容易了。每次從隊頭取一個鏈接。抓取並保存。然後提取出這個頁面的所有href,然後用full_link函數得到完整鏈接,判斷一下是否已經出現過,如果沒有,加入隊列中。

這就是這個程序的原理,一些實現細節可以揣摩一下代碼,當然代碼也有可能有不完善的地方,但是對於一些簡單的抓取需求來說基本夠用了。

2.3 基於寬度優先搜索的網頁爬蟲原理講解