用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個字元
}
結果: