專案實戰
靜態網頁實戰
本節我們將為大家展現一個完整爬蟲的大致過程,此次專案內容為提取貓眼電影TOP100榜中的所有電影資訊並儲存至CSV檔案中,其首頁地址為http://maoyan.com/board/4,在3.2.2中我們已經獲取過第一頁中的所有電影名了,但是如何獲取第二頁、第三頁的資料呢,即獲取第二頁第三頁對應的URL,那麼我們可以在瀏覽器中不斷翻頁尋找位址列中URL的變化規律:
第二頁: http://maoyan.com/board/4?offset=10
第三頁: http://maoyan.com/board/4?offset=20
第四頁: http://maoyan.com/board/4?offset=30
......
複製程式碼
我們看見URL的改變規律就是引數offset值不斷偏移,每頁偏移的值為10,由此我們可以編寫一個獲取每頁資料函式,接收引數就是頁碼數:
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
# 偏移引數,預設為0,即為第一頁
params = {
'offset': 0
}
def get_html(page):
'''
獲取一頁html頁面
:param page: 頁數
:return: 該頁html頁面
'''
params['offset'] = page * 10
url = 'http://maoyan.com/board/4'
try:
response = requests.get(url, headers=headers, params=params)
if response.status_code == 200:
html = response.text
return html
else:
return -1
except:
return None
複製程式碼
當我們獲取到html頁面後,就可以提取相應的電影資訊了,比如榜單張每一項電影都會有的屬性:電影名稱,主演,上映時間,評分等資訊。提取資訊有多種方式,下面我們利用正規表示式提取電影資訊:
def parse_infor(html):
'''
提取html頁面中的電影資訊
:param html: html頁面
:return: 電影資訊列表
'''
# 編寫正則字串規則,提取 電影名,主演,上映時間,評分資訊
pat = re.compile('<div class="movie-item-info">.*?<p.*?><a.*?>(.*?)</a></p>.*?<p.*?>(.*?)</p>.*?<p.*?>(.*?)</p>.*?</div>.*?<div.*?>.*?<p.*?><i.*?>(.*?)</i><i.*?>(.*?)</i></p>.*?</div>.*?</div>.*?</div>', re.S)
# 得到一個二重列表
results = re.findall(pat, html)
one_page_film = []
if results:
for result in results:
film_dict = {}
# 獲取電影名資訊
film_dict['name'] = result[0]
# 獲取主演資訊
start = result[1]
# 替換字串中的 '\n' 字元,即換行字元
start.replace('\n', '')
# 去掉字串兩邊的空格,並使用切片去除字串開頭的'主演:'三個字元
start = start.strip()[3:]
film_dict['start'] = start
# 獲取上映時間資訊
releasetime = result[2]
# 使用切片去除字串開頭的'上映時間:'五個字元
releasetime = releasetime[5:]
film_dict['releasetime'] = releasetime
# 獲取評分資訊,由於評分是有兩個字元拼接的,這裡我們提取後也需要進行拼接操作
left_half =result[3]
right_half = result[4]
score = left_half + right_half
film_dict['score'] = score
# 列印該電影資訊:
print(film_dict)
# 將該電影資訊字典存入一頁電影列表中
one_page_film.append(film_dict)
return one_page_film
else:
return None
複製程式碼
不熟悉正則讀者要好好複習下前面的知識,雖然正則寫起來可能會麻煩些,當時他的提取效率是最高的,接下來我們就可以將提取好的電影資訊進行儲存操作,這裡我們儲存為CSV檔案:
def save_infor(one_page_film):
'''
儲存提取好的電影資訊
:param html: 電影資訊列表
:return: None
'''
with open('top_film.csv', 'a', newline='') as f:
csv_file = csv.writer(f)
for one in one_page_film:
csv_file.writerow([one['name'], one['start'], one['releasetime'], one['score']])
複製程式碼
以上是獲取一頁html頁面並提取電影資訊儲存至CSV中的過程,接下來我們構造十頁的URL便可以完成貓眼電影TOP100榜中的所有電影資訊的獲取和儲存了,以下是完整程式:
import requests
import re
import csv
import time
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
params = {
'offset': 0
}
def get_html(page):
'''
獲取一頁html頁面
:param page: 頁數
:return: 該頁html頁面
'''
params['offset'] = page * 10
url = 'http://maoyan.com/board/4'
try:
response = requests.get(url, headers=headers, params=params)
if response.status_code == 200:
html = response.text
return html
else:
return -1
except:
return None
def parse_infor(html):
'''
提取html頁面中的電影資訊
:param html: html頁面
:return: 電影資訊列表
'''
pat = re.compile('<div class="movie-item-info">.*?<p.*?><a.*?>(.*?)</a></p>.*?<p.*?>(.*?)</p>.*?<p.*?>(.*?)</p>.*?</div>.*?<div.*?>.*?<p.*?><i.*?>(.*?)</i><i.*?>(.*?)</i></p>.*?</div>.*?</div>.*?</div>', re.S)
results = re.findall(pat, html)
one_page_film = []
if results:
for result in results:
film_dict = {}
# 獲取電影名資訊
film_dict['name'] = result[0]
# 獲取主演資訊
start = result[1]
# 替換字串中的 '\n' 字元,即換行字元
start.replace('\n', '')
# 去掉字串兩邊的空格,並使用切片去除字串開頭的'主演:'三個字元
start = start.strip()[3:]
film_dict['start'] = start
# 獲取上映時間資訊
releasetime = result[2]
# 使用切片去除字串開頭的'上映時間:'五個字元
releasetime = releasetime[5:]
film_dict['releasetime'] = releasetime
# 獲取評分資訊
left_half =result[3]
right_half = result[4]
score = left_half + right_half
film_dict['score'] = score
# 列印該電影資訊:
print(film_dict)
# 將該電影資訊字典存入一頁電影列表中
one_page_film.append(film_dict)
return one_page_film
else:
return None
def save_infor(one_page_film):
'''
儲存提取好的電影資訊
:param one_page_film: 電影資訊列表
:return: None
'''
with open('top_film.csv', 'a', newline='', errors='ignore') as f:
csv_file = csv.writer(f)
for one in one_page_film:
csv_file.writerow([one['name'], one['start'], one['releasetime'], one['score']])
if __name__ == "__main__":
# 利用迴圈構建頁碼
for page in range(10):
# 請求頁面
html = get_html(page)
if html:
# 提取資訊
one_page_film = parse_infor(html)
if one_page_film:
# 儲存資訊
save_infor(one_page_film)
time.sleep(1)
複製程式碼
動態網頁實戰
本節我們將爬取貓眼電影實時票房資料,學會在動態網頁中獲取我們想要的資料,首先開啟貓眼專業版-實時票房, 其網址為:https://piaofang.maoyan.com/dashboard,然後我們可以看見現在的實時電影票房資料,可以看見 “今日實時” 的資料在不斷地動態增加:
而當我們檢視該網頁原始碼時,卻並沒有電影相關的票房等資訊,那麼可以判斷該頁面可能使用了Ajax(即“Asynchronous Javascript And XML”(非同步 JavaScript 和 XML))技術,即動態網頁(是指跟靜態網頁相對的一種網頁程式設計技術。靜態網頁,隨著html程式碼的生成,頁面的內容和顯示效果就基本上不會發生變化了——除非你修改頁面程式碼。而動態網頁則不然,頁面程式碼雖然沒有變,但是顯示的內容卻是可以隨著時間、環境或者資料庫操作的結果而發生改變)。我們可以利用瀏覽器的開發者工具進行分析:
我們可以發現每隔一段時間都會有一個新的請求,其請求型別都為xhr,而Ajax的請求型別就是xhr,這請求可能就是實時更新的票房資訊,而我們需要的資料可能就在這些檔案裡,於是我們選擇一個進行分析:
在Preview中,我們可以看見大量的電影相關的資訊,即我們想要獲取的實時電影票房資料,而這些內容是JSON格式的,瀏覽器開發者工具自動做了解析方便我們檢視,接下來我們只需要用Python模擬這些Ajax請求,拿下這些資料然後解析即可,而這些Ajax無非依然是HTTP請求,所以只要拿到對應URL然後使用Python模擬該請求即可,我們可以直接複製,如下圖:
獲取到該請求的連結,接下來我們就用Python模擬該請求:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
def get_html():
'''
獲取JSON檔案
:return: JSON格式的資料
'''
# 請求second.json的URL
url = 'https://box.maoyan.com/promovie/api/box/second.json'
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
# 由於是JSON檔案,我們可以返回JSON格式的資料便於後續提取
return response.json()
else:
return -1
except:
return None
複製程式碼
獲取對應的JSON資料後,我們就可以利用進行提取操作了。
def parse_infor(json):
'''
從JSON資料中提取電影票房資料,包括:電影名,上映資訊,綜合票房,票房佔比,累計票房
:param json: JSON格式的資料
:return: 每次迴圈返回一次字典型別的電影資料
'''
if json:
# 利用json中的get()方法層層獲取對應的資訊
items = json.get('data').get('list')
for item in items:
piaofang = {}
piaofang['電影名'] = item.get('movieName')
piaofang['上映資訊'] = item.get('releaseInfo')
piaofang['綜合票房'] = item.get('boxInfo')
piaofang['票房佔比'] = item.get('boxRate')
piaofang['累計票房'] = item.get('sumBoxInfo')
# 利用生成器每次迴圈都返回一個資料
yield piaofang
else:
return None
複製程式碼
讀者可能看見我們沒有使用常規的return進行函式返回,而是使用了生成器,這樣就能每次迴圈都返回一次資料,具體讀者可以生成器 | 廖雪峰的官方網站進一步瞭解學習,接下來我們就將提取好的票房資訊儲存為格式化的HTML檔案:
def save_infor(results):
'''
儲存格式化的電影票房資料HTML檔案
:param results: 電影票房資料的生成器
:return: None
'''
rows = ''
for piaofang in results:
# 利用Python中的format字串填充html表格中的內容
row = '<tr><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td></tr>'.format(piaofang['電影名'],
piaofang['上映資訊'],
piaofang['綜合票房'],
piaofang['票房佔比'],
piaofang['累計票房'])
# 利用字串拼接迴圈儲存每個格式化的電影票房資訊
rows = rows + '\n' + row
# 利用字串拼接處格式化的HTML頁面
piaofang_html = '''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>電影票房</title>
</head>
<body>
<style>
.table1_5 table {
width:100%;
margin:15px 0
}
.table1_5 th {
background-color:#00BFFF;
color:#FFFFFF
}
.table1_5,.table1_5 th,.table1_5 td
{
font-size:0.95em;
text-align:center;
padding:4px;
border:1px solid #dddddd;
border-collapse:collapse
}
.table1_5 tr:nth-child(odd){
background-color:#aae9fe;
}
.table1_5 tr:nth-child(even){
background-color:#fdfdfd;
}
</style>
<table class='table1_5'>
<tr>
<th>電影名</th>
<th>上映資訊</th>
<th>綜合票房</th>
<th>票房佔比</th>
<th>累計票房</th>
</tr>
''' + rows + '''
</table>
</body>
</html>
'''
# 儲存已經格式化的html頁面
with open('piaofang.html', 'w', encoding='utf-8') as f:
f.write(piaofang_html)
複製程式碼
我們將以上過程整合,即可得到完整的票房資料獲取的程式碼例項:
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
def get_html():
'''
獲取JSON檔案
:return: JSON格式的資料
'''
# 請求second.json的URL
url = 'https://box.maoyan.com/promovie/api/box/second.json'
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
# 由於是JSON檔案,我們可以返回JSON格式的資料便於後續提取
return response.json()
else:
return -1
except:
return None
def parse_infor(json):
'''
從JSON資料中提取電影票房資料,包括:電影名,上映資訊,綜合票房,票房佔比,累計票房
:param json: JSON格式的資料
:return: 每次迴圈返回一次字典型別的電影資料
'''
if json:
# 利用json中的get()方法層層獲取對應的資訊
items = json.get('data').get('list')
for item in items:
piaofang = {}
piaofang['電影名'] = item.get('movieName')
piaofang['上映資訊'] = item.get('releaseInfo')
piaofang['綜合票房'] = item.get('boxInfo')
piaofang['票房佔比'] = item.get('boxRate')
piaofang['累計票房'] = item.get('sumBoxInfo')
# 利用生成器每次迴圈都返回一個資料
yield piaofang
else:
return None
def save_infor(results):
'''
儲存格式化的電影票房資料HTML檔案
:param results: 電影票房資料的生成器
:return: None
'''
rows = ''
for piaofang in results:
# 利用Python中的format字串填充html表格中的內容
row = '<tr><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td></tr>'.format(piaofang['電影名'],
piaofang['上映資訊'],
piaofang['綜合票房'],
piaofang['票房佔比'],
piaofang['累計票房'])
# 利用字串拼接迴圈儲存每個格式化的電影票房資訊
rows = rows + '\n' + row
# 利用字串拼接處格式化的HTML頁面
piaofang_html = '''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>電影票房</title>
</head>
<body>
<style>
.table1_5 table {
width:100%;
margin:15px 0
}
.table1_5 th {
background-color:#00BFFF;
color:#FFFFFF
}
.table1_5,.table1_5 th,.table1_5 td
{
font-size:0.95em;
text-align:center;
padding:4px;
border:1px solid #dddddd;
border-collapse:collapse
}
.table1_5 tr:nth-child(odd){
background-color:#aae9fe;
}
.table1_5 tr:nth-child(even){
background-color:#fdfdfd;
}
</style>
<table class='table1_5'>
<tr>
<th>電影名</th>
<th>上映資訊</th>
<th>綜合票房</th>
<th>票房佔比</th>
<th>累計票房</th>
</tr>
''' + rows + '''
</table>
</body>
</html>
'''
# 儲存已經格式化的html頁面
with open('piaofang.html', 'w', encoding='utf-8') as f:
f.write(piaofang_html)
if __name__ == "__main__":
# 獲取資訊
json = get_html()
# 提取資訊
results = parse_infor(json)
# 儲存資訊
save_infor(results)
複製程式碼
HTML檔案儲存效果如下圖所示:
可以看見,動態網頁的爬蟲可能會更加簡單些,關鍵就在於找到對應的XHR格式的請求,而一般這種格式的檔案都是JSON格式的,提取相對也會更加簡單方便,而讀者可能會問為何要把這個資訊儲存為HTML檔案格式的呢,喜歡電影的讀者可能會經常開啟貓眼電影檢視每天的電影票房資料,何不嘗試將我們所學的爬蟲知識運用起來製作一個定時爬取電影票房資料並推送至個人郵箱的爬蟲小程式呢,這樣就省得我們每天開啟網頁檢視,讓資料主動為我們服務,也算是學習致用了吧,感興趣的讀者可以自己嘗試下,下圖筆者根據這個爬蟲程式擴充套件每天收到的實時票房資訊郵件,每天定時爬取推送給筆者,列表內容如下圖所示:
推動內容如下圖所示: