使用 Python 3 編寫簡單爬蟲
本文主要是學習Python3一個小階段的記錄,邊看視訊邊做的一個爬蟲——抓取主播名字和人氣然後進行降序排序,當作複習。
學習視訊:https://coding.imooc.com/class/136.html
···準備工作
1)明确目的:直播平台某個遊戲的主播和人氣
2)找到對應的頁面例如:https://www.huya.com/g/dnf
3)使用浏覽器的審查元素,檢視對應文本位置
···編碼部分
1)模拟http請求發送到伺服器,傳回html
2)使用正規表達式提取關鍵資料
3)對關鍵資料進行精煉和排序
準備工作
打開浏覽器,進入網址https://www.huya.com/g/dnf
按F12打開浏覽器的審查元素,Ctrl+B使用滑鼠選擇元素,找到對應代碼位置,如圖所示:
開始編碼
1)引入模拟http請求所需的内置子產品。
建立一個Spider類,
定義私有方法 __fetch_content(),
定義公開方法 go 作為入口方法:
from urllib import request
class Spider(): # 私有方法,模拟請求,擷取html内容
url = 'https://www.huya.com/g/dnf'
def __fetch_content(self):
r = request.urlopen(Spider.url)
html = r.read() # 這裡儲存的是位元組流檔案
html = str(htmls,encoding='UTF-8') # 全部轉換成字元串
print (html) # 輸出結果測試
return html
def go(self): # 公開的入口方法
html = self.__fetch_content() # 調用
spider = Spider()
spider.go()
2)成功擷取到html文本内容之後,找到如圖檔所示的代碼塊
可見, class=”txt” 的父标簽 span 下的兩個子标簽 span,正是需要擷取的資料。
是以正規表達式的 pattern 應該為
<span class="txt">[\s\S]*?</span>
其中,中括号裡的是比對的字元集,包含了(空白字元)
\s
(非空白字元)
\S
*
(後續内容)
這樣就可以代表比對的是所有的文本内容。而中括号後的
?
代表了非貪婪模式
意思是後續内容中比對到
就停止比對。
</span>
3)導入正則比對所需要的re子產品
定義一個分析方法 __analysis()來分析并截取對應的資料:
from urllib import request
import re
class Spider():
url = 'https://www.huya.com/g/dnf'
def __fetch_content(self):
r = request.urlopen(Spider.url)
html = r.read()
html = str(html,encoding='UTF-8')
return html
def __analysis(self,html):
root_pattern = '<span class="txt">[\s\S]*?</span>' # 正規表達式
root_html = re.findall(root_pattern,html)
print(root_html[]) # 輸出結果測試
return root_html
def go(self):
html = self.__fetch_content()
html = self.__analysis(html)
spider = Spider()
spider.go()
得到的結果是:
<span class="txt">
<span class="avatar fl">
<img data-original="https://huyaimg.msstatic.com/avatar/1069/a8/028a1c545ab90184f502cd7a9b1a2a_180_135.jpg" src="//a.msstatic.com/huya/main/assets/img/default/84x84.jpg" onerror="this.onerror=null; this.src='//a.msstatic.com/huya/main/assets/img/default/84x84.jpg';" alt="AzZ丶狂人" title="AzZ丶狂人">
<i class="nick" title="AzZ丶狂人">AzZ丶狂人</i>
</span>
發現由于使用了非貪婪模式的正規表達式,比對到第一個
</span>
隻包含了一個資料。是以需要把右邊界字元換成相對唯一的
</li>
再次運作測試,成功的包含了兩個需要的資料:
<span class="txt">
<span class="avatar fl">
<img data-original="https://huyaimg.msstatic.com/avatar/1069/a8/028a1c545ab90184f502cd7a9b1a2a_180_135.jpg" src="//a.msstatic.com/huya/main/assets/img/default/84x84.jpg" onerror="this.onerror=null; this.src='//a.msstatic.com/huya/main/assets/img/default/84x84.jpg';" alt="AzZ丶狂人" title="AzZ丶狂人">
<i class="nick" title="AzZ丶狂人">AzZ丶狂人</i>
</span>
<span class="num"><i class="num-icon"></i><i class="js-num">11.6萬</i></span>
</span>
</li>
4)對得到的結果再次進行正則比對,分别擷取主播名字和人氣:
from urllib import request
import re
class Spider():
url = 'https://www.huya.com/g/dnf'
def __fetch_content(self):
r = request.urlopen(Spider.url)
html = r.read()
html = str(html,encoding='UTF-8')
return html
def __analysis(self,html):
root_pattern = '<span class="txt">[\s\S]*?</li>'
# 下清單達式([\s\S]*?)外面還加了一層小括号,僅保留小括号裡的内容
name_pattern = '<i class="nick" title="[\s\S]*?">([\s\S]*?)</i>'
number_pattern = '<i class="js-num">([\s\S]*?)</i>'
root_html = re.findall(root_pattern,html)
anchors = [] # 建立一個list
for html in root_html: # 使用for循環,将每個結果存儲為字典,添加到list中。
name = re.findall(name_pattern, html)
number = re.findall(number_pattern, html)
anchor = {'name': name, 'number': number}
anchors.append(anchor)
print(anchors[]) # 輸出結果測試
return anchors
def go(self):
html = self.__fetch_content()
html = self.__analysis(html)
spider = Spider()
spider.go()
得到示例結果:
{'name': ['AzZ丶七天'], 'number': ['6.1萬']}
5)定義一個__refine()方法,對anchors裡的資料進行精煉,即隻保留主幹字元:
strip() 方法用于移除字元串頭尾指定的字元(預設為空格或換行符)或字元序列。
注意:該方法隻能删除開頭或是結尾的字元,不能删除中間部分的字元。
在go()方法中将傳回的結果存儲為list
from urllib import request
import re
class Spider():
url = 'https://www.huya.com/g/dnf'
def __fetch_content(self):
r = request.urlopen(Spider.url)
html = r.read()
html = str(html,encoding='UTF-8')
return html
def __analysis(self,html):
root_pattern = '<span class="txt">[\s\S]*?</li>'
name_pattern = '<i class="nick" title="[\s\S]*?">([\s\S]*?)</i>'
number_pattern = '<i class="js-num">([\s\S]*?)</i>'
root_html = re.findall(root_pattern,html)
anchors = []
for html in root_html:
name = re.findall(name_pattern, html)
number = re.findall(number_pattern, html)
anchor = {'name': name, 'number': number}
anchors.append(anchor)
return anchors
def __refine(self, anchors):
#匿名函數
f = lambda x: {
'name': x['name'][].strip(), # strip()用于移除空白字元
'number': x['number'][]
}
return map(f, anchors) # map()方法,将上述函數作用于list中的每一個元素
def go(self):
html = self.__fetch_content()
html = self.__analysis(html)
html = list(self.__refine(html)) # 将對象轉換成list
print (html[])
spider = Spider()
spider.go()
測試的輸出結果為:
{'name': 'AzZ丶七天', 'number': '5.4萬'}
6)定義一個__sort()方法,對anchors進行排序
定義一個__show()方法,将結果列印輸出:
from urllib import request
import re
class Spider():
url = 'https://www.huya.com/g/dnf'
def __fetch_content(self):
r = request.urlopen(Spider.url)
html = r.read()
html = str(html,encoding='UTF-8')
return html
def __analysis(self,html):
root_pattern = '<span class="txt">[\s\S]*?</li>'
name_pattern = '<i class="nick" title="[\s\S]*?">([\s\S]*?)</i>'
number_pattern = '<i class="js-num">([\s\S]*?)</i>'
root_html = re.findall(root_pattern,html)
anchors = []
for html in root_html:
name = re.findall(name_pattern, html)
number = re.findall(number_pattern, html)
anchor = {'name': name, 'number': number}
anchors.append(anchor)
return anchors
def __refine(self, anchors):
l = lambda x: {
'name': x['name'][].strip(),
'number': x['number'][]
}
return map(l, anchors)
def __sort(self, anchors):
# 作為key的number裡存在中文“萬”字,需要重新設定;reverse将升序改為降序
return sorted(anchors,key=self.__sort_seed,reverse=True)
def __sort_seed(self, anchors):
r = re.findall('\d*', anchors['number'])
# r在這裡是list,[0]是上面表達式比對的數字,其他則仍然是字元串
number = float(r[])
if '萬' in anchors['number']:
number *=
return number
def __show(self, anchors):
for rank in range(, range(,): #len(anchors)這裡改成輸出前五
print(rank + , anchors[rank]['name']
+ '-----' + anchors[rank]['number'])
def go(self):
html = self.__fetch_content()
anchors = self.__analysis(html)
anchors = list(self.__refine(anchors))
anchors = self.__sort(anchors)
anchors = self.__show(anchors)
spider = Spider()
spider.go()
輸出結果:
勝哥-----萬
AzZ丶小古子-----萬
AzZ丶小炜-----萬
AzZ丶仇冬生-----萬
AzZ丶傑哥助您圓夢-----萬
結語
至此,該簡單爬蟲已經基本實作。
主要是前期的網頁代碼分析,找到關鍵位置的代碼
然後就是擷取html文本,注意編碼格式
分析和截取等等是爬取後的工作
爬蟲的基本思想應該就是擷取和分析提煉
其中又分成其他細小工作