Python爬蟲入門 (看這篇就夠了)

  • 2020 年 3 月 17 日
  • 筆記

1、什麼是爬蟲

「爬蟲」是一種形象的說法。互聯網比喻成一張大網,爬蟲是一個程式或腳本在這種大網上爬走。碰到蟲子(資源),若是所需的資源就獲取或下載下來。這個資源通常是網頁、文件等等。可以通過該資源裡面的url鏈接,順藤摸瓜繼續爬取這些鏈接的資源。

你也可以把爬蟲當作模擬我們正常上網。打開網頁並分析網頁的內容獲取我們想要的東西。

那麼,這裡就涉及到http傳輸協議等相關的知識。

我們通常打開一個網頁,基本上都是打開一個Url鏈接即可。在這個過程當中,實際上發生了很多事情。

打開一個Url鏈接,瀏覽器自動向Url鏈接的伺服器發送一個請求(Request),告訴伺服器說我需要訪問這個Url鏈接的內容,請返回數據給我。伺服器就處理該請求,響應該請求並返回結果給瀏覽器。

既然爬蟲需要模擬該過程。根據http協議,爬蟲需要構造一個請求(Request),發到請求到目標伺服器(通常是Url鏈接)。然後等待伺服器的響應(Response)。

所有相關的數據都在這個響應結果當中,這個就是爬蟲實現的基本邏輯。

2、urllib2實現GET請求

GET和POST是請求中最常見的兩種方式。(一共有6種)

GET方式是通過Url鏈接的方式傳輸相關的參數或數據。一般打開網址是GET方式請求,例如打開百度首頁、Google首頁。

有時候,需要向這個鏈接傳輸一些參數。

例如我在百度搜索一個詞,發現鏈接變成 https://www.baidu.com/s?ie=UTF-8&wd=測試

這裡有個?問號以及後面一堆數據。問號後面的數據是GET請求的參數,這裡一共有兩組參數。

1)ie = UTF-8

2)wd = 測試

每組參數用&符號鏈接。在參數中,等號前面的是參數名;等號後面的是參數值。

例如第2組參數的含義是百度搜索關鍵字為「測試」。第1組參數是設置返回ie瀏覽器的編碼格式,可有可無,作為說明加入進來。

那麼,我使用urllib2模擬百度搜索程式碼如下:

#coding:utf-8import urllib, urllib2 #前半部分的鏈接(注意是http,不是https)url_pre = 'http://www.baidu.com/s' #GET參數params = {}params['wd'] = u'測試'.encode('utf-8')url_params = urllib.urlencode(params) #GET請求完整鏈接url = '%s?%s' % (url_pre, url_params) #打開鏈接,獲取響應response = urllib2.urlopen(url) #獲取響應的htmlhtml = response.read() #將html保存到文件with open('test.txt', 'w') as f:    f.write(html)

執行程式碼,可以看到爬取的內容。

5、反爬蟲設置header

有些伺服器為了避免被爬蟲,會檢查header。header是發送請求的時候,一起發送給伺服器的數據。可以通過header得到瀏覽器的類型,手機端還是電腦端訪問,以及從什麼地方進入該鏈接等等。

若發現不是正常瀏覽器訪問,伺服器則直接拒絕。

so~ 我們需要進一步模擬瀏覽器的行為,需要模擬設置header。

#coding:utf-8import urllib, urllib2   #設置headeruser_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'  headers = {'User-Agent':user_agent}  #構造Request請求,其中第二個參數是dataurl = 'http://www.server.com/login'request = urllib2.Request(url, None, headers) #響應請求response = urllib2.urlopen(request)  html = response.read()

同樣,若你不知道如何設置header,可以通過抓包軟體獲取,例如Fiddler。

6、解析html

前面說了這麼多,都是為了獲取網頁內容html。既然獲取到html之後,我們解析?從中提取我們需要的數據?

我們所獲取的html本質是字元串。處理字元串最基本的方法是通過相關的字元串函數,但效率很低,容易出錯。

還可以使用正則表達式處理字元串。這部分的知識也是很多,大家可以自行了解。

這裡,我想給大家說的處理方式是使用BeautifulSoup。

BeautifulSoup是解析html/xml的庫。非Python自帶的庫,安裝如下:

pip install beautifulsoup4pip install lxml

安裝lxml庫是為了加快html解析效率。

先我們設置1個html內容,使用BeautifulSoup解析方法如下:

#coding:utf-8from bs4 import BeautifulSoup #先隨便假設一個htmlhtml = '''<html><head></head><body>    <p id="test_p">test1</p>    <p>test2</p></body><html>''' #使用lxml解析htmlsoup = BeautifulSoup(html, 'lxml')

soup是解析得到的解析器。我們可以根據html的結構獲取對應的節點。例如我想獲取p標籤:

p = soup.body.p

但該方法只能獲取到第1個節點。假如body標籤下有很多p節點,該方法無法獲取全部。

這裡,我們可以用find_all或select方法獲取。建議大家使用select方法,這個方法可以jQuery選擇器用法差不多。例如:

p1 = soup.select('p') #獲取p標籤p2 = soup.select('#test_p') #獲取id為test_p的標籤p3 = soup.select('.test')   #獲取class為test的標籤p4 = soup.select('body .test') #獲取body下的class為test的標籤

來個完整的程式碼,輸出結果:

#coding:utf-8from bs4 import BeautifulSoup #先隨便假設一個htmlhtml = '''<html><head></head><body>    <p id="test_p">test1</p>    <p>test2</p></body><html>''' #使用lxml解析htmlsoup = BeautifulSoup(html, 'lxml') #獲取全部p標籤for p in soup.select('p'):    print(p)

通過該方法,可以輸出全部p標籤。

那假如我要獲取p標籤的屬性和數據呢?方法如下:

for p in soup.select('p'):    print(p.name) #標籤名稱        #標籤屬性,也可以用p['id']。若屬性不存在會報錯,和字典獲取鍵值類似    print(p.get('id'))     print(p.string) #標籤內容

若一個標籤裡面有很多子標籤,你可以再進一步繼續使用select。

若想獲取標籤下全部子標籤的文本內容。可以用strings屬性得到一個生成器,不過可能有很多回車和空格。若想屏蔽回車和空格,可以使用stripped_strings屬性。如下所示:

print(''.join(soup.body.strings))print(''.join(soup.body.stripped_strings))

將分別得到:

u'ntest1ntest2n'u'test1test2'