人生苦短,我用 Python
前文傳送門:
小白學 Python 爬蟲(1):開篇
小白學 Python 爬蟲(2):前置準備(一)基本類庫的安裝
小白學 Python 爬蟲(3):前置準備(二)Linux基礎入門
小白學 Python 爬蟲(4):前置準備(三)Docker基礎入門
小白學 Python 爬蟲(5):前置準備(四)數據庫基礎
小白學 Python 爬蟲(6):前置準備(五)爬蟲框架的安裝
小白學 Python 爬蟲(7):HTTP 基礎
小白學 Python 爬蟲(8):網頁基礎
小白學 Python 爬蟲(9):爬蟲基礎
小白學 Python 爬蟲(10):Session 和 Cookies
小白學 Python 爬蟲(11):urllib 基礎使用(一)
小白學 Python 爬蟲(12):urllib 基礎使用(二)
小白學 Python 爬蟲(13):urllib 基礎使用(三)
小白學 Python 爬蟲(14):urllib 基礎使用(四)
小白學 Python 爬蟲(15):urllib 基礎使用(五)
小白學 Python 爬蟲(16):urllib 實戰之爬取妹子圖
小白學 Python 爬蟲(17):Requests 基礎使用
小白學 Python 爬蟲(18):Requests 進階操作
小白學 Python 爬蟲(19):Xpath 基操
小白學 Python 爬蟲(20):Xpath 進階
小白學 Python 爬蟲(21):解析庫 Beautiful Soup(上)
小白學 Python 爬蟲(22):解析庫 Beautiful Soup(下)
小白學 Python 爬蟲(23):解析庫 pyquery 入門
引言
從本篇的標題各位同學應該已經猜到了,本篇又到了實戰環節~~~
2019 已經快過完了,按照本文推送的時間預估,到 2020 應該還有十來天的時間,又到了各個公司出各種 2019 榜單的時間,小編這裡呢,就先幫豆瓣搞一個 2019 電影評分排行榜,希望豆瓣官方看到不要打我。
鄭重聲明: 本文僅限用作學習等目的。
分析
還是先看一下我們要爬取的頁面:
鏈接:
<code>https://movie.douban.com/explore#!type
=movie&tag=%E7%83
%AD%E9%97
%A8&sort
=time
&page_limit=20
&page_start=0
/<code>
思維敏捷的同學看著上面這個鏈接可能就已經發現了什麼,對的,這個鏈接上已經有分頁信息了。
page_limit 應該是一頁的元素, page_start 應該是這一頁開始的一個序號。
我們往下翻一下頁面,看看下面有沒有下一頁之類的按鈕,翻幾頁看下地址欄的變化是否和我們推測的一致。
emmmmmmmmm
小編猜錯了,這裡不是下一頁,是加載更多,不過問題不大,一個意思,先點一下我們看下地址欄:
<code>https://movie.douban.com/explore#!type
=movie&tag=%E7%83
%AD%E9%97
%A8&sort
=time
&page_limit=20
&page_start=20
/<code>
和前面一個地址作對比可以發現,只有最後的 page_start 參數有變化,說明我們剛才上面的猜測沒有問題。
加載更多多點幾次,可以發現,這裡的電影是可以一直往後排的,可以加載到 2018 年的數據:
emmmmmmmmm,有點尷尬,這個數據竟然手動翻出來了,理論上是應該程序自己判斷的。
這裡的懸浮層上已經顯示了我們想要的數據,接下來的問題是,我們如何獲得這個懸浮層上的數據,直接從 DOM 節點來取可以麼?
顯然是不行的,不信可以自己動手試試,每個電影的懸浮層其實都是同一個 DOM 節點,只是裡面填充的數據不同,顯然這個 DOM 節點中的數據是鼠標挪上去的時候才動態加載出來的。
那麼我們從哪裡能看到加載數據的來源呢?
如果上一篇實戰有仔細看實操過的同學應該已經想到了, Chrome 瀏覽器開發者模式中的 Network 標籤。
沒錯,就是這裡,我們看一下:
首先選擇 Network 標籤,然後在下面的標籤上選擇 XHR 。然後鼠標在不同的電影上移動,可以看到鼠標每次移到一張圖片上,就會有一個請求,我們看一下這個請求的響應信息:
<code>{"r"
:0
,"subject"
:{"episodes_count"
:""
,"star"
:"40"
,"blacklisted"
:"available"
,"title"
:"我身體裡的那個傢伙 내안의 그놈 (2019)"
,"url"
:"https:\/\/movie.douban.com\/subject\/27088750\/"
,"collection_status"
:""
,"rate"
:"7.2"
,"short_comment"
:{"content"
:"男主真的他媽帥 但是我真的接受不了和羅美蘭打k "
,"author"
:"SOUL"
},"is_tv"
:false
,"subtype"
:"Movie"
,"directors"
:["姜孝鎮"
],"actors"
:["鄭振永"
,"樸聖雄"
,"羅美蘭"
,"李垂珉"
,"李俊赫"
,"金光奎"
,"閔智雅"
,"尹敬浩"
,"金賢穆"
,"樸慶惠"
,"趙賢榮"
,"尹頌雅"
,"智燦"
,"金凡振 "
,"鄭元昌"
,"孫光業"
,"黃仁俊"
,"Dae-han Kim"
],"duration"
:"122分鐘"
,"region"
:"韓國"
,"playable"
:false
,"id"
:"27088750"
,"types"
:["劇情"
,"喜劇"
],"release_year"
:"2019"
}} /<code>
那麼我們剩下要關心的就是這個請求的地址了,先看下這個請求的地址:
<code>https
: /<code>
這裡看起來好像只有最後一個 subject_id 參數是變化的,其他的都是定死的,多看幾個請求檢驗下我們的推測,小編這裡就不檢驗了,免得嫌棄說小編水內容。
還有一個問題,最後這個 subject_id 的數據從哪裡來,好像沒見過的,小編憑藉自己多年豐富的開發經驗,猜測這個數據應該是在頁面的上的。我們接著看下這個電影的頁面 DOM 結構。
看到了沒,這裡的數據是來源於 DOM 結構上的 data-id 屬性。
PS:更新一件事情,一件異常尷尬的事情,小編偶然發現,點擊加載更多的時候,實際上是對應了一個 API 接口,這個接口的訪問地址如下:
<code>https://movie
.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort
=time
&page_limit=20
&page_start=20
/<code>
這個在 NetWork 中有看到,如下圖:
從圖中可以看到,這裡直接返回了 JSON 數據,並且這個 JSON 數據返回後,順便還修改了地址欄的數據。
這裡得到的數據如下:
<code>{"subjects"
:[ {"rate"
:"6.7"
,"cover_x"
:1382
,"title"
:"在無愛之森吶喊"
,"url"
:"https://movie.douban.com/subject/30337760/"
,"playable"
:false
,"cover"
:"https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2571542101.jpg"
,"id"
:"30337760"
,"cover_y"
:2048
,"is_new"
:false
} ] } /<code>
因為整體數據有 20 條,太長了放不下,小編這裡僅保留了一條數據。
編碼
有了上面的分析,其實寫代碼就已經很簡單了,我們所有需要用到的數據都可以直接從 API 接口中直接獲取到 JSON 的數據。
屢一下思路,首先我們從
<code>https://movie
.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort
=time
&page_limit=20
&page_start=0
/<code>
這個鏈接中直接獲取電影的相關數據,這裡對我們有用的數據是 id ,獲取到這個 id 後,再從
<code>https
: /<code>
這個鏈接中獲取到電影的詳情數據,用上面得到的 id 替換這裡的 subject_id 。
好像沒頁面 DOM 解析啥事兒了,哎,真的是一次失敗的選題,下次再也不選豆瓣了。
代碼內容有些簡單,小編直接貼出來吧,數據還是在 Mysql 中開了一張表做存放:
<code>import
requestsimport
pymysqldef
connect
()
: conn = pymysql.connect(host='localhost'
, port=3306
, user='root'
, password='password'
, database='test'
, charset='utf8mb4'
) cursor = conn.cursor()return
{"conn"
: conn,"cursor"
: cursor} connection = connect() conn, cursor = connection['conn'
], connection['cursor'
] sql_insert ="insert into douban2019(id, title, rate, short_comment, duration, subtype, region, release_year, create_date) values (%(id)s, %(title)s, %(rate)s, %(short_comment)s, %(duration)s, %(subtype)s, %(region)s, %(release_year)s, now())"
headers = {'User-Agent'
:'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
} flag =True
def
get_movie_list
(page_start)
: r = requests.get('https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=time&page_limit=20&page_start='
+ str(page_start), headers = headers)for
itemin
r.json()['subjects'
]: get_movie_info(item['id'
])def
get_movie_info
(subject_id)
: r = requests.get('https://movie.douban.com/j/subject_abstract?subject_id='
+ str(subject_id), headers=headers) subject = r.json()['subject'
]if
subject['release_year'
] !='2019'
:global
flag flag =False
return
print(subject) insert_data = {"id"
: subject['id'
],"title"
: subject['title'
],"rate"
: subject['rate'
],"short_comment"
: subject['short_comment'
]['content'
],"duration"
: subject['duration'
],"subtype"
: subject['subtype'
],"region"
: subject['region'
],"release_year"
: subject['release_year'
] } cursor.execute(sql_insert, insert_data) conn.commit() print(subject['title'
],'寫入完成'
)def
main
()
: num =0
while
(flag): get_movie_list(num) num +=20
if
__name__ =='__main__'
: main() /<code>
小結
最後小編做了一下簡單的統計,截止目前, 2019 豆瓣電影共計 312 部,評分超過 8.0 分的共計 37 部,超過 8.5 分的共計 14 部,超過 9.0 分的只有 1 部。
下表為評分超過 8.0 分的,還有沒看過的小夥伴可以抓緊時間看一下咯~~
名稱評分愛爾蘭人 The Irishman (2019)9.1銀河英雄傳說 Die Neue These 星亂 第1章 銀河英雄伝説 Die Neue These 星亂 第1章 (2019)8.9小丑 Joker (2019)8.8婚姻故事 Marriage Story (2019)8.8玩具總動員4 Toy Story 4 (2019)8.7寄生蟲 기생충 (2019)8.7代號基亞斯:復活的魯路修 コードギアス 復活のルルーシュ (2019)8.782年生的金智英 82년생 김지영 (2019)8.7克勞斯:聖誕節的秘密 Klaus (2019)8.6痛苦與榮耀 Dolor y gloria (2019)8.6青春期豬頭少年不做懷夢少女的夢 青春ブタ野郎はゆめみる少女の夢を見ない (2019)8.6復仇者聯盟4:終局之戰 Avengers: Endgame (2019)8.5哪吒之魔童降世 (2019)8.5普羅米亞 プロメア (2019)8.5少年的你 (2019)8.4少年泰坦出擊大戰少年泰坦 Teen Titans Go! vs Teen Titans (2019)8.4悲慘世界 Les misérables (2019)8.4我的一級兄弟 나의 특별한 형제 (2019)8.3燃燒女子的肖像 Portrait de la jeune fille en feu (2019)8.3再見鍾情 Mon inconnue (2019)8.3我在雨中等你 The Art of Racing in the Rain (2019)8.2羅小黑戰記 (2019)8.2行騙天下JP:浪漫篇 コンフィデンスマンJP (2019)8.2阿松 劇場版 劇場版 えいがのおそ松さん (2019)8.2蠟筆小新:新婚旅行颶風之遺失的野原廣志 映畫クレヨンしんちゃん 新婚旅行ハリケーン ~失われたひろし~ (2019)8.2馭風男孩 The Boy Who Harnessed the Wind (2019)8.1對不起,我們錯過了你 Sorry We Missed You (2019)8.1續命之徒:絕命毒師電影 El Camino: A Breaking Bad Movie (2019)8.1我失去了身體 J'ai perdu mon corps (2019)8.1最初的夢想 Chhichhore (2019)8.1千子2 センコロール コネクト (2019)8.0地久天長 (2019)8.0金髮男子 Un rubio (2019)8.0心理測量者SS2:第一衛士 PSYCHO-PASS サイコパス Sinners of the System Case.2「First Guardian」 (2019)8.0漫長的告別 長いお別れ (2019)8.0我的喜馬拉雅 (2019)8.0小委託人 어린 의뢰인 (2019)8.0
示例代碼
本系列的所有代碼小編都會放在代碼管理倉庫 Github 和 Gitee 上,方便大家取用。
示例代碼-Github
示例代碼-Gitee