1. 程式人生 > >用Requests和正則表示式爬取貓眼電影(TOP100+最受期待榜)

用Requests和正則表示式爬取貓眼電影(TOP100+最受期待榜)

目標站點分析

目標站點(貓眼榜單TOP100):
在這裡插入圖片描述
如下圖,貓眼電影的翻頁offset明顯在URL中,所以只要搞定第一頁的內容加上一個迴圈加上offset就可以爬取前100。
第一頁
第二頁

流程框架

1、抓取單頁內容

利用requests請求目標站點,得到單個網頁HTML程式碼,返回結果。

2、正則表示式分析

根據HTML程式碼分析得到電影的排名、地址、名稱、主演、上映時間、評分等資訊。

3、儲存至檔案

通過檔案的形式將結果儲存,每一部電影一個結果一行Json字串。

4、開啟迴圈及多執行緒

對多頁內容遍歷,開啟多執行緒提高抓取速度。

實戰

1、抓取單頁內容

興致勃勃地碼了一段程式碼,用來獲得第一頁的內容:

import requests
from requests.exceptions import RequestException


#提取單頁內容,用try,except方便找bug
def get_one_page(url):
    try:
        response = requests.get(url)      
        if response.status_code == 200:#如果狀態碼為200,說明請求成功
            return response.text
        return response.
status_code#否則請求失敗,返回狀態碼果 except RequestException: return None def main(): url= 'http://maoyan.com/board/4' html = get_one_page(url) print(html) if __name__ == '__main__': main()

結果很尷尬,直接來了個403狀態碼,說明請求失敗了。
在這裡插入圖片描述

回顧一下之前學的知識:使用Requests庫來進行爬蟲的詳解
headers在爬蟲中是非常必要的,很多時候如果請求不加headers,那麼你可能會被禁掉或出現伺服器錯誤…
解決辦法:加入headers試試看(做一個瀏覽器的偽裝),只需要向get方法傳入headers引數就好了,具體的headers內容直接就用文中給出的例子就行。

import requests
from requests.exceptions import RequestException

headers = {'User-Agent':'Mozilla/5.0(Macintosh;Intel Mac OS X 10_11_4)AppleWebKit/537.36(KHTML,like Gecko)Chrome/52.0.2743.116 Safari/537.36'}
#提取單頁內容,用try,except方便找bug
def get_one_page(url):
    try:
        
        response = requests.get(url, headers=headers)#傳入headers引數
        if response.status_code == 200:
            return response.text
        return response.status_code
    except RequestException:
        return None

def main():
    url= 'http://maoyan.com/board/4'
    html = get_one_page(url)
    print(html)

if __name__ == '__main__':
    main()

在這裡插入圖片描述
呼呼,打印出了一堆內容,說明這回已經請求成功了。再次說明了請求時加入headers引數的重要性。

2、正則表示式分析

我們想要從爬取到的內容中提取6個資訊:排名,封面(超連結),標題,演員,上映時間,評分。
先來看看每一個電影在網頁中的結構是怎樣的,這樣才能知道該怎麼寫正則表示式:
在這裡插入圖片描述
仔細研究上圖,紅框中的正是我們要提取的資訊,那麼只要將它們置於正則表示式的7個括號中(最後的評分是分開的,所以需要2個括號來選中),並且在括號兩旁做出正確的限定,那麼這個正則表示式還是很好寫的。
程式碼如下(注意要匯入re庫):

def parse_one_page(html):#定義一個函式用來解析html程式碼
	#生成一個正則表示式物件
	pattern = re.compile('<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name"><a' #此處換行
		+'.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>'
		+'.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>',re.S)
	items = re.findall(pattern,html)

對輸出的結果再進行格式化處理,使輸出更美觀:

#items是一個list,其中的每個內容都是一個元組
#將雜亂的資訊提取並格式化,變成一個字典形式
	for item in items:
		yield { #構造一個字典
			'index': item[0],
			'image': item[1],
			'title': item[2],
			'actor': item[3].strip()[3:],#做一個切片,去掉“主演:”這3個字元
			'time': item[4].strip()[5:],#做一個切片,去掉“上映時間:”這5個字元
			'score': item[5]+item[6]#將小數點前後的數字拼接起來
		}

再修改一下main函式:

def main():
    url= 'http://maoyan.com/board/4'
    html = get_one_page(url)
    for item in parse_one_page(html):#item是一個生成器
    	print(item)

執行一下看看:
在這裡插入圖片描述
效果還可以吧~

3、儲存至檔案

定義一個函式用來將上面提取的資訊儲存到檔案中:

def write_to_file(content):
	with open('result.txt','a',encoding='utf-8') as f:
	#a表示模式是“追加”;採用utf-8編碼可以正常寫入漢字
		f.write(json.dumps(content, ensure_ascii = False) + '\n')#不允許寫入ascii碼
		#content是一個字典,我們需要轉換成字串形式,注意匯入json庫		
		f.close()

再修改一下main函式:


def main():
    url= 'http://maoyan.com/board/4'
    html = get_one_page(url)
    for item in parse_one_page(html):#item是一個生成器
    	print(item)
    	write_to_file(item)

執行一下看看:
在這裡插入圖片描述
成功儲存為檔案啦,看看內容,沒問題:
在這裡插入圖片描述

4、開啟迴圈及多執行緒

迴圈:

再看看網頁的特點,點選下一頁時,offset這個引數會增加10:
在這裡插入圖片描述
在這裡插入圖片描述
我們只需要修改一下主函式就可以了,給它新增一個offset引數,實現10個頁面的抓取:

def main(offset):
    url= 'http://maoyan.com/board/4?offset='+str(offset)#把offset引數以字串形式新增到url中
    html = get_one_page(url)
    for item in parse_one_page(html):#item是一個生成器
    	print(item)
    	write_to_file(item)

還有起始部分:

if __name__ == '__main__':
    for i in range(10):#構造一個數組實現0,10,20,...,90的迴圈
    	main(i*10)

執行看看結果(為了展示更美觀,在程式碼中把超連結這個資訊去掉了):
在這裡插入圖片描述
在這裡插入圖片描述
很棒!

多程序

採用多程序的方法可以提高執行的效率:

from multiprocessing import Pool
if __name__ == '__main__':

    	pool = Pool()#建立一個程序池
    	pool.map(main,[i*10 for i in range(10)])#map方法建立程序(不同引數的main),並放到程序池中

總結

非常順利的爬取了貓眼電影TOP100,關鍵在於正則表示式的寫法。
全部程式碼如下:

import requests
from requests.exceptions import RequestException
import re
import json
from multiprocessing import Pool

headers = {'User-Agent':'Mozilla/5.0(Macintosh;Intel Mac OS X 10_11_4)AppleWebKit/537.36(KHTML,like Gecko)Chrome/52.0.2743.116 Safari/537.36'}
#提取單頁內容,用try,except方便找bug
def get_one_page(url):
    try:
        
        response = requests.get(url, headers=headers)#傳入headers引數
        if response.status_code == 200:
            return response.text
        return response.status_code
    except RequestException:#捕獲這個型別的異常
        return None

def parse_one_page(html):#定義一個函式用來解析html程式碼
	#生成一個正則表示式物件
	pattern = re.compile('<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name"><a' #此處換行
		+'.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>'
		+'.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>',re.S)
	items = re.findall(pattern,html)
	#items是一個list,其中的每個內容都是一個元組
	#將雜亂的資訊提取並格式化,變成一個字典形式
	for item in items:
		yield { #構造一個字典
			'index': item[0],
			#'image': item[1],
			'title': item[2],
			'actor': item[3].strip()[3:],#做一個切片,去掉“主演:”這3個字元
			'time': item[4].strip()[5:],#做一個切片,去掉“上映時間:”這5個字元
			'score': item[5]+item[6]#將小數點前後的數字拼接起來
		}

def write_to_file(content):
	with open('result.txt','a',encoding='utf-8') as f:
	#a表示模式是“追加”;採用utf-8編碼可以正常寫入漢字
		f.write(json.dumps(content, ensure_ascii = False) + '\n')#不允許寫入ascii碼
		#content是一個字典,我們需要轉換成字串形式,注意匯入json庫		
		f.close()


def main(offset):
    url= 'http://maoyan.com/board/4?offset='+str(offset)#把offset引數以字串形式新增到url中
    html = get_one_page(url)
    for item in parse_one_page(html):#item是一個生成器
    	print(item)
    	write_to_file(item)


if __name__ == '__main__':

    	pool = Pool()#建立一個程序池
    	pool.map(main,[i*10 for i in range(10)])#map方法建立程序(不同引數的main),並放到程序池中

爬取最受歡迎榜

原理還是一樣的,只需要確定一下要篩選什麼資訊(因為這和TOP100略有不同),並且修改一下正則表示式和一些小細節即可。

def parse_one_page(html):#定義一個函式用來解析html程式碼
	
	pattern = re.compile('<dd>.*?"board-index.*?">(\d+)</i>.*?href.*?data-src.*?</a>.*?board-item-main.*?name">.*?title.*?">(.*?)</a>.*?star">(.*?)</p>.*?'
				+'releasetime">(.*?)</p>.*?</dd>', re.S)
	items = re.findall(pattern, html)
	
	
	#items是一個list,其中的每個內容都是一個元組
	#將雜亂的資訊提取並格式化,變成一個字典形式
	for item in items:
		yield { #構造一個字典
			'index': item[0],
					#'image': item[1],
			'title': item[1],
			'actor': item[2].strip()[3:],#做一個切片,去掉“主演:”這3個字元
			'time': item[3].strip()[5:],#做一個切片,去掉“上映時間:”這5個字元
			
		}

結果:
在這裡插入圖片描述