Python 基礎教程 —— 網絡爬蟲入門篇

前言 

Python 是一種解釋型、面向對象、動態數據類型的高級程序設計語言,它由 Guido van Rossum 於 1989 年底發明,第一個公開發行版發行於 1991 年。
自面世以後,Python 深受廣大開發者的喜迎,在網站開發,網絡爬蟲,數據分析,機器學習,人工智能等領域都有其過人之處。
在「Python基礎教程「中,本人將會從各個不同領域介紹Python的用法,今天就先從最常用的網絡爬蟲開始說起。
網絡爬蟲主要目的是通過定期收集網絡的信息,把信息保存後進行分析歸類,最後通過報表顯示給相關的用戶作為業務參考。幾年我也曾經做過一個項目是對稅務局的政府網站進行信息收集,把收集到的稅務政策,各行業的稅率變動,國家頒佈的新行稅法進行分析,把分析結果綜合到財稅管理平台進行財務核算。
為了簡化流程,這次就以常用的天氣網為例子(//www.weather.com.cn/),定時收集地區的天氣情況,最後把數據作為圖表顯示。

 

目錄

一、頁面下載

二、數據讀取

三、數據保存

四、使用 matplotlib 顯示數據

五、定時器

 

一、頁面下載

這裡用到了 urllib 庫裏面的 request 類,它有兩個常用的方法:

1.  urlretrieve 用於下載網頁

1 def urlretrieve(url: str,
2       filename: Optional[str] = ...,
3       reporthook: Optional[(int, int, int) -> None] = ...,
4       data: Optional[bytes] = ...)

參數說明

url:網頁地址 url
filename:指定了保存到本地的路徑(如果未指定該參數,urllib會生成一個臨時文件來保存數據);
reporthook:是一個回調函數,當連接上服務器、以及相應的數據塊傳輸完畢的時候會觸發該回調。我們可以利用這個回調函數來顯示當前的下載進度。
data:指post到服務器的數據。該方法返回一個包含兩個元素的元組(filename, headers),filename表示保存到本地的路徑,header表示服務器的響應頭。

 

2. urlopen 可以像打開文檔一樣直接打開遠程頁面,區別在於 urlopen是只讀模式

1 def urlopen(url: Union[str, Request],
2             data: Optional[bytes] = ...,
3             timeout: Optional[float] = ...,
4             *,
5             cafile: Optional[str] = ...,
6             capath: Optional[str] = ...,
7             cadefault: bool = ...,
8             context: Optional[SSLContext] = ...)

 參數說明

 url :目標資源在網路中的位置。可以是一個表示URL的字符串,也可以是一個urllib.request對象,詳細介紹請跳轉

data:data用來指明發往服務器請求中的額外的參數信息(如:在線翻譯,在線答題等提交的內容),data默認是None,此時以GET方式發送請求;當用戶給出data參數的時候,改為POST方式發送請求。

timeout:設置網站的訪問超時時間

cafile、capath、cadefault:用於實現可信任的CA證書的HTTP請求。(基本上很少用)

context參數:實現SSL加密傳輸。

 

 1 class Weather():
 2 
 3     def __init__(self):
 4         #確定下載路徑,以日期作為文件名
 5         self.path='E:/Python_Projects/Test/weather/'
 6         self.filename=str(datetime.date.today()).replace('-','')
 7 
 8     def getPage(self,url):
 9         #下載頁面並保存
10         file=self.path+self.filename+'.html'
11         urlretrieve(url,file,None,None)

 運行方法後可以看到在文件夾里已經保存了整個靜態頁面

回到目錄

 

二、數據讀取

因為每個html頁面的數據均有不同,我們可以觀察html代碼的特徵,通過 re 的功能找到所需要的數據。
這裡介紹幾個 re 常用的方法

1、re.compile(pattern,flags = 0 )

將正則表達式模式編譯為正則表達式對象,可使用match(),search()以及下面所述的其他方法將其用於匹配

2、re.search(pattern,string,flags = 0 )

掃描字符串以查找正則表達式模式產生匹配項的第一個位置 ,然後返回相應的match對象。None如果字符串中沒有位置與模式匹配,則返回;否則返回false。請注意,這與在字符串中的某個點找到零長度匹配不同。

3、re.match(pattern,string,flags = 0 )

如果字符串開頭的零個或多個字符與正則表達式模式匹配,則返回相應的匹配對象。None如果字符串與模式不匹配,則返回;否則返回false。請注意,這與零長度匹配不同。

4、re.fullmatch(pattern,string,flags = 0 )

如果整個字符串與正則表達式模式匹配,則返回相應的match對象。None如果字符串與模式不匹配,則返回;否則返回false。請注意,這與零長度匹配不同。

5、re.split(pattern,string,maxsplit = 0,flags = 0 )

通過出現模式來拆分字符串。如果在pattern中使用了捕獲括號,那麼模式中所有組的文本也將作為結果列表的一部分返回。如果maxsplit不為零,則最多會發生maxsplit分割,並將字符串的其餘部分作為列表的最後一個元素返回。

6、re.findall(pattern,string,flags = 0 )

以string列表形式返回string中pattern的所有非重疊匹配項。從左到右掃描該字符串,並以找到的順序返回匹配項。如果該模式中存在一個或多個組,則返回一個組列表;否則,返回一個列表。如果模式包含多個組,則這將是一個元組列表。空匹配項包含在結果中。

7、re.finditer(pattern,string,flags = 0 )

返回一個迭代器,該迭代器在string類型的RE 模式的所有非重疊匹配中產生匹配對象。 從左到右掃描該字符串,並以找到的順序返回匹配項。空匹配項包含在結果中。

 

本例子比較簡單,可以看到在地區白天/夜晚的氣溫都包含在  <p class=”tem”><span>30</span><em>°C</em></p>,可以直接通過 re.compile() 找到數據。

然而在不同的頁面里,數據可能是通過後台綁定,或者在頁面渲染時綁定,這時候就需要細心地找尋數據來源,再通過鏈接獲取。

 1     def readPage(self):
 2         #讀取頁面
 3         file=open(self.path+self.filename+'.html','r',1024,'utf8')
 4         data=file.readlines()
 5         #找出當天白天溫度與晚上溫度
 6         pat=re.compile('<span>[0-9][0-9]</span>')
 7         data=re.findall(pat,str(data))
 8         file.close()
 9         #篩選溫度值,返回list
10         list1 = []
11         for weather in data:
12             w1 = weather.replace('<span>', '')
13             w2 = w1.replace('</span>', '')
14             list1.append(w2) 
15         return list1

最後返回 list 數組,其中包含當天的日間氣溫與夜間氣溫

回到目錄

 

三、數據保存

把當天日期、日間氣溫、夜間氣溫保存到數據庫

 1     def save(self,list1):
 2         #保存到數據庫
 3         db = MySQLdb.connect("localhost", "root", "********", "database", charset='utf8')
 4         cursor = db.cursor()
 5         sql = 'INSERT INTO weather(date,daytime,night) VALUES ('+self.filename+','+list1[0]+','+list1[1]+')'
 6         try:
 7             cursor.execute(sql)
 8             db.commit()
 9         except:
10             # 發生錯誤時回滾
11             db.rollback()
12         # 關閉數據庫連接
13         db.close()

 回到目錄

 

四、使用 matplotlib 顯示數據

在數據庫積累多天數據後,通過 matplotlib 庫顯示數據

 1 def display():
 2     # X軸旋轉90度
 3     plt.xticks(rotation=90)
 4     # 從數據庫中獲取數據
 5     db = MySQLdb.connect("localhost", "root", "********", "database", charset='utf8')
 6     cursor = db.cursor()
 7     sql = 'SELECT date,daytime,night FROM weather'
 8     try:
 9         cursor.execute(sql)
10         data=np.array(cursor.fetchall())
11         db.commit()
12     except:
13         # 發生錯誤時回滾
14         db.rollback()
15     #數據轉換成日期數組,白天溫度數組,夜間溫度數組
16     if len(data)!=0:
17         date=data[:,0]
18         # y軸數據需要轉化為int形式,否則將按字符串形式排列
19         daytime=(np.int16(data[:,1]))
20         night=(np.int16(data[:,2]))
21         plt.xlabel('Date')
22         plt.ylabel('Temperature')
23         plt.title('Weather')
24         # 顯示數據
25         plt.plot(date,daytime,label='day')
26         plt.plot(date,night,label='night')
27         plt.legend()
28         plt.show()

顯示結果

回到目錄

 

五、定時器

使用 Timer 定時器每天執行一次,下載數據,再刷新畫面

 1 def start():
 2     weather=Weather()
 3     weather.getPage(url)
 4     data=weather.readPage()
 5     weather.save(data)
 6     display()
 7     t = threading.Timer(86400, start)
 8     t.start()
 9 
10 url='//www.weather.com.cn/weather1d/101280101.shtml'
11 if __name__ == '__main__':
12     start()

回到目錄

全部源代碼

 1 from urllib.request import urlretrieve,urlopen
 2 from matplotlib import pyplot as plt
 3 
 4 import numpy as np,threading,re,datetime,MySQLdb
 5 
 6 class Weather():
 7 
 8     def __init__(self):
 9         #確定下載路徑,以日期作為文件名
10         self.path='E:/Python_Projects/Test/weather/'
11         self.filename=str(datetime.date.today()).replace('-','')
12 
13     def getPage(self,url):
14         #下載頁面並保存
15         file=self.path+self.filename+'.html'
16         urlretrieve(url,file,None,None)
17 
18     def readPage(self):
19         #讀取頁面
20         file=open(self.path+self.filename+'.html','r',1024,'utf8')
21         data=file.readlines()
22         #找出當天白天溫度與晚上溫度
23         pat=re.compile('<span>[0-9][0-9]</span>')
24         data=re.findall(pat,str(data))
25         file.close()
26         #篩選溫度值,返回list
27         list1 = []
28         for weather in data:
29             w1 = weather.replace('<span>', '')
30             w2 = w1.replace('</span>', '')
31             list1.append(w2)  # 保存數據
32         return list1
33 
34     def save(self,list1):
35         #保存到數據庫
36         db = MySQLdb.connect("localhost", "root", "********", "database", charset='utf8')
37         cursor = db.cursor()
38         sql = 'INSERT INTO weather(date,daytime,night) VALUES ('+self.filename+','+list1[0]+','+list1[1]+')'
39         try:
40             cursor.execute(sql)
41             db.commit()
42         except:
43             # 發生錯誤時回滾
44             db.rollback()
45         # 關閉數據庫連接
46         db.close()
47 
48 def display():
49     # X軸旋轉90度
50     plt.xticks(rotation=90)
51     # 從數據庫中獲取數據
52     db = MySQLdb.connect("localhost", "root", "********", "database", charset='utf8')
53     cursor = db.cursor()
54     sql = 'SELECT date,daytime,night FROM weather'
55     try:
56         cursor.execute(sql)
57         data=np.array(cursor.fetchall())
58         db.commit()
59     except:
60         # 發生錯誤時回滾
61         db.rollback()
62     #數據轉換成日期數組,白天溫度數組,夜間溫度數組
63     if len(data)!=0:
64         date=data[:,0]
65         # y軸數據需要轉化為int形式,否則將按字符串形式排列
66         daytime=(np.int16(data[:,1]))
67         night=(np.int16(data[:,2]))
68         plt.xlabel('Date')
69         plt.ylabel('Temperature')
70         plt.title('Weather')
71         # 顯示數據
72         plt.plot(date,daytime,label='day')
73         plt.plot(date,night,label='night')
74         plt.legend()
75         plt.show()
76 
77 def start():
78     weather=Weather()
79     weather.getPage(url)
80     data=weather.readPage()
81     weather.save(data)
82     display()
83     t = threading.Timer(86400, start)
84     t.start()
85 
86 url='//www.weather.com.cn/weather1d/101280101.shtml'
87 if __name__ == '__main__':
88     start()

View Code

 

總結

這個例子只是從最簡單的角度介紹爬蟲的使用方式,對應實際的應用場景只是冰山一角,在現實中經常還會遇到IP地址被封,數據綁定無法直接獲取,數據加密等諸多問題,在後面再作詳細介紹。
由於時間緊迫,文章中有所缺漏的地方敬請點評。

對 JAVA 開發有興趣的朋友歡迎加入QQ群:174850571 共同探討!
對 .NET  開發有興趣的朋友歡迎加入QQ群:230564952 共同探討 !

 

作者:風塵浪子

//www.cnblogs.com/leslies2/p/14719516.html

原創作品,轉載時請註明作者及出處