寫在前面的話
目前為止,你應該已經(jīng)了解爬蟲的三個基本小節(jié):
來源:xiaqo.com
?
- 正文
明確需求
我們今天要爬的數(shù)據(jù)是
豆瓣電影Top250
,是的,只有250條數(shù)據(jù),你沒猜錯。
輸入網(wǎng)址?https://movie.douban.com/top250
?我們可以看到網(wǎng)頁長這樣:
?
編輯?
編輯?
`250條數(shù)據(jù)`清清楚楚,沒有問題。可以看到,這個頁面其實已經(jīng)包含了影片的
主要內(nèi)容
:影片名、排序、編劇、主演、年份、類型、評論人數(shù)、評分,基本上都在這個頁面中。但我點開詳細影片
之后,發(fā)現(xiàn)了這個:
編輯?
似乎這個頁面數(shù)據(jù)更全一些,我們爬數(shù)據(jù)要的是什么,肯定是數(shù)據(jù)越多越好啊。相比這個詳細內(nèi)容,更是多了每個星級的影評占比,那我們肯定選擇它了啊
?
好,那理一下我們的思路寫一下偽代碼
# 遍歷10頁 data_movies # 保存所有影片數(shù)據(jù)集 for per_page in pages: # 爬取10頁的每一頁數(shù)據(jù) movies = craw_page_info(per_page) # 遍歷每一頁的25個影片 for movie in movies: # 爬取每個影片的詳細內(nèi)容 data_per_movie = craw_detail_info(movie) # 保存每個影片信息到數(shù)據(jù)集中 data_movies.append(data_per_movie) # 保存結(jié)果到數(shù)據(jù)庫中 data_movies_to_mysql
稍微解釋一下:
兩層循環(huán)
,第一層是遍歷10頁網(wǎng)頁
,因為其中每個網(wǎng)頁分別有25個影片,所以,第二層循環(huán)又依次遍歷25個影片
獲取詳細信息,最后保存結(jié)果到數(shù)據(jù)庫中!是不是,很,簡,單!
但是,實操起來你可能會遇到各種各樣的問題,做好心理準備!
#### 開始實操 **首先,確定我們要輸出的影片字段** `主要數(shù)據(jù)`包括:影片排序、影片名稱、影片導演、影片編劇、影片主演、影片又名、影片鏈接 `關鍵數(shù)據(jù)`包括:影片類型、制片國家、影片語言、上映日期、影片片長 `核心數(shù)據(jù)`包括:影片評分、評論人數(shù)、5/4/3/2/1各星級對應的評論占比字段如下
:movie_rank:影片排序 movie_name:影片名稱 movie_director:影片導演 movie_writer:影片編劇 movie_starring:影片主演 movie_type:影片類型 movie_country:影片制片國家 movie_language:影片語言 movie_release_date:影片上映日期 movie_run_time:影片片長 movie_second_name:影片又名 movie_imdb_href:影片IMDb 鏈接 movie_rating:影片總評分 movie_comments_user:影片評論人數(shù) movie_five_star_ratio:影片5星占比 movie_four_star_ratio:影片4星占比 movie_three_star_ratio:影片3星占比 movie_two_star_ratio:影片2星占比 movie_one_star_ratio:影片1星占比 movie_note:影片備注信息,一般為空
然后,開始主流程確認一下主要參數(shù),起始頁碼(默認為0),每頁影片25個,共10頁,
參數(shù)如下
:start_page:起始頁碼 page_size:每一頁大小 pages:總頁碼
定義類對象這里我們將每個影片封裝成一個對象,傳入我們的主要參數(shù),設置爬蟲頭部,并建立和數(shù)據(jù)庫的相關連接
類定義對象如下
:class DouBanMovie: def __init__(self, url, start_page, pages, page_size): """ 初始化 @param url: 爬取主網(wǎng)址 @param start_page: 起始頁碼 @param pages: 總頁碼(截止頁碼) @param page_size: 每頁的大小 """ self.url = url self.start_page = start_page self.pages = pages self.page_size = page_size self.data_info = [] self.pymysql_engine, self.pymysql_session = connection_to_mysql() self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36' }
“小一哥,你這里的數(shù)據(jù)庫連接用的是什么啊,我怎么看不太懂?” “我封裝了一下,數(shù)據(jù)庫的連接這里選用了 SQLAlchemy。"不要著急,以后會專門寫一篇 SQLAlchemy 關于數(shù)據(jù)庫的相關操作
# 創(chuàng)建基類, Base = declarative_base() def connection_to_mysql(): """ 連接數(shù)據(jù)庫 @return: """ engine = create_engine('mysql+pymysql://username:passwd@localhost:3306/db_name?charset=utf8') Session = sessionmaker(bind=engine) db_session = Session() # 創(chuàng)建數(shù)據(jù)表 Base.metadata.create_all(engine) return engine, db_session
確定主框架:# 如果當前頁碼小于0,異常退出 if self.start_page < 0: return "" # 如果起始頁面大于總頁碼數(shù),退出 if self.start_page > self.pages: return "" # 若當前頁其實頁碼小于總頁數(shù),繼續(xù)爬取數(shù)據(jù) while self.start_page < pages: # 拼接當前頁的網(wǎng)址 # 主爬蟲代碼 # 下一頁 self.start_page = self.start_page + 1
拼接當前頁的網(wǎng)址
這里解釋一下,當我們?nèi)ピL問第一頁的時候發(fā)現(xiàn)網(wǎng)址如下https://movie.douban.com/top250
去訪問下一頁的時候發(fā)現(xiàn)網(wǎng)址變化如下
https://movie.douban.com/top250?start=25&filter=
而再下一頁的網(wǎng)址變化如下:
https://movie.douban.com/top250?start=50&filter=
可以發(fā)現(xiàn),新的網(wǎng)址只是
變化了后面的 start 參數(shù)
,于是我們拼接出每一頁的網(wǎng)址:start_number = self.start_page * self.page_size new_url = self.url + '?start=' + str(start_number) + '&filter='
爬取第一個頁面
確定好主框架之后,我們需要去爬取
第一個網(wǎng)頁
,也就是包含25個影片的頁面
。
這時候,我們前三節(jié)提到的爬蟲實現(xiàn)方式直接拿過來:self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', } # 爬取當前頁碼的數(shù)據(jù) response = requests.get(url=new_url, headers=self.headers)
成功獲取到頁面數(shù)據(jù)之后,我們需要對頁面解析
,拿到每一個影片跳轉(zhuǎn)詳細頁面的超鏈接
通過谷歌瀏覽器 F12 開發(fā)者工具可查看網(wǎng)頁源碼
可以看到每個影片的詳細信息在一個
li 標簽
中,而每個 li 標簽中都有一個class='pic' 的 div
,在 div 里面存在這樣一個?a 標簽
?中而這個 a 標簽的 href 正是我們要需要的?
詳細頁面信息的超鏈接
編輯?
確定了超鏈接位置所在,打開我們上一節(jié)的 BeautifulSoup 詳解,定位、解析soup = BeautifulSoup(response.text, 'html.parser') # 定位到每一個電影的 div (pic 標記的 div) soup_div_list = soup.find_all(class_="pic") # 遍歷獲取每一個 div 的電影詳情鏈接 for soup_div in soup_div_list: # 定位到每一個電影的 a 標簽 soup_a = soup_div.find_all('a')[0] movie_href = soup_a.get('href') print(movie_href)
拿到當前頁面的25 個影片的詳細內(nèi)容的超鏈接
我們離成功又進了一步!
爬取詳細頁面同樣,一行代碼拿下頁面數(shù)據(jù)
'''爬取頁面,獲得詳細數(shù)據(jù)''' response = requests.get(url=movie_detail_href, headers=self.headers)
創(chuàng)建一個有序字典,保存當前影片數(shù)據(jù)
# 生成一個有序字典,保存影片結(jié)果 movie_info = OrderedDict()
我們再來看一下這個頁面的的源碼是什么樣的,首先是影片排序和影片名稱,我們可以從上個頁面?zhèn)鬟f過來。但是,既然它這里有,我直接解析行不行?
必須行??!
編輯?
這個更簡單,影片排名直接定位一個 `class='top250-no' 的 span 標簽`,影片名稱定位一個 `property='v:itemreviewed' 的 span 標簽`,獲取標簽內(nèi)容即可# 解析電影排名和名稱 movie_info['movie_rank'] = soup.find_all('span', class_='top250-no')[0].string movie_info['movie_name'] = soup.find_all('span', property='v:itemreviewed')[0].string
接下來是影片主要數(shù)據(jù):
編輯?
這個時候我們需要先定位到?id='info' 的 div
中,然后可以看到整個 div 的數(shù)據(jù)
就是我們需要的主要數(shù)據(jù)。# 定位到影片數(shù)據(jù)的 div soup_div = soup.find(id='info')
“不對啊,小一哥,我發(fā)現(xiàn)編劇有時候是一個,有時候是多個。多個的時候存在在多個 span 標簽中,這個怎么辦?。俊?/p>
編輯?
“這個簡單,我寫一個小函數(shù),統(tǒng)一處理一下?!?/p>
def get_mul_tag_info(self, soup_span): """ 獲取多個標簽的結(jié)果合并在一個結(jié)果中返回,并用 / 分割 """ info = '' for second_span in soup_span: # 區(qū)分 href 和標簽內(nèi)容 info = ('' if (info == '') else '/').join((info, second_span.string)) return info
“對了,你記得把最外層的 span 標簽給我就行。像這種:”
# 解析電影發(fā)布信息 movie_info['movie_director'] = self.get_mul_tag_info(soup_div.find_all('span')[0].find_all('a')) movie_info['movie_writer'] = self.get_mul_tag_info(soup_div.find_all('span')[3].find_all('a')) movie_info['movie_starring'] = self.get_mul_tag_info(soup_div.find_all('span')[6].find_all('a')) movie_info['movie_type'] = self.get_mul_tag_info(soup_div.find_all('span', property='v:genre')) movie_info['movie_country'] = soup_div.find(text='制片國家/地區(qū):').next_element.lstrip().rstrip() movie_info['movie_language'] = soup_div.find(text='語言:').next_element.lstrip().rstrip() movie_info['movie_release_date'] = self.get_mul_tag_info(soup_div.find_all('span', property='v:initialReleaseDate')) movie_info['movie_run_time'] = self.get_mul_tag_info(soup_div.find_all('span', property='v:runtime')) movie_info['movie_imdb_href'] = soup_div.find('a', target='_blank')['href']
“小一哥,又出問題了,有的影片沒有
又名
標簽,這個怎么處理呢?”
“這個我們做個異常檢測,沒有的手動賦空值就行了。”movie_second_name = '' try: movie_second_name = soup_div.find(text='又名:').next_element.lstrip().rstrip() except AttributeError: print('{0} 沒有又名'.format(movie_info['movie_name'])) movie_info['movie_second_name'] = movie_second_name
最后還剩下評分數(shù)據(jù)
評分數(shù)據(jù)不但有總評分,還有每個星級的評分。
“小一哥,你說我們?nèi)∧膫€數(shù)據(jù)???”
“小孩才做選擇,我當然是全部都要!”
編輯?
可以看到,總評分和總評論人數(shù)分別有一個`唯一的 property`,分別是` property='v:average' 的 strong 標簽`和 `property='v:votes' 的 span 標簽`ok,接下來直接拿數(shù)據(jù):
# 獲取總評分和總評價人數(shù) movie_info['movie_rating'] = soup.find_all('strong', property='v:average')[0].string movie_info['movie_comments_user'] = soup.find_all('span', property='v:votes')[0].string
最后就剩下
每個星級的評分占比
,可以看到?5星/4星/3星/2星/1星
?分別對應?力薦/推薦/還行/較差/很差
,可以看到他們都存在在一個class='ratings-on-weight' 的 div
中所以,先定位 div :
# 定位到影片星級評分占比的 div soup_div = soup.find('div', class_="ratings-on-weight")
然后獲取每個星級評分占比數(shù)據(jù):
# 獲取每個星級的評分 movie_info['movie_five_star_ratio'] = soup_div.find_all('div')[0].find(class_='rating_per').string movie_info['movie_four_star_ratio'] = soup_div.find_all('div')[2].find(class_='rating_per').string movie_info['movie_three_star_ratio'] = soup_div.find_all('div')[4].find(class_='rating_per').string movie_info['movie_two_star_ratio'] = soup_div.find_all('div')[6].find(class_='rating_per').string movie_info['movie_one_star_ratio'] = soup_div.find_all('div')[8].find(class_='rating_per').string
打印一下看一下我們
當前的影片
數(shù)據(jù):對 movie_starring 字段只輸出部分顯示
OrderedDict( [ ('movie_rank', 'No.1'), ('movie_name', '肖申克的救贖 The Shawshank Redemption'), ('movie_director', '弗蘭克·德拉邦特'), ('movie_writer', '弗蘭克·德拉邦特/斯蒂芬·金'), ('movie_starring', '蒂姆·羅賓斯/摩根·弗里曼/鮑勃·岡頓/威廉姆·賽德勒/), ('movie_type', '劇情/犯罪'), ('movie_country', '美國'), ('movie_language', '英語'), ('movie_release_date', '1994-09-10(多倫多電影節(jié))/1994-10-14(美國)'), ('movie_run_time', '142分鐘'), ('movie_imdb_href', 'https://www.imdb.com/title/tt0111161'), ('movie_rating', '9.7'), ('movie_comments_user', '1720706'), ('movie_five_star_ratio', '84.8%'), ('movie_four_star_ratio', '13.6%'), ('movie_three_star_ratio', '1.4%'), ('movie_two_star_ratio', '0.1%'), ('movie_one_star_ratio', '0.1%'), ('movie_note', '') ] )
搞定,成功拿到了想要的數(shù)據(jù),最后一步:
保存數(shù)據(jù)庫
# 保存當前影片信息 self.data_info.append(movie_info) # 獲取數(shù)據(jù)并保存成 DataFrame df_data = pd.DataFrame(self.data_info) # 導入數(shù)據(jù)到 mysql 中 df_data.to_sql('t_douban_movie_top_250', self.pymysql_engine, index=False, if_exists='append')
看一眼我們的數(shù)據(jù)庫,該有的數(shù)據(jù)都存進去了
編輯?
到這里,爬蟲就算是結(jié)束了。
?總結(jié)一下:
準備工作:
?開始爬蟲:
?思考:
以上就是我們今天爬蟲實戰(zhàn)的主要內(nèi)容,相對來說比較簡單。
第一個項目,旨在讓大家了解爬蟲流程
,同時,也可以思考一下以下幾點:以上數(shù)據(jù)的獲取是否可以用
今天的獲取方法
?如果不行,那應該通過什么方式獲取這些數(shù)據(jù)?
?寫在后面的話
今天的實戰(zhàn)項目就結(jié)束了,需要源代碼的同學可以在
公眾號后臺
回復?豆瓣電影
?獲取,如果覺得小一哥講的還不錯的話,不妨點個贊
?文章來源:http://www.zghlxwxcb.cn/news/detail-844208.html開篇已經(jīng)提到,我們的目的不是爬數(shù)據(jù)。所以,我會利用這些數(shù)據(jù)做一個簡單數(shù)據(jù)分析,目的很簡單:了解數(shù)據(jù)分析的流程。下期見。文章來源地址http://www.zghlxwxcb.cn/news/detail-844208.html
- 首先,進入豆瓣電影Top250,一共10頁,每頁25個影片。
- 然后,針對每一頁的25個影片,進入其詳細內(nèi)容頁面
- 最后,解析每個影片的詳細內(nèi)容,保存內(nèi)容到數(shù)據(jù)庫中
- 首先我們定義了一個影片對象,傳入了網(wǎng)址的參數(shù)信息,設置了爬蟲頭部,并建立了數(shù)據(jù)庫連接
- 我們通過下一頁分析出每個影片頁的超鏈接,發(fā)現(xiàn)只是改變了參數(shù)
- 建立了主流程,并寫出了主流程的偽代碼
- 爬取
第一頁
的網(wǎng)頁內(nèi)容 - 解析
第一頁
的內(nèi)容,獲取每頁中25個影片的詳細超鏈接 - 爬取
詳細影片
的網(wǎng)頁內(nèi)容 - 解析
第二頁
的內(nèi)容,保存到每個影片對象中 -
保存數(shù)據(jù)
到數(shù)據(jù)庫中 - 影片詳細頁面的短評論數(shù)據(jù)
- 影片詳細頁面的獲獎情況數(shù)據(jù)
- 影片詳細頁面的討論區(qū)數(shù)
到了這里,關于爬蟲實戰(zhàn)-手把手教你爬豆瓣電影 | 附詳細源碼和講解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!