0x000 前言
最近和公司的IOS談起脆霎,以前我們是做移動端总处,現(xiàn)在已經(jīng)和前端混為一談了。 也是 睛蛛, 做了一年多的Android 鹦马,一直都是在寫界面展示數(shù)據(jù),寫交互 忆肾,存儲簡單的數(shù)據(jù) 荸频,最主要的業(yè)務(wù)數(shù)據(jù)與業(yè)務(wù)邏輯都在服務(wù)器端,這讓我有種離了服務(wù)端客冈,我們只能玩玩單機和聯(lián)機了 旭从。作為曾經(jīng)的web開發(fā),深知服務(wù)端的重要性郊酒,我們要想做一款好的產(chǎn)品遇绞,沒有服務(wù)端是萬萬不能的 , 而我們做移動端的更是要知曉一些服務(wù)端的知識 燎窘。
我有一個夢想摹闽,做一款屬于自己的產(chǎn)品 ,但苦于沒有足夠的數(shù)據(jù)褐健,于是就開始了我的Python爬蟲之旅 付鹿。(如果有時間澜汤,我會寫如何從零收集數(shù)據(jù)-->服務(wù)器端的建立 --> 客戶端的編寫 , 一整套教程)
0x001 Python基礎(chǔ)
Python是一門很簡潔的語言 舵匾, 因為市面上介紹Python語法的教程很多 俊抵, 大多數(shù)都很好 ,我這里就不啰嗦了 坐梯, 下面貼出我看過的網(wǎng)站:
0x002 網(wǎng)絡(luò)爬蟲
網(wǎng)絡(luò)爬蟲是一個自動提取網(wǎng)頁的程序徽诲,它為搜索引擎從萬維網(wǎng)上下載網(wǎng)頁,是搜索引擎的重要組成吵血。傳統(tǒng)爬蟲從一個或若干初始網(wǎng)頁的URL開始谎替,獲得初始網(wǎng)頁上的URL,在抓取網(wǎng)頁的過程中蹋辅,不斷從當(dāng)前頁面上抽取新的URL放入隊列,直到滿足系統(tǒng)的一定停止條件钱贯。[來自百度]
我們通過URL讓爬蟲來爬取我們想要的內(nèi)容 , 根據(jù)我們的規(guī)則進行深入爬取 侦另, 比如說 秩命, 我們想要爬取正在上映的電影數(shù)據(jù) , 并下載電影海報褒傅,其他的內(nèi)容我們忽略掉弃锐,那么我們就需要根據(jù)網(wǎng)頁內(nèi)容的規(guī)則來制訂,哪些內(nèi)容是我們需要的樊卓,哪些是我們不需要的拿愧。
0x003 爬取豆瓣電影數(shù)據(jù)
第一步 : 打開豆瓣電影 , 分析網(wǎng)頁結(jié)構(gòu)
我們爬取的網(wǎng)址是 https://movie.douban.com/cinema/nowplaying/shenzhen/
碌尔,里面有一個的模塊叫正在上映 浇辜, 我們就解析這個模塊,其他的內(nèi)容我們忽略掉唾戚。chrome(firefox)按f12查看網(wǎng)頁結(jié)構(gòu)柳洋。
我們可以用元素選擇工具,選中我們要分析的數(shù)據(jù)叹坦,firebug控制臺就會將選中html結(jié)構(gòu)展示出來熊镣。我們通過分析 , 我們想要的數(shù)據(jù)是在一個ul標(biāo)簽里面募书,每一個li標(biāo)簽就是一部電影的數(shù)據(jù)绪囱,我們只需要取出里面的li中的數(shù)據(jù)即可。豆瓣的前臺使用了數(shù)據(jù)綁定的技術(shù) 莹捡,這為我們獲取數(shù)據(jù)鬼吵,方便了不少 ,我們直接取出li中的屬性就可以將電影信息獲取到了 篮赢。
第二步 : 下載網(wǎng)頁內(nèi)容
我們需要將網(wǎng)頁中的數(shù)據(jù)提取出去 齿椅, 就需要先將網(wǎng)頁內(nèi)容下載下來琉挖,寫Java程序的朋友應(yīng)該知道 , 如果使用Java內(nèi)置網(wǎng)絡(luò)請求涣脚,將網(wǎng)頁數(shù)據(jù)下載下來示辈,那將要寫多少行代碼,實話說遣蚀,Java的網(wǎng)絡(luò)請求真的很不友好矾麻,幸而Java社區(qū)異常強大,涌現(xiàn)了一批又一批比較好用的網(wǎng)絡(luò)請求框架妙同,如OkHttp,liteHttp等等射富,大大簡化了我們的工作。但是粥帚,在Python中,將一個網(wǎng)頁數(shù)據(jù)下載下來限次,只需要一行 芒涡。
with request.urlopen('https://movie.douban.com/cinema/nowplaying/shenzhen/') as response:
這是Python內(nèi)置的urllib下的requests對象,將下載好的二進制數(shù)據(jù)封裝在response對象里面卖漫,使用readlines
方法就可以將其讀取出來 费尽。
from urllib import request
from urllib import parse
# 打開鏈接,并得到返回值
with request.urlopen('https://movie.douban.com/cinema/nowplaying/shenzhen/') as response:
conent_list = response.readlines() # 得到一個byte類型的list
#打印內(nèi)容
for content in conent_list:
print(content .decode()) # 因為content是byte類型羊始,所以需要解碼成str類型
雖然內(nèi)置urlib可以完成我們的需求旱幼,但是我們不會用系統(tǒng)內(nèi)置,因為還是比較麻煩突委,我們將使用富有盛名的requets庫柏卤,雖然名字和系統(tǒng)內(nèi)置的差不多,但是確實截然不同的庫 匀油。系統(tǒng)庫是在urllib模塊下缘缚,而requests就在requests模塊下 。
安裝requests
pip install requests
example
# url 請求地址
# headers 請求頭
url = 'https://movie.douban.com/cinema/nowplaying/shenzhen/'
res = requests.get(url,headers=headers)
res.text() # 得到請求的文本內(nèi)容
好戲敌蚜,馬上開場
第三步 : 解析網(wǎng)頁內(nèi)容
解析是使用的Python內(nèi)置的Html解析器桥滨,類似Java的jsoup.jar提供的api 。都是通過遍歷Html dom樹來進行分析弛车,判斷需要的tag 齐媒,然后進行屬性解析。Python正在強大的Html解析器是纷跛,XPath解析喻括,也是scrapy爬蟲庫內(nèi)置的解析器 ,當(dāng)然還是有beautifulsoup 忽舟。
1.引入HTMLPaser
from html.parser import HTMLParser
2.新建解析類双妨,繼承HTMLPaser
class MoviesParser(HTMLParser):
3.overload handle_starttag方法 淮阐, 解析標(biāo)簽
class MoviesParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
# 電影列表list集合
self.movies = []
# 標(biāo)簽是否在li標(biāo)簽中的標(biāo)識
self.img_into_movie = False
self.a_into_movie = False
def handle_starttag(self, tag, attrs):
# 因為屬性都是key=val形式 , 所以根據(jù)屬性名稱取值
def _attr(attrs,attr_name):
for attr in attrs:
if attr[0] == attr_name:
return attr[1]
return None
# 取出屬性值 , 根據(jù)每個Item的特征取值 刁品,因為有些Item的屬性可能會重復(fù) 泣特, 所以要盡量找出差異性,這樣才能保證數(shù)據(jù)的準(zhǔn)確性
if tag == 'li' and _attr(attrs,'data-title') and _attr(attrs,'data-category') == 'nowplaying':
movie = {}
movie['title'] = _attr(attrs,'data-title')
movie['score'] = _attr(attrs,'data-score')
movie['star'] = _attr(attrs,'data-star')
movie['duration'] = _attr(attrs,'data-duration')
movie['region'] = _attr(attrs,'data-region')
movie['director'] = _attr(attrs,'data-director')
movie['actors'] = _attr(attrs,'data-actors')
self.movies.append(movie)
self.img_into_movie = True
self.a_into_movie = True
#獲取海報圖片
if tag == 'img' and self.img_into_movie:
self.img_into_movie = False
img_src = _attr(attrs,'src')
movie = self.movies[len(self.movies) -1]
movie['poster_url'] = img_src
# 下載圖片
donwload_poster_url(img_src)
# 解析a標(biāo)簽挑随,提取電影詳情頁的Url
if tag == 'a' and self.a_into_movie:
if _attr(attrs,'data-psource') == 'title':
self.a_into_movie = False
movie_url = _attr(attrs,'href')
movie = self.movies[len(self.movies) -1]
movie['movie_url'] = movie_url
這是爬蟲中最關(guān)鍵的部分状您,數(shù)據(jù)解析是保證數(shù)據(jù)正確的性的地方,解析沒做好兜挨,就可能存在很多臟數(shù)據(jù)膏孟,這是我們應(yīng)當(dāng)避免的 。
第四步 : 下載圖片
# 下載圖片
def donwload_poster_url(url):
res = requests.get(url)
file_name = str.split(url,'/')[-1]
file_path = 'poster_img/' + file_name
print('download img file_path = ',file_path)
with open(file_path,'wb') as f:
f.write(res.content)
我們直接使用requests庫拌汇,get圖片地址柒桑,得到圖片的二進制數(shù)據(jù),再見二進制數(shù)據(jù)寫入到文件中噪舀,這樣我們的圖片文件就下載好了魁淳。
完整源碼
# 使用requests爬豆瓣正在上映的電影
from html.parser import HTMLParser
import requests
class MoviesParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.movies = []
self.img_into_movie = False
self.a_into_movie = False
def handle_starttag(self, tag, attrs):
# 根據(jù)屬性名稱取值
def _attr(attrs,attr_name):
for attr in attrs:
if attr[0] == attr_name:
return attr[1]
return None
# 取出屬性值
if tag == 'li' and _attr(attrs,'data-title') and _attr(attrs,'data-category') == 'nowplaying':
movie = {}
movie['title'] = _attr(attrs,'data-title')
movie['score'] = _attr(attrs,'data-score')
movie['star'] = _attr(attrs,'data-star')
movie['duration'] = _attr(attrs,'data-duration')
movie['region'] = _attr(attrs,'data-region')
movie['director'] = _attr(attrs,'data-director')
movie['actors'] = _attr(attrs,'data-actors')
self.movies.append(movie)
self.img_into_movie = True
self.a_into_movie = True
#獲取海報圖片
if tag == 'img' and self.img_into_movie:
self.img_into_movie = False
img_src = _attr(attrs,'src')
movie = self.movies[len(self.movies) -1]
movie['poster_url'] = img_src
donwload_poster_url(img_src)
if tag == 'a' and self.a_into_movie:
if _attr(attrs,'data-psource') == 'title':
self.a_into_movie = False
movie_url = _attr(attrs,'href')
movie = self.movies[len(self.movies) -1]
movie['movie_url'] = movie_url
# 下載圖片
def donwload_poster_url(url):
res = requests.get(url)
file_name = str.split(url,'/')[-1]
file_path = 'poster_img/' + file_name
print('download img file_path = ',file_path)
with open(file_path,'wb') as f:
f.write(res.content)
def douban_movies(url):
#首先構(gòu)建請求頭 ,模擬瀏覽器請求頭
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}
# # 打開鏈接与倡,并得到返回值
res = requests.get(url,headers=headers)
# 創(chuàng)建Html解析器
moviesParser = MoviesParser()
# 解析Html數(shù)據(jù)
moviesParser.feed(res.text)
return moviesParser.movies
if __name__ == '__main__':
url_str = 'https://movie.douban.com/cinema/nowplaying/shenzhen/'
movies_res = douban_movies(url_str)
import json
json_str = json.dumps(movies_res,sort_keys=True,indent=4,separators=(',',': '))
# 打印json數(shù)據(jù)
print(json_str)
從下載數(shù)據(jù)到解析Html界逛,只用了七十多行 ,包含了注釋和空格纺座,真是人生苦短息拜,我用Python 。
0x004 結(jié)語
Python確實是一個比較簡潔的語言净响,學(xué)起來也相對比較輕松少欺,各種庫應(yīng)有盡有,可以作為獲取數(shù)據(jù)的不二選擇 别惦。其實狈茉,解析Html 還可以簡化,使用Xpath更加簡潔掸掸,幾行代碼就可以搞定氯庆,在后續(xù)的文章中,我會逐一介紹 扰付。
有時候感覺文字的表現(xiàn)力真是太弱了堤撵,特別是對于技術(shù)文章,因為涉及的多羽莺,很多時候?qū)懼鴮懼蛦铝耸底颍缓髣h掉 。 俗話說:子不如表 盐固, 表不如圖荒给,圖不如視頻 丈挟。最近關(guān)注了一個簡書的作者,他做了一系列的視頻志电,講得挺好的曙咽,我思考著,要不要出個爬蟲的視頻教程 挑辆。