1. 程式人生 > >python爬蟲進階(一):靜態網頁爬取

python爬蟲進階(一):靜態網頁爬取

一、文章說明

本文是在學習過程中的筆記分享,開發環境是win7,Python3,編輯器pycharm,文章中若有錯誤歡迎指出、積極討論。

另外,推薦一個比較好的爬蟲教程

二、課程基礎

1、HTML和CSS

爬蟲和網頁內容處處打交道,首先要掌握一部分前端內容。參考教程:

2、xpath解析網頁

掌握了上面的知識,下面就可以開始下一步學習了。如何解析網頁?這裡我推薦BeautifulSoupxpath,掌握了這兩種解析方法基本上就夠了,當然,還有一種必須掌握:正則表示式,有點簡單粗暴,但屢試不爽吐舌頭

3、http響應狀態

2xx:成功

3xx:調轉

4xx:客戶端錯誤

5xx:伺服器錯誤

三、爬取過程的選擇策略

一般我們爬取都有一個明確的目標,如知道要爬那些網頁、網頁上的那些內容、需要爬多少等。但是當我們要對一個網站進行無腦爬取時,應綜合考慮如下策略:

1、重要的網頁距離種子站點比較近

2、深度有限,一般17層,再往深處爬無意義

3、寬度優先有利於多爬蟲並行爬取

4、深度限制與寬度優先相結合

四、如何記錄爬取歷史,不重複抓取?

1、將URL經過MD5或SHA-1等單向雜湊後再儲存到hashset或資料庫,這樣每一個URL儲存下來就只佔16個位元組。

2、Bit-Map方法。建立一個BitSet,將每個URL經過一個雜湊函式對映到某一位,只佔1位元組。

技巧:看一個站點有多少資訊,以便於我們估計記憶體消耗

百度:site:www.mafengwo.cn

我們可以看到螞蜂窩有多少個網頁。

同樣,Google:    site:www.mafengwo.cn ,更厲害的是Google能看到種子站點下一個站點的網頁資訊:

site:www.mafengwo.cn/gonglve/

3、BitMap方式記錄

pip install bitarray

pip install mmh3

>>> from bitarray import bitarray
>>> import mmh3
>>> a = 2**31
>>> a
2147483648
>>> offset = 2147483647
>>> offset = 2147483647//2**31-1
>>> bit_array = bitarray(4*1024*1024*1023)
>>> #分配4G記憶體
>>> bit_array.setall(0)#記憶體位置初始化為0
>>> b1 = mmh3.hash('www.baidu.com',42)+offset#42是固定設定,offset將偏置設為0,索引從0開始,b1返回int型別
>>> bit_array[b1] = 1#值為0或1,如果該位置沒有佔用,就按照預設0,如果佔用就是1

4、Bloom Filter 演算法

pip install pybloom

>>> import pybloom
>>> fruit = pybloom.BloomFilter(100000,0.1)#0.1 容錯率
>>> fruit.update('apple')
Traceback (most recent call last):
  File "<pyshell#44>", line 1, in <module>
    fruit.update('apple')
AttributeError: 'BloomFilter' object has no attribute 'update'
>>> fruit.add('apple')
False
>>> len(fruit)#fruit包含的元素個數
1
>>> fruit.add('pear','orange','apple')
Traceback (most recent call last):
  File "<pyshell#47>", line 1, in <module>
    fruit.add('pear','orange','apple')
TypeError: add() takes from 2 to 3 positional arguments but 4 were given
>>> fruit.union('pear','orange','apple')
Traceback (most recent call last):
  File "<pyshell#48>", line 1, in <module>
    fruit.union('pear','orange','apple')
TypeError: union() takes 2 positional arguments but 4 were given
>>> fruit.add('pear')#只能新增一個??如果fruit內不包含返回False,反之,True
False
>>> fruit.add('orange')
False
>>> fruit.add('apple')
True
>>> len(fruit)
3
>>> 'mike' in fruit
False
>>> 'apple' in fruit
True
>>> 
技巧:在某些網站robots.txt頁面下有該網站的所有網頁資訊 www.xxxxxxxxx.xml

sitemap: 

五、實戰案例

僅做測試:

獲取螞蜂窩城市遊記

程式碼:

環境:win7,Python3,pycharm

import urllib.request
import http.client
import re
from pybloom import BloomFilter
import os

request_headers = {
    'host': "www.mafengwo.cn",
    'connection': "keep-alive",
    'cache-control': "no-cache",
    'upgrade-insecure-requests': "1",
    'user-agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36",
    'accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    'accept-language': "zh-CN,en-US;q=0.8,en;q=0.6"
}

def get_html(url):
    req = urllib.request.Request(url, headers=request_headers)
    response = urllib.request.urlopen(req)
    html = response.read()
    
    return html


def download_city_notes(id):
    for i in range(1, 999):
        url = 'http://www.mafengwo.cn/yj/%s/1-0-%d.html' % (id, i)
        if url in download_bf:
            continue
        print ('open url %s' %url)
        download_bf.add(url)
        html = get_html(url)
        htmlcontent = html.decode('utf-8')

        city_notes = re.findall('href="/i/\d{7}.html', htmlcontent)

        # 如果導航頁錯誤,該頁的遊記數為0,則意味著 1-0-xxx.html 已經遍歷完,結束這個城市
        if len(city_notes) == 0:
            return
        for city_note in city_notes:
            try:
                city_url = 'http://www.mafengwo.cn%s' % (city_note[6:])
                if city_url in download_bf:
                    continue
                print ('download %s' % (city_url))
                html = get_html(city_url)
                filename = city_url[7:].replace('/', '_')
                fo = open("%s%s" % (dirname, filename), 'wb+')
                fo.write(html)
                fo.close()
                download_bf.add(city_url)
            except Exception as Arguments:
                print (Arguments)
                continue


#global

city_home_pages = []
city_ids = []
dirname = 'mafengwo_notes/'

# 建立 Bloom Filter
download_bf = BloomFilter(1024 * 1024 * 16, 0.01)


def main():
    # 檢查用於儲存網頁資料夾是否存在,不存在則建立
    if not os.path.exists(dirname):
        os.makedirs(dirname)

    try:
        # 下載目的地的首頁
        mdd_url = 'http://www.mafengwo.cn/mdd/'
        html = get_html(mdd_url)
        htmlcontent = html.decode('utf-8')       #正則表示式匹配時需要解碼

        # 利用正則表示式,找出所有的城市主頁
        city_home_pages = re.findall('/travel-scenic-spot/mafengwo/\d{5}.html', htmlcontent)
        # 通過迴圈,依次下載每個城市下的所有遊記
        for city in city_home_pages:
            city_ids.append(city[29:34])
            download_city_notes(city[29:34])
    except urllib.request.HTTPError as Arguments:
        print (Arguments)
    except http.client.BadStatusLine:
        print ('BadStatusLine')
    except Exception as Arguments:
        print (Arguments)



if __name__ == '__main__':
    main()


好的!!暫時就這麼多了微笑微笑

第一次將筆記寫在CSDN上,太難寫了,主要太浪費時間抓狂抓狂還要寫得好看吐舌頭

希望堅持,將整個課程寫完