初識python
作為一位前端開發(fā)人員,一直以來,接觸最多的也都是html,css,js;有時候會需要搭建一些node服務(wù)器,寫一些node后臺代碼,僅此而已;
而python,只是在以前使用某些軟件時,需要python環(huán)境,就安裝到了電腦,然后,從來都沒有真正的接觸過它;
隨著數(shù)據(jù)分析、深度學(xué)習(xí)、機(jī)器學(xué)習(xí)的發(fā)展,也讓我開始了python的學(xué)習(xí)之旅;雖然都說,不管是什么語言,基本上都是相通的;但是對于一種你從未接觸過的語言,基礎(chǔ),還是非常重要的;所以,我還是默默的打開了瀏覽器,在百度中搜索了“python基礎(chǔ)教程”,最后找到了Python 基礎(chǔ)教程 | 菜鳥教程,在這里面,有環(huán)境的搭建、基本語法、數(shù)據(jù)類型、常用函數(shù)等非常基礎(chǔ)的教程,我覺得非常適合我這種從來沒有接觸過的人;隨后,我花了幾個小時的時間,將這里面基礎(chǔ)教程的所有內(nèi)容看了一遍,在看的過程中,也跟著做了一些練習(xí);
雖然說,看了基礎(chǔ)教程,也頂多你大概能夠使用python進(jìn)行一些簡單的運(yùn)算而已;而作為開發(fā)人員,都明白一個道理,最好、最快的學(xué)習(xí)一種語言的方式當(dāng)然是從一個簡單的項(xiàng)目開始,自己能夠從頭開始做一個自己覺得有意思但是難度不是很大的東西,這樣,不僅能夠讓自己能夠更快的去理解這種語言,而且,會使自己更有學(xué)習(xí)的動力和信心;因此,我開始了一個簡單的網(wǎng)站的數(shù)據(jù)爬取的實(shí)現(xiàn),因?yàn)閷τ谖疫@種前端出生的人來說,與瀏覽器打交道,與網(wǎng)頁打交道,相對來說,簡單很多;
開始
首先,我肯定不知道從何處開始,因此,我只能繼續(xù)百度“python 爬蟲”;最后找到了這篇文章“Python爬蟲實(shí)戰(zhàn)二之爬取百度貼吧帖子”,為什么選擇它呢?只有一個原因,在它的文章最后,有完整的代碼,我沒去看里面的描述,而是,直接用sublime直接創(chuàng)建了一個名為
$ pachong.py
的文件,然后將代碼全部拷貝進(jìn)去(雖然說可能代碼根本運(yùn)行不通);
隨后,我開始了我的爬蟲之旅,一開始,我就直接在拷貝的代碼基礎(chǔ)上直接修改,而不同的是它爬取的是百度貼吧,我爬取的是汽車之家。然后代碼就在這兒pachong.py(由于代碼較多,就不直接貼出來了,有興趣的,可以直接點(diǎn)鏈接進(jìn)github查看);在這里面有個很坑的地方,它的所有篩選自己需要數(shù)據(jù)的方式是通過正則匹配的方式實(shí)現(xiàn)的,而我什么也不知道的跟著學(xué)著,也使用正則,讓我為了從整個html中篩選自己需要的,花費(fèi)了太多的時間,而且效果一般;但是后面才發(fā)現(xiàn),已經(jīng)有很完善的專門匹配頁面內(nèi)容方式的Python庫BeautifulSoup(這是它的中文文檔鏈接);
使用BeautifulSoup重寫
當(dāng)發(fā)現(xiàn)了BeautifulSoup之后,簡單看了一篇BeautifulSoup教程,看完之后,如獲至寶,因?yàn)樗氖褂梅绞嚼锩嬗泻芏嗪颓岸死锩嫒カ@取元素,獲取節(jié)點(diǎn)有異曲同工之妙;而我最喜歡使用的,是里面的“CSS選擇器”方式,簡直和前端使用jquery一模一樣,比如:
通過標(biāo)簽名查找
print soup.select('p')
通過類名查找
print soup.select('.sister')
通過 id 名查找
print soup.select('#link1')
......等等;
因此,我果斷選擇使用BeautifulSoup對上面最初的版本進(jìn)行重寫,尤其是在對內(nèi)容的篩選;而這一次,我選擇了去爬取“螞蜂窩”的游記;
作為前端開發(fā)的我,對于Chrome DevTools的使用,簡直是易如反掌;所以很容易就可以找出怎么去獲取自己需要的內(nèi)容(到底是用類名查找還是id查找,或者是什么組合查找),對于Chrome DevTools不是很熟的同學(xué),可以看下這個感覺挺全的;
以爬取螞蜂窩的游記為例,比如這一篇:
1、通過request獲取網(wǎng)頁
request = urllib2.Request('http://www.mafengwo.cn/i/6958115.html')
response = urllib2.urlopen(request)
soup = BeautifulSoup(response.read().decode('utf8').encode('utf8'), "html.parser")
2、獲取標(biāo)題
return soup.select('.headtext')[0].string
3、獲取文章html內(nèi)容
article = soup.select('.vc_article')[0]
html = article.prettify()
4、獲取用戶信息(如頭像,名稱),這個稍微要復(fù)雜一些,因?yàn)樗谏厦娴谝徊街苯诱埱蟮膆tml頁面里面是找不到的,然后通過Chrome DevTools中的Network功能,可以找到拉取這些信息的請求地址“https://www.mafengwo.cn/note/pagelet/pagelet/headOperateApi?callback=jQuery1810173556954190627_1492868085919¶ms=%7B%22iid%22%3A%226958115%22%7D&_=1492868086249”,細(xì)心的你可能會發(fā)現(xiàn),在這個請求中有個“6958115”,這個不就是第一步里面頁面的結(jié)尾數(shù)字嗎,我們可以理解為id;那么,這個請求就是通過
開始
https://www.mafengwo.cn/note/__pagelet__/pagelet/headOperateApi?callback=jQuery1810173556954190627_1492868085919¶ms=%7B%22iid%22%3A%226958115%22%7D&_=1492868086249](https://www.mafengwo.cn/note/__pagelet__/pagelet/headOperateApi?callback=jQuery1810173556954190627_1492868085919¶ms=%7B%22iid%22%3A%22
加上 id
6958115
加上結(jié)尾
%22%7D&_=1492868086249
最后組合成的一個完整的url,然后通過第一步請求后,將獲取的數(shù)據(jù)通過正則的方式,只去html部分,然后通過BeautifulSoup取出對應(yīng)的頭像,姓名;
//其中article_id就是上面說的第一步鏈接結(jié)尾數(shù)字“6958115”
userInfoURL = 'https://www.mafengwo.cn/note/__pagelet__/pagelet/headOperateApi?callback=jQuery1810173556954190627_1492868085919¶ms=%7B%22iid%22%3A%22' + article_id +'%22%7D&_=1492868086249'
request = urllib2.Request(userInfoURL)
response = urllib2.urlopen(request)
result = response.read()
pattern = re.compile('jQuery.*?\(')
pattern1 = re.compile('\)\;')
result = re.sub(pattern, "", result)
result = re.sub(pattern1, "", result)
result = eval(result)
html = result['data']['html']
soup = BeautifulSoup(html, "html.parser")
//頭像
avatar = soup.select('.per_pic img')[0]['src']
//用戶名
name = soup.select('.per_name')[0]['title'].encode('utf-8')
搭建服務(wù)器
在你爬取到數(shù)據(jù)之后,你總會希望能夠在界面上能夠真正的展示出來,所以,我就決定使用python搭建一個簡單的本地服務(wù)器;因?yàn)橥ㄟ^學(xué)習(xí)python的時候發(fā)現(xiàn)菜鳥教程確實(shí)比較適合我這種菜鳥的,所以就看完了Django 菜鳥教程,這個教程雖然簡單,但是全面,對我做一個簡單服務(wù)器來說,足夠了;然后,就通過這個教程搭建了一個簡單的python服務(wù)器,在瀏覽器中,就可以看到自己爬取的頁面了,具體可見整個項(xiàng)目代碼
mongodb 數(shù)據(jù)保存、使用
在你爬取一篇過后,肯定是不會停止的,所以,我們總會找個能夠保持我們爬取數(shù)據(jù)的地方(數(shù)據(jù)庫);因?yàn)橐郧霸谶M(jìn)行node開發(fā)的時候,使用過mongodb,所以就選擇了它作為我保存數(shù)據(jù)的地方;使用python來操作mongodb,我沒有找到相關(guān)比較好的文章,所以就直接參考的PyMongo 3.4.0 documentation這個api文檔,這個夜比較詳細(xì)和簡單,對于我來說夠用了;它包含了如何和mongodb鏈接、如何獲取數(shù)據(jù)庫,如何獲取數(shù)據(jù)庫里面的collection,如何查找、添加數(shù)據(jù)等等;其實(shí)很簡單:
from pymongo import MongoClient
import settings
#連接mongodb
client=MongoClient('mongodb://127.0.0.1:27017/')
//獲取數(shù)據(jù)庫
db = client[settings.DBNAME]
// 獲取數(shù)據(jù)庫里面的collection
def getCollection(collection):
if(collection):
return db[collection]
else:
return None
數(shù)據(jù)保存的代碼就不貼出來了,有興趣可以看這個models.py.
自動爬取所有游記
當(dāng)你想爬取更多的時候,你肯定不希望是自己手動去查找一個一個的id,然后手動爬取,所以,我們就希望有更加自動化的爬取方式;對于這種方式,必須得解決下面兩個問題:
- 獲取所有的游記id(就形如“6958115”這種的東西)
- 解決因?yàn)樽詣踊埱筮^快,過于頻繁,導(dǎo)致“服務(wù)器拒絕訪問”的問題
第一個,獲取所有id;當(dāng)你使用Chrome DevTools Network去看列表下一頁的請求的時候,你會發(fā)現(xiàn),它訪問了一個“http://www.mafengwo.cn/note/pagelet/pagelet/recommendNoteApi?callback=jQuery18103478581123017468_1492999122522¶ms=%7B%22type%22%3A0%2C%22objid%22%3A0%2C%22page%22%3A1%2C%22ajax%22%3A1%2C%22retina%22%3A0%7D&_=1492999206862”這樣的鏈接,它也是和上面獲取用戶信息一樣分成三個部分,其中有個第幾頁的參數(shù)“page=1”,這里面就返回了所有的列表的html代碼,其中包括我們需要的id;
url = 'http://www.mafengwo.cn/note/__pagelet__/pagelet/recommendNoteApi?callback=jQuery18103478581123017468_1492999122522¶ms=%7B%22type%22%3A0%2C%22objid%22%3A0%2C%22page%22%3A'+str(page)+'%2C%22ajax%22%3A1%2C%22retina%22%3A0%7D&_=1492999206862'
request = urllib2.Request(url)
response = urllib2.urlopen(request)
result = response.read()
pattern = re.compile('jQuery.*?\(')
pattern1 = re.compile('\)\;')
result = re.sub(pattern, "", result)
result = re.sub(pattern1, "", result)
result = eval(result)
html = result['data']['html']
html = html.replace('\\/', '/')
html = html.decode('string-escape')
soup = BeautifulSoup(html, "html.parser")
links = soup.select('.tn-item .tn-image a')
_ids = []
for link in links:
_id = link['href'].replace('/i/', '').replace('.html', '')
_ids.append(_id)
然后我們就會根據(jù)每一頁獲取的id循環(huán)自動跑爬取對應(yīng)頁面數(shù)據(jù)并保存至數(shù)據(jù)庫,每跑完一頁的數(shù)據(jù),就會遞歸跑下一頁數(shù)據(jù),直到最后一頁
只是在跑的過程中,就會遇到第二個問題,因?yàn)樵L問過快、過于頻繁而導(dǎo)致服務(wù)器拒絕訪問,在這兒我沒有通過網(wǎng)上說的各種高端的方式,而是采用的比較笨重的方式,就是在跑完一個請求后,讓程序休息幾秒鐘,再去進(jìn)行下一個請求,我將時間設(shè)置的10秒,目前沒有拒絕訪問的問題,只是跑起來比較慢一些;
import time
.....
time.sleep(10)
.....
對于上面的所有代碼,如果感興趣的,整個項(xiàng)目代碼可以在這兒(github)找到.
目前,爬取就差不多只有這些,后面會慢慢繼續(xù)去完善和學(xué)習(xí);對于我來說,python的學(xué)習(xí)之路也才剛剛開始;