1. re模塊
之前我們在python基礎中介紹過正則表達式,而re模塊可以使用正則表達式對字符串進行很好的篩選。re模塊的使用可以分為兩種:第一種是對象式的方式,第二種是函數式的方式。之前已經介紹過正則模塊的簡單使用,我們在這里就直接進行案例操作。
案例:表情包爬取
將此頁面下的前十頁圖片全部獲取下來:https://fabiaoqing.com/biaoqing
通過網頁分析發現每一個圖片的地址都被放在了該標簽下。
在瀏覽器地址欄中輸入該圖片的地址就可以找到這個圖片。現在圖片已經找到了,下一步就是對圖片進行下載。那么如何通過代碼去實現呢?
url='https://fabiaoqing.com/biaoqing/lists/page/6.html'
resp=requests.get(url).text
print(resp)
通過剛開始打印的控制台的內容發現瀏覽器中的代碼結構與控制台打印的並不太一樣。因此我們在網頁源代碼中查看
我們會發現每個圖片的 URL 地址都在 data-original這個標簽中,因此我們對該屬性中的內容進行正則匹配,正則匹配規則為<img class="ui image lazy" data-original="(.*?)" 通過re.findall()來獲得匹配到的內容,括號內的參數主要有三個,第一個是正則表達式,第二個是需要匹配的內容,第三個是匹配規則,在這里我們一般只需要記住第一個和第二個就可以了。
resp=requests.get(url)
img_src=re.findall('<img class="ui image lazy" data-original="(.*?)"',resp.text,re.S)
匹配到的內容是一個列表,再遍歷這個列表,依次對列表中的圖片地址發送請求,因為是圖片,所以它是二進制的形式,因此我們以二進制的形式進行保存,具體的完整代碼請看文末附錄一:
for src in img_src:
src_filename=src.rsplit('/')[-1]
img_content=requests.get(src)
with open(f'表情包\\第{page}頁\\{src_filename}',mode='wb') as f:
f.write(img_content.content)
re模塊對於新手來說,我們只需要記住表達式 .* ? 就可以了,將需要匹配的內容以 .* ?的形式,re模塊就會進行貪婪匹配。如果對於re模塊想要深入了解的話大家可以對正則表達式那一部分的知識點進行深一步的了解。
2. css選擇器
在學習css選擇器之前還需要安裝一個對應的庫 parsel。這個庫內置了很多的數據解析方式,比如常見的css解析、xpath解析以及re模塊都是內置的,使用起來十分方便。
pip install parsel
如果學過前端三件套的話,那么對於css選擇器的理解與使用應該是非常容易的。所謂css選擇器,其實就是當我們在寫css樣式時需要對某一個具體的標簽進行的定位。在使用css選擇器之前需要先創建一個selector對象,所有的內容都以selector的形式來讀取,如果想要獲取具體的標簽內容或者是屬性內容,則需要特定的語法。
2.1 標簽選擇器
標簽選擇器就類似於js中的 getElementbyTagName()一樣,通過直接對標簽定位來對同一類型的標簽加樣式。常見的標簽有 < div > 、< span >、< p > 、< a > 等。通過直接綁定標簽名稱來獲取標簽。
import parsel
selector = parsel .selector(html)
span = selector.css( ' span ' ).getall()
print(span)
2.2 類選擇器
類選擇器就是通過標簽中設定的class類來進行定位,定位到具有該類屬性的標簽,之后在進行獲取。類的方式與css中的方式相同。
import parsel
selector = parsel .selector(html)
top = selector.css( '.top' ).getall()
print(top)
2.3 ID選擇器
ID選擇器在平時的使用中使用的比較多,因為同一個html文件中,id屬性是唯一的,每一個id都對應一個標簽,因此如果當獲取的對象是唯一的,那么就可以使用id選擇器。
import parse1
selector = parsel.selector(html)
p = selector.css ( ' #content ' ).geta11()
print(p)
2.4 組合選擇器
在css進行樣式修改時,如果權重不夠,可以多個組合進行權重的增加,以此來讓樣式起到效果。
import parse1
selector = parsel.selector(html)
content = selector.css ( 'div .top' ).geta11()
print(content)
2.5 偽類選擇器
偽類選擇器在css樣式的時候可能會多一點,但在爬蟲中使用的很少,幾乎沒有,所以我們只作為了解內容即可。
import parsel
selector = parse1.selector(htm1)
p = selector.css( 'p:nth-child(2) ').getall()
print(p)
2.6 屬性提取器
大多數情況我們在使用css選擇器的時候需要提取的是文本內容,比如標簽中包含的文本或者是屬性的內容,比如src屬性中的url地址。而且屬性提取器的使用也非常的方便與簡潔。其中獲取標簽中包含的文本內容的語法是" p: :text ",使用兩個冒號來作為區分,而獲取標簽的屬性內容則為" p: :attr(href)" 括號中是需要獲取的標簽的屬性。
import parse1
selector = parse1.selector(htm1)
# p::text是獲取p標簽的文本內容
p = selector.css( 'p: :text' ).geta11()
print(p)
# a::attr(href)是獲取a標簽的href屬性
a = selector.css( 'a: :attr(href) ' ).getall()
print(a)
2.7 案例
上面已經介紹過css選擇器的具體用法,下面讓我們來給大家布置一個小任務:
使用 css 選擇器將豆瓣 250 的全部電影信息全部提取出來。
title(電影名)、 info(導演、主演、出版時間)、 score(評分)、follow(評價人數)
目標網址:https://movie.douban.com/top250
進行操作之前我們還是要對網站進行分析,先要確定所要獲取的內容是否直接在網頁上,然后再確定它的請求方式。之后才正式開始進行數據獲取。通過分析發現,索要獲取的內容都在網頁上,那我們直接獲取就可以了。
因為豆瓣網站有相對的反扒機制,所以需要我們添加一些請求參數,需要將headers以及cookies都添加上才能獲取到數據
url = f'https://movie.douban.com/top250?start={page}&filter='
headers = {
'Cookie': 'bid=nJ5mbBL3XUQ; __gads=ID=d4e33a95e1a5dad6-',
'Host': 'movie.douban.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36',
}
# 2. 發送請求
response = requests.get(url=url, headers=headers)
html_data = response.text
print(html_data)
通過控制台打印的內容比對發現我們需要獲取的內容確實就在里面,因此從獲取到的內容中進行篩選,使用css選擇器進行選擇。
我們可以先獲取所有的 li 標簽,然后再遍歷每個li標簽,從中獲取我們想要的相關信息。
selector = parsel.Selector(html_data)
lis = selector.css('.grid_view li') # 所有li標簽
for li in lis:
title = li.css('.title::text').get() //獲取title文本
info = li.css('.bd p:nth-child(1)::text').getall()
score = li.css('.rating_num::text').get()
follow = li.css('.star span:nth-child(4)::text').get()
print(title, info, score, follow)
通過運行代碼就可以發現我們已經將數據獲取到了,對於獲取的數據只需要做一些處理就是我們需要的數據。需要完整代碼請直接到文末附錄二。
3. xpath數據解析
3.1 認識xpath
XPath是一門再HTML\XML文檔中查找信息的語言,可用在HTML\XML文檔中對元素和屬性進行遍歷
對於一些xml文件的內容和節點的遍歷,我們可以使用xpath來獲取。xml文件被用來傳輸和存儲數據,也是由成對的標簽組成,但標簽是自定義的。xml主體呈現樹狀結構,跟html類似,也是有父子關系的標簽,而節點與節點之間的關系就類似於我們人類生活中的族譜,有親兄弟關系、父子關系、同級關系等。
3.2 xpath的語法
xpath中的常用語法主要就如下這些,掌握了這些對我們日常爬蟲也是足夠了。在使用時與主要理清節點與節點之間的層級關系就可以了。
3.3 案例
目標網址: https://nba.hupu.com/stats/players/pts
需求:
1、用xpath采集 nba 球員數據
2、采集以下信息
rank # 排名
player # 球員
team # 球隊
score # 得分
hit_shot # 命中-出手
hit_rate # 命中率
hit_three # 命中-三分
three_rate # 三分命中率
hit_penalty # 命中-罰球
penalty_rate # 罰球命中率
session # 場次
playing_time # 上場時間
做完准備工作后我們開始對該網站的數據進行獲取。每一個球員的數據都放在一個< tr >標簽里面,因此我們可以先獲取所有的 < tr > 標簽,然后再遍歷每一個標簽,並從中獲取相關的內容。
selector = parsel.Selector(html_data)
trs = selector.xpath('//table[@class="players_table"]/tbody/tr')[1:] # 二次提取
獲取到所有的tr之后再遍歷每一個tr,從中獲取球員的各項數據
for tr in trs:
rank = tr.xpath('./td[1]/text()').get() # 排名
player = tr.xpath('./td[2]/a/text()').get() # 球員
team = tr.xpath('./td[3]/a/text()').get() # 球隊
score = tr.xpath('./td[4]/text()').get() # 得分
hit_shot = tr.xpath('./td[5]/text()').get() # 命中-出手
hit_rate = tr.xpath('./td[6]/text()').get() # 命中率
hit_three = tr.xpath('./td[7]/text()').get() # 命中-三分
three_rate = tr.xpath('./td[8]/text()').get() # 三分命中率
hit_penalty = tr.xpath('./td[9]/text()').get() # 命中-罰球
penalty_rate = tr.xpath('./td[10]/text()').get() # 罰球命中率
session = tr.xpath('./td[11]/text()').get() # 場次
playing_time = tr.xpath('./td[12]/text()').get() # 上場時間
最后再將結果打印出來:
完整代碼放在文末附錄三。
4.總結
數據解析的內容到現在也已經介紹完畢。我介紹的只是常用的數據解析方法,當然還有很多數據解析的方法,比如Bautifulsoup、PyQuery……但是我上面介紹的這些已經完全足夠了。每一種都能夠獲取想要的數據,只不過獲取的方式有所不同。具體使用哪種方式取決於所要爬取的網頁結構。這里需要注意的是我們在安裝了parsel庫之后就不需要另外再安裝css以及xpath庫了,在這里就可以直接構建selector對象並使用,還是十分方便的。如果大家對於爬蟲有需求或者是有疑惑可以聯系我。下一次給大家介紹數據持久化的方式,也就是我們常說的將數據保存為xlsx、csv、txt……等的一些格式,而不單單是打印出來,以便我們后續對數據進行操作。
附錄一:
import requests
import re
for page in range(1, 11):
print(f'----正在抓取第{page}頁數據---')
response = requests.get(f'https://fabiaoqing.com/biaoqing/lists/page/{page}.html')
html_data = response.text
# print(html_data)
# 數據解析
"""
<img class="ui image lazy" data-original="(.*?)"
"""
result_list = re.findall('<img class="ui image lazy" data-original="(.*?)"', html_data, re.S)
# print(result_list)
for result in result_list:
img_data = requests.get(result).content # 請求到的圖片數據
# 准備文件名
file_name = result.split('/')[-1]
with open('img\\' + file_name, mode='wb') as f:
f.write(img_data)
print('保存完成:', file_name)
附錄二:
import parsel
import requests
for page in range(0, 226, 25):
print('-' * 100)
url = f'https://movie.douban.com/top250?start={page}&filter='
headers = {
'Host': 'movie.douban.com',
'Cookie': 'bid=0Zh2xEHDtYQ; dbcl2="244637883:MwqForzW8Xw"; ck=5JFU; push_doumail_num=0; '
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36',
}
# 2. 發送請求
response = requests.get(url=url, headers=headers)
html_data = response.text
# 一定要對比數據,確認我們要的數據已經請求到了
selector = parsel.Selector(html_data)
lis = selector.css('.grid_view li') # 所有li標簽
for li in lis:
title = li.css('.title::text').get()
info = li.css('.bd p:nth-child(1)::text').getall()
score = li.css('.rating_num::text').get()
follow = li.css('.star span:nth-child(4)::text').get()
print(title, info, score, follow)
附錄三:
import requests
import parsel
url = 'https://nba.hupu.com/stats/players/pts'
# 2. 發送網絡請求
response = requests.get(url=url)
html_data = response.text
# print(html_data) # 通過數據對比確認了我想要的數據已經請求下來了
# 3.1 轉換數據類型
selector = parsel.Selector(html_data)
trs = selector.xpath('//table[@class="players_table"]/tbody/tr')[1:] # 二次提取
for tr in trs:
rank = tr.xpath('./td[1]/text()').get() # 排名
player = tr.xpath('./td[2]/a/text()').get() # 球員
team = tr.xpath('./td[3]/a/text()').get() # 球隊
score = tr.xpath('./td[4]/text()').get() # 得分
hit_shot = tr.xpath('./td[5]/text()').get() # 命中-出手
hit_rate = tr.xpath('./td[6]/text()').get() # 命中率
hit_three = tr.xpath('./td[7]/text()').get() # 命中-三分
three_rate = tr.xpath('./td[8]/text()').get() # 三分命中率
hit_penalty = tr.xpath('./td[9]/text()').get() # 命中-罰球
penalty_rate = tr.xpath('./td[10]/text()').get() # 罰球命中率
session = tr.xpath('./td[11]/text()').get() # 場次
playing_time = tr.xpath('./td[12]/text()').get() # 上場時間
print(rank, player, team, score, hit_shot, hit_rate,
hit_three, three_rate, hit_penalty, penalty_rate,
session, playing_time, sep=' | ')
# 4. 數據持久化