實戰一
抓取您想要的網頁,並將其保存至本地計算機。
首先我們對要編寫的爬蟲程序進行簡單地分析,該程序可分為以下三個部分:
- 拼接 url 地址
- 發送請求
- 將照片保存至本地
明確邏輯后,我們就可以正式編寫爬蟲程序了。
導入所需模塊
from urllib import request, parse
拼接 URL 地址
定義 URL 變量,拼接 url 地址。代碼如下所示:
url = 'http://www.baidu.com/s?wd={}'
word = input('請輸入想要搜索的內容:')
params = parse.quote(word)
full_url = url.format(params)
向URL發送請求
發送請求主要分為以下幾個步驟:
- 創建請求對象-Request
- 獲取響應對象-urlopen
- 獲取響應內容-read
代碼如下所示:
# 重構請求頭
headers = {
'User-Agent':
'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0'
}
# 創建請求對應
req = request.Request(url=full_url, headers=headers)
# 獲取響應對象
res = request.urlopen(req)
# 獲取響應內容
html = res.read().decode('utf-8')
保存為本地文件
把爬取的照片保存至本地,此處需要使用 Python 編程的文件 IO 操作,代碼如下:
filename = word + '.html'
with open(filename, 'w', encoding='utf-8') as f:
f.write(html)
完整程序如下所示:
from urllib import request, parse
# 1.拼url地址
url = 'http://www.baidu.com/s?wd={}'
word = input('請輸入想要搜索的內容:')
params = parse.quote(word)
full_url = url.format(params)
# 2.發請求保存到本地
# 重構請求頭
headers = {
'User-Agent':
'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0'
}
# 創建請求對應
req = request.Request(url=full_url, headers=headers)
# 獲取響應對象
res = request.urlopen(req)
# 獲取響應內容
html = res.read().decode('utf-8')
# 3.保存文件至當前目錄
filename = word + '.html'
with open(filename, 'w', encoding='utf-8') as f:
f.write(html)
嘗試運行程序,並輸入 RioTianの博客園,確認搜索,然后您會在當前的工作目錄中找到“RioTianの博客園.html”文件。
函數式編程修改程序
Python 函數式編程可以讓程序的思路更加清晰、易懂。接下來,使用函數編程的思想更改上面代碼。
定義相應的函數,通過調用函數來執行爬蟲程序。修改后的代碼如下所示:
from urllib import request, parse
# 拼接URL地址
def get_url(word):
url = 'http://www.baidu.com/s?{}'
# 此處使用urlencode()進行編碼
params = parse.urlencode({'wd': word})
url = url.format(params)
return url
# 發請求,保存本地文件
def request_url(url, filename):
headers = {
'User-Agent':
'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0'
}
# 請求對象 + 響應對象 + 提取內容
req = request.Request(url=url, headers=headers)
res = request.urlopen(req)
html = res.read().decode('utf-8')
# 保存文件至本地
with open(filename, 'w', encoding='utf-8') as f:
f.write(html)
# 主程序入口
if __name__ == '__main__':
word = input('請輸入搜索內容:')
url = get_url(word)
filename = word + '.html'
request_url(url, filename)
除了使用函數式編程外,也可以使用面向對象的編程方法(實戰二),在后續內容中會做相應介紹。
實戰二
抓取百度貼吧(https://tieba.baidu.com/)頁面,比如 Python爬蟲吧、編程吧,只抓取貼吧的前 5 個頁面即可。
判斷頁面類型
通過簡單的分析可以得知,待抓取的百度貼吧頁面屬於靜態網頁,分析方法非常簡單:打開百度貼吧,搜索“Python爬蟲”,在出現的頁面中復制任意一段信息,比如“爬蟲需要 http 代理的原因”,然后點擊右鍵選擇查看源碼,並使用 Ctrl+F 快捷鍵在源碼頁面搜索剛剛復制的數據,如下所示:
由上圖可知,頁面內的所有信息都包含在源碼頁中,數據並不需要從數據庫另行加載,因此該頁面屬於靜態頁面。
尋找URL變化規律
接下來尋找要爬取頁面的 URL 規律,搜索“Python爬蟲”后,此時貼吧第一頁的的 url 如下所示:
https://tieba.baidu.com/f?ie=utf-8&kw=python爬蟲&fr=search
點擊第二頁,其 url 信息如下:
https://tieba.baidu.com/f?kw=python爬蟲&ie=utf-8&pn=50
點擊第三頁,url 信息如下:
https://tieba.baidu.com/f?kw=python爬蟲&ie=utf-8&pn=100
重新點擊第一頁,url 信息如下:
https://tieba.baidu.com/f?kw=python爬蟲&ie=utf-8&pn=0
如果還不確定,您可以繼續多瀏覽幾頁。最后您發現 url 具有兩個查詢參數,分別是 kw 和 pn,並且 pn 參數具有規律性,如下所示:
第n頁:pn=(n-1)*50
#參數params
pn=(page-1)*50
params={
'kw':name,
'pn':str(pn)
}
url 地址可以簡寫為:
https://tieba.baidu.com/f?kw=python爬蟲&pn=450
編寫爬蟲程序
下面以類的形式編寫爬蟲程序,並在類下編寫不同的功能函數,代碼如下所示:
from urllib import request,parse
import time
import random
from ua_info import ua_list #使用自定義的ua池
#定義一個爬蟲類
class TiebaSpider(object):
#初始化url屬性
def __init__(self):
self.url='http://tieba.baidu.com/f?{}'
# 1.請求函數,得到頁面,傳統三步
def get_html(self,url):
req=request.Request(url=url,headers={'User-Agent':random.choice(ua_list)})
res=request.urlopen(req)
#windows會存在亂碼問題,需要使用 gbk解碼,並使用ignore忽略不能處理的字節
#linux不會存在上述問題,可以直接使用decode('utf-8')解碼
html=res.read().decode("gbk","ignore")
return html
# 2.解析函數,此處代碼暫時省略,還沒介紹解析模塊
def parse_html(self):
pass
# 3.保存文件函數
def save_html(self,filename,html):
with open(filename,'w') as f:
f.write(html)
# 4.入口函數
def run(self):
name=input('輸入貼吧名:')
begin=int(input('輸入起始頁:'))
stop=int(input('輸入終止頁:'))
# +1 操作保證能夠取到整數
for page in range(begin,stop+1):
pn=(page-1)*50
params={
'kw':name,
'pn':str(pn)
}
#拼接URL地址
params=parse.urlencode(params)
url=self.url.format(params)
#發請求
html=self.get_html(url)
#定義路徑
filename='{}-{}頁.html'.format(name,page)
self.save_html(filename,html)
#提示
print('第%d頁抓取成功'%page)
#每爬取一個頁面隨機休眠1-2秒鍾的時間
time.sleep(random.randint(1,2))
#以腳本的形式啟動爬蟲
if __name__=='__main__':
start=time.time()
spider=TiebaSpider() #實例化一個對象spider
spider.run() #調用入口函數
end=time.time()
#查看程序執行時間
print('執行時間:%.2f'%(end-start)) #爬蟲執行時間
程序執行后,爬取的文件將會保存至 Pycharm 當前工作目錄,輸出結果:
輸入貼吧名:python爬蟲
輸入起始頁:1
輸入終止頁:2
第1頁抓取成功
第2頁抓取成功
執行時間:12.25
以面向對象方法編寫爬蟲程序時,思路簡單、邏輯清楚,非常容易理解,上述代碼主要包含了四個功能函數,它們分別負責了不同的功能,總結如下:
1) 請求函數
請求函數最終的結果是返回一個 HTML 對象,以方便后續的函數調用它。
2) 解析函數
解析函數用來解析 HTML 頁面,常用的解析模塊有正則解析模塊、bs4 解析模塊。通過分析頁面,提取出所需的數據,在后續內容會做詳細介紹。
3) 保存數據函數
該函數負責將抓取下來的數據保至數據庫中,比如 MySQL、MongoDB 等,或者將其保存為文件格式,比如 csv、txt、excel 等。
4) 入口函數
入口函數充當整個爬蟲程序的橋梁,通過調用不同的功能函數,實現數據的最終抓取。入口函數的主要任務是組織數據,比如要搜索的貼吧名、編碼 url 參數、拼接 url 地址、定義文件保存路徑。
爬蟲程序結構
用面向對象的方法編寫爬蟲程序時,邏輯結構較為固定,總結如下:
# 程序結構
class xxxSpider(object):
def __init__(self):
# 定義常用變量,比如url或計數變量等
def get_html(self):
# 獲取響應內容函數,使用隨機User-Agent
def parse_html(self):
# 使用正則表達式來解析頁面,提取數據
def write_html(self):
# 將提取的數據按要求保存,csv、MySQL數據庫等
def run(self):
# 主函數,用來控制整體邏輯
if __name__ == '__main__':
# 程序開始運行時間
spider = xxxSpider()
spider.run()
注意:掌握以上編程邏輯有助於您后續的學習。
爬蟲程序隨機休眠
在入口函數代碼中,包含了以下代碼:
# 每爬取一個頁面隨機休眠1-2秒鍾的時間
time.sleep(random.randint(1,2))
爬蟲程序訪問網站會非常快,這與正常人類的點擊行為非常不符。因此,通過隨機休眠可以使爬蟲程序模仿成人類的樣子點擊網站,從而讓網站不易察覺是爬蟲訪問網站,但這樣做的代價就是影響程序的執行效率。
聚焦爬蟲是一種執行效率較低的程序,提升其性能,是業界一直關注的問題,由此也誕生了效率較高的 Python 爬蟲框架 Scrapy。