前幾天在大神指導(dǎo)下動(dòng)手寫了年輕人第一個(gè)算得上程序的程序混驰。
一個(gè)豆瓣電影 TOP250 爬蟲
完整代碼已經(jīng)開源碌奉,放在我的 GitHub 上
網(wǎng)絡(luò)爬蟲
什么是網(wǎng)絡(luò)爬蟲俊性?
用人話講,爬蟲就是一個(gè)從互聯(lián)網(wǎng)上抓取信息的程序螃壤,比如我們需要一些電影信息,可以上新聞網(wǎng)站筋帖,或者我們需要一些新聞奸晴,可以上新聞網(wǎng)站,當(dāng)然我們最多的是使用搜索引擎幕随,關(guān)于搜索引擎后邊會(huì)講到蚁滋。
當(dāng)然這只在獲取少量信息的時(shí)候管用,如果我想要找的有幾千幾萬條的信息呢赘淮,這時(shí)候靠人工就顯得太費(fèi)時(shí)了。當(dāng)我們想獲取數(shù)據(jù)睦霎,只要網(wǎng)絡(luò)上有的梢卸,都可以通過爬蟲程序去抓取,所以爬蟲就是一種獲取數(shù)據(jù)的程序副女。
當(dāng)我們上網(wǎng)的時(shí)候蛤高,我們?cè)诟墒裁矗?/h2>
下邊以我們經(jīng)常用來測(cè)試網(wǎng)絡(luò)的一個(gè)網(wǎng)站為例
當(dāng)我們?cè)跒g覽器中輸入baidu.com
按下回車之后發(fā)生了什么?
- 瀏覽器會(huì)自動(dòng)幫我們補(bǔ)齊
https://www.baidu.com
- 瀏覽器首先向 DNS 服務(wù)器請(qǐng)求了
baidu.com
對(duì)應(yīng)的 IP 地址碑幅; - 瀏覽器得到 IP 地址之后戴陡,就會(huì)向這個(gè)地址發(fā)送一個(gè) HTTP 請(qǐng)求;
- 然后從百度的服務(wù)器端請(qǐng)求到首頁(yè)的 HTML 源碼沟涨;
- 最后通過瀏覽器引擎解析源碼恤批,再次向服務(wù)器發(fā)請(qǐng)求得到里面引用的 Javascript、CSS裹赴、圖片啊一些資源喜庞,最后就得到了百度首頁(yè)。
爬蟲要干什么棋返?
大多數(shù)情況下延都,爬蟲其實(shí)就是在模擬上面說的過程。
當(dāng)然爬蟲不會(huì)全部模擬一遍睛竣,而是會(huì)選擇合適的步驟模擬晰房。
比如大多數(shù)情況下只需要請(qǐng)求 HTML 源碼,并不需要請(qǐng)求 CSS 和 Javascript 射沟。在得到源碼后殊者,就像瀏覽器會(huì)解析 HTML 源碼一樣,爬蟲也會(huì)解析 HTML躏惋,然后篩選出我們想要的內(nèi)容保存幽污。
爬蟲在哪兒?
最大的爬蟲是什么簿姨?
當(dāng)然是 Goolge距误,百度等等這些搜索引擎簸搞,當(dāng)然這種說法只是通俗意義上的,搜索引擎大致分為下載系統(tǒng)准潭、分析系統(tǒng)趁俊、索引系統(tǒng)和查詢系統(tǒng),爬蟲是搜索引擎的第一道門檻刑然。再譬如寺擂,據(jù)說有幾億人在用的新聞客戶端,沒錯(cuò)泼掠,就是今日頭條怔软,其就是依靠爬蟲,在網(wǎng)易新聞择镇、騰訊新聞挡逼、搜狐新聞和其他新聞網(wǎng)站上爬取新聞數(shù)據(jù),雖然這種做法有點(diǎn)不道德吧…
爬蟲有什么用腻豌?
第一個(gè)例子是一個(gè)另類的資訊 APP——即刻家坎,它不制造新聞,也不同今日頭條無節(jié)操的從友商那拿東西吝梅,他完全可以自己定制或是選擇別人已經(jīng)制作好的話題來進(jìn)行追蹤虱疏,比如什么電腦病毒在全世界爆發(fā)了,像這些天鬧得比較火熱的比特幣病毒苏携,比如宮崎駿新作的最新消息啦做瞪,比如誰誰誰男神女神點(diǎn)贊微博了,又比如哪部電視劇更新了兜叨,這么多的提醒如果完全靠人工編輯審核那不得累死穿扳,估計(jì)即刻都活不過幾個(gè)月,但是這些提醒大多數(shù)靠的并不是人工国旷,而是網(wǎng)絡(luò)爬蟲矛物,這些爬蟲從社交網(wǎng)絡(luò),比如微博跪但,從視頻網(wǎng)站履羞,從其他地方抓取想要的信息然后推送。
第二個(gè)例子也是一個(gè) App —— Price Tag屡久,一個(gè) IOS 上的查價(jià) App忆首,也是一位在杭州的獨(dú)立開發(fā)者 61 開發(fā)的,他就利用了爬蟲在 App Store 服務(wù)器中爬取應(yīng)用的價(jià)格被环,然后將 App 的價(jià)格用圖表的形式直觀的顯示出來糙及。當(dāng)然你也會(huì)說爬取到價(jià)格之后有什么用呢?說的直白點(diǎn)筛欢,如何變現(xiàn)呢浸锨?還是以 Price Tag 為例唇聘,61 在兩方面進(jìn)行變現(xiàn),一個(gè)是通過 App 的內(nèi)購(gòu)柱搜,還有一個(gè)是通過和 Apple 合作迟郎,獲取聯(lián)盟令牌,這樣用戶在 Price Tag 中查詢點(diǎn)擊進(jìn)行的購(gòu)買行為聪蘸,61 就能分到 7% 的傭金宪肖。
Python
什么是 Python?
從我的感官來說健爬,Python 是一種類似于我們學(xué)過的 C 和 C++ 的高級(jí)語(yǔ)言控乾,但其語(yǔ)法比 C 和 C++ 簡(jiǎn)單的多的多。
同時(shí)浑劳,Python 更多的又被叫做腳本語(yǔ)言阱持。
Python 被大量應(yīng)用于 WEB 開發(fā)中,比如在國(guó)內(nèi)不存在的網(wǎng)站 YouTuBe魔熏、Instagram,還有小資文藝聚集地豆瓣鸽扁、知乎蒜绽、果殼,這些都是完全用 Python 開發(fā)的桶现,為什么呢躲雅?
因?yàn)槲乃嚽嗄甓几F,所有東西都得一個(gè)人搞骡和,于是就只能用 Python 了相赁。
——這是在知乎上看到的一個(gè)回答
當(dāng)然這不只是說笑,從這個(gè)回答也可以看出 Python 的強(qiáng)悍慰于,因?yàn)樗母鱾€(gè)模塊都是現(xiàn)成的 钮科,拿過來就能用,不用像 C++ 那樣要用到一個(gè)模塊還得自己去寫婆赠。
為什么選擇 Python绵脯?
-
其一:語(yǔ)法簡(jiǎn)單
舉個(gè)實(shí)例,完成一個(gè)最簡(jiǎn)單的程序休里,顯示請(qǐng)輸入你的名字
然后從鍵盤輸入名字
最后顯示Hello蛆挫,名字
用 C 語(yǔ)言寫
#include<stdio.h>` #include<string.h>` int main() { char name[20]; printf(“Please enter U name:”); scanf(“%s”,name); printf(“Hello,%s”,name); }
用 C++ 寫
#include<iostream> #include<string> using namespace std; int main() { char name[20]; cout<<“Please enter U name:”<<endl; cin>>name; cout<<“Hello,”<<name<<endl; }
用 Python 寫
print('Hello,',input('Please enter U name:'))
結(jié)果顯而易見
其二:Python 有成熟的爬蟲腳本語(yǔ)言,正因?yàn)槭悄_本語(yǔ)言妙黍,編寫調(diào)試方便悴侵,而且有多線程更加高效。
豆瓣電影 TOP250 實(shí)戰(zhàn)
搭建開發(fā)環(huán)境
我在這里裝了 Python 3.6
接著再需要安裝兩個(gè)第三方庫(kù)拭嫁,Requests 和 BeautifulSoup
Requests 用于處理 HTTP 請(qǐng)求可免;
BeautifulSoup 用來解析 HTML 源碼抓于;
這兩個(gè)庫(kù)在接下來會(huì)用到。
分析 HTML 結(jié)構(gòu)
開發(fā)環(huán)境搭建完成巴元,編寫爬蟲之前毡咏,我們需要先思考爬蟲需要干什么?目標(biāo)網(wǎng)站有什么特點(diǎn)逮刨。
今天我們的目標(biāo)是把豆瓣電影 TOP250網(wǎng)站上所有的電影名稱呕缭,上映年份,評(píng)分修己,拍攝地區(qū)和電影簡(jiǎn)介爬下來恢总。
先打開豆瓣電影 TOP250,檢查分析網(wǎng)站源碼睬愤。
OK片仿,在 HTML 源碼中我們可以得到一些信息了。
- 每頁(yè)有25條電影尤辱,共有10頁(yè)砂豌。
- 電影列表在頁(yè)面上的位置為一個(gè)
class
屬性為grid_view
的ol
標(biāo)簽中。 - 每條電影信息放在這個(gè)
ol
標(biāo)簽的一個(gè)li
標(biāo)簽里光督。 - 電影名稱放在一個(gè)
class
屬性為title
的span
標(biāo)簽中阳距。 - 上映年份,拍攝地區(qū)放在一個(gè)
class
屬性為bd
的div
標(biāo)簽下的一個(gè)p
標(biāo)簽中结借。 - 評(píng)分放在一個(gè)
class
屬性為star
的div
標(biāo)簽下的一個(gè) class 屬性為rating_num
的span
標(biāo)簽中筐摘。
下載網(wǎng)頁(yè)源碼
import requests as r
url = 'https://movie.douban.com/top250'
html = r.get(url).text
print(html)
只需要三句,最后一句打印出來用來判斷下載源碼是否成功船老。
網(wǎng)頁(yè)源碼已經(jīng)在控制臺(tái)打印出來咖熟。
解析網(wǎng)頁(yè)
OK,當(dāng)我們拿到網(wǎng)頁(yè)源碼之后柳畔,就需要解析 HTML 源碼了馍管。
這里,我們使用 BeautifulSoup 來搞定這件事荸镊。
import requests as r
from bs4 import BeautifulSoup as bs
url = 'https://movie.douban.com/top250'
html = r.get(url).text
items = bs(html).find_all('div','item')
for item in items:
movie_name = item.find('span','title').string
movie_rating = item.find('span','rating_num').string
movie_quote = item.find('span','inq').string
movie = (movie_name,movie_rating,movie_quote)
print(str(movie))
正則表達(dá)式
電影名稱咽斧,評(píng)分,簡(jiǎn)介就在父標(biāo)簽中躬存,從電影信息的父標(biāo)簽匹配就可以拿到张惹,但是上映年份和拍攝地區(qū)和其他的信息一起在一個(gè)父標(biāo)簽中,無法單獨(dú)拿出來岭洲,這時(shí)候就需要用到正則表達(dá)式了宛逗。
如何從這里取出上映年份呢,年份都是四個(gè)數(shù)字吧盾剩,所有只要取出四個(gè)數(shù)字雷激,用正則表達(dá)式是 \d{4}
那拍攝地區(qū)又該怎么取出呢替蔬?拍攝地區(qū)在兩個(gè)斜杠之間,所以我們只要取出兩個(gè)斜杠之間的信息屎暇,所以正則表達(dá)式是 (?<=\d./).*(?=/)
這邊解釋一下
(?<=\d./)
匹配1個(gè)數(shù)字后面跟斜杠承桥,但是輸出結(jié)果不包含斜杠
(?=/)
匹配后面跟斜杠,但是輸出不包括斜杠根悼。
.*
代表除換行符外任意字?jǐn)?shù)的任意字符
movie_info = item.find('div','bd').find('p','').get_text()
movie_year = re.findall('\d{4}',movie_info)[0]
movie_nation = re.findall('(?<=\d./).*(?=/)',movie_info)[0]
如何翻頁(yè)
到這一步凶异,我們已經(jīng)得到了當(dāng)前頁(yè)想要的數(shù)據(jù),那么如何處理翻頁(yè)的問題呢挤巡?
- 第一個(gè)想到辦法是找到頁(yè)碼導(dǎo)航頁(yè)的“下一頁(yè)”的鏈接剩彬,也很容易實(shí)現(xiàn)。
- 第二個(gè)辦法是觀察 URL 結(jié)構(gòu)發(fā)現(xiàn)的
所以可以用列表推導(dǎo)的方式來實(shí)現(xiàn)翻頁(yè)
url = ['https://movie.douban.com/top250?start={}'.format(str(i)) for i in range(0,226,25)]
多線程
在完成基本的架構(gòu)之后矿卑,就要開始進(jìn)行優(yōu)化了喉恋。
爬蟲一定要多線程,不然當(dāng)爬大量數(shù)據(jù)時(shí)就太慢了
比如說母廷,如果你用單線程發(fā)送一個(gè)請(qǐng)求轻黑,那必須在等結(jié)果返回之后再去發(fā)送下一個(gè)請(qǐng)求,而多線程呢琴昆,在發(fā)送完一個(gè)請(qǐng)求之后苔悦,不用等到結(jié)果返回,繼續(xù)發(fā)送下一個(gè)請(qǐng)求椎咧,這樣就減少了 CPU 的等待時(shí)間,也提高了 CPU 的使用效率把介。
拼裝
OK勤讽,到了最后一步,完成寫入模塊后把程序拼裝成一個(gè)完整的程序拗踢。但這個(gè)時(shí)候又遇到了一個(gè)問題脚牍,每次都只能爬到 107 個(gè)數(shù)據(jù),之后就開始報(bào)錯(cuò)巢墅。
然后進(jìn)網(wǎng)站去看了第 108 個(gè)數(shù)據(jù)诸狭,發(fā)現(xiàn)……
這丫的瘋狂動(dòng)物城沒有簡(jiǎn)介……
所以在簡(jiǎn)介這里加個(gè)判斷進(jìn)去。
OK君纫,到這里一段完整的豆瓣電影 TOP250 爬蟲就寫完了驯遇。
#!/usr/bin/env python
# encoding=utf-8
from bs4 import BeautifulSoup as bs
import requests as r
import csv
import re
import codecs
from multiprocessing.dummy import Pool
movies = []
url = ['https://movie.douban.com/top250?start={}'.format(str(i)) for i in range(0,226,25)]
def soup(url):
html = r.get(url).text
items = bs(html).find_all('div','item')
for item in items:
movie_name = item.find('span','title').string
movie_rating = item.find('span','rating_num').string
try:
movie_quote = item.find('span','inq').string
except:
movie_quote='none'
movie_info = item.find('div','bd').find('p','').get_text()
movie_year = re.findall('\d{4}',movie_info)[0]
movie_nation = re.findall('(?<=\d./).*(?=/)',movie_info)[0]
movie = (movie_name,movie_year,movie_rating,movie_nation,movie_quote)
#print(str(movie))
movies.append(movie)
pool = Pool()
pool.map(soup, url)
pool.close()
pool.join()
with codecs.open('DoubanMovieTOP250.csv','w','utf-8',newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(["電影名稱","上映年份","評(píng)分","地區(qū)","簡(jiǎn)介"])
writer.writerows(movies)
運(yùn)行
運(yùn)行程序,生成一個(gè) CSV 文件蓄髓。
數(shù)據(jù)淺析
- 電影拿走不謝叉庐,請(qǐng)叫我雷鋒
- 美國(guó),中國(guó)会喝,日本上榜電影排前三
- 主要的電影內(nèi)容:信仰陡叠,青春玩郊,科幻,情懷等
- 電影數(shù)最多的幾年為 1995~2013枉阵,近幾年電影較少译红,總結(jié)了一下原因,大概是:雖然制片投入和電影效果越來越好兴溜,但內(nèi)容卻沒以前那么好了侦厚,換句話說,人人可以當(dāng)導(dǎo)演的年代昵慌,編劇才是最重要的假夺。