天天看點

爬取豆瓣電影8608部電影的資訊-模拟Ajax請求的應用

一入爬蟲深似海,回頭還是在入門。

豆瓣電影提供最新的電影介紹及評論包括上映影片的影訊查詢及購票服務。你可以記錄想看、在看和看過的電影電視劇,順便打分、寫影評。極大地友善了人們的生活。1

豆瓣電影是國内較大的電影讨論社群,代表了電影圈的風向和潮流(我沒有收取任何廣告費),豆瓣上文藝青年比較多,但是讨論和評價的人數數量很大,還是有一定可信度和代表性的,是以選擇爬取豆瓣電影,主要還是因為它的網頁結構比較簡單。

本次爬取選擇的網址是:https://movie.douban.com/tag/#/?sort=U&range=0,10&tags=電影 。網頁打開後,可以看到最下面并不是分頁顯示的,而是一個“加載更多”,點選後整個頁面并沒有重新整理,網址也沒有改變,隻是顯示了更多的内容,由此可見這個頁面使用的是異步加載。

爬取豆瓣電影8608部電影的資訊-模拟Ajax請求的應用

首先我們通過Chrome浏覽器的開發者模式,看一下這個Ajax請求是怎麼樣的。

爬取豆瓣電影8608部電影的資訊-模拟Ajax請求的應用

可以看到Request URL是 https://movie.douban.com/j/new_search_subjects?sort=U&range=0,10&tags=電影&start=20 。不停的點加載更多,變化的隻有start=20這個地方,這裡的20指的是每一頁顯示的電影個數,start=0表示第一頁,start=20表示第二頁,start=40表示第三頁,以此類推。經手動測試,start=9960是最後一頁。

爬取豆瓣電影8608部電影的資訊-模拟Ajax請求的應用

Ajax請求傳回的是一個json格式的資料,Chrome浏覽器的開發者模式非常人性化的有一個Preview的功能可以直覺的看到解析後的内容。這裡可以看到data包含了20個電影的清單,其中我們要提取的是主演(casts)、導演(directors)、評分(rate)、電影名字(title)。但是這些資訊似乎不夠,還缺少一些我們想要提取的比如類型、制片國家、上映時間等,這裡可以看到json資料還有一個url位址,https://movie.douban.com/subject/3011091/ ,沒錯,這個就是這部電影詳細資訊的位址。

爬取豆瓣電影8608部電影的資訊-模拟Ajax請求的應用

這個詳細資訊頁面裡面就包含了我們想要提取的資訊。當然這個詳細資訊頁面就不是Ajax請求傳回的了,是一個實實在在的網址,我們可以通過解析原始的HTML文檔獲得想要的東西,這部分資訊都包含在一個叫info的節點内。

爬取豆瓣電影8608部電影的資訊-模拟Ajax請求的應用

目标确定好了,現在還有一個問題就是如何防止被封ip。通過網上搜尋得到的非官方消息是豆瓣白天的通路限制頻率是40次/分鐘,晚上是60次/分鐘,解決封ip的有效方法主流的有兩種,一是使用代理,二是設定爬取的時間間隔。使用代理是很好的方法,但是也有很多缺點,比如穩定的代理是要收費的,而且在網上還搜到使用代理依然被封原始ip的案例。網際網路的思想是資訊共享,但是我們在享受免費資源的同時,也要考慮到網站伺服器的壓力,要做一個有素質的人,是以這裡我們果斷選擇第二種方法(其實是還不太會弄代理)。

import requests
import time
import random
from openpyxl import Workbook
from bs4 import BeautifulSoup

lst=[] #存放爬取結果
error_list=[] #存放未能提取的頁碼
base_url='https://movie.douban.com/j/new_search_subjects?sort=U&range=0,10&tags=%E7%94%B5%E5%BD%B1&start='
headers={
        'Host':'movie.douban.com',
        'Referer':'https://movie.douban.com/tag/',
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
        }

StartTime=time.time()
for num in range(0,9961,20):
    num=(num-1)*20
    url=base_url+str(num)
    print('現在正在提取第%d頁……' % (num//20+1))
    try:
        response=requests.get(url,headers=headers)
        if response.status_code==200:
            json=response.json()
        items=json.get('data')
        for item in items:
            response=requests.get(item.get('url')) #周遊每個url
            html=response.text
            soup=BeautifulSoup(html,'lxml')
            info=soup.find(id="info").get_text() #提取info節點内的文本
            summary=soup.find(property="v:summary").get_text() #提取劇情簡介
            detail={} #以字典的形式存儲每個電影提取的資訊
            detail['名稱']=item.get('title')
            detail['評分']=item.get('rate')
            detail['主演']='/'.join(item.get('casts'))
            detail['導演']='' #設定空值,以保證每部電影對應的字典都包含所有Key
            detail['類型']=''
            detail['制片國家/地區']=''
            detail['語言']=''
            detail['上映日期']=''
            detail['片長']=''
            for x in info.split('\n'):
                if x[:x.find(':')]=='導演':
                    detail['導演']=x[x.find(':')+1:].replace(' ','')
                elif x[:x.find(':')]=='類型':
                    detail['類型']=x[x.find(':')+1:].replace(' ','')
                elif x[:x.find(':')]=='制片國家/地區':
                    detail['制片國家/地區']=x[x.find(':')+1:].replace(' ','')
                elif x[:x.find(':')]=='語言':
                    detail['語言']=x[x.find(':')+1:].replace(' ','')
                elif x[:x.find(':')]=='上映日期':
                    detail['上映日期']=x[x.find(':')+1:].replace(' ','')
                elif x[:x.find(':')]=='片長':
                    detail['片長']=x[x.find(':')+1:].replace(' ','')
            detail['劇情簡介']=summary.strip()
            lst.append(detail)
            time.sleep(random.uniform(0.1,2.0)) #設定一個随機等待時間,模拟人的操作,以防被封ip
    except:
        error_list.append(num//20+1) #儲存未能提取的頁碼
EndTime=time.time()

print('正在存儲資料……')
wb=Workbook() #存儲到excel
ws=wb.active
col=1
for key in lst[0].keys():
    ws.cell(row=1,column=col,value=key)
    col+=1
for i in range(len(lst)):
    col=1
    for key in lst[0].keys():
        ws.cell(row=i+2,column=col,value=lst[i][key])
        col+=1		
save_name=r'C:\Users\Administrator\Desktop\MovieData.xlsx'
wb.save(save_name)
print('未能提取的頁碼:',error_list)
print('運作時間:%s' % (EndTime-StartTime))
           

整個爬取過程曆時長達5個小時!!!!足以展現本人在追求真理的道路上,堅韌不拔,持之以恒的精神!

有少量頁碼沒有成功,應該是網絡原因,最終爬取的電影數目是8608部。

爬取的結果如下圖,這個結果準備用于後續的分析。

爬取豆瓣電影8608部電影的資訊-模拟Ajax請求的應用
  1. https://baike.baidu.com/item/豆瓣電影#reference-[1]-2989101-wrap ↩︎