人生苦短,我用Python && C#典阵。
1.引言
最近初學(xué)Python奋渔,寫爬蟲上癮。爬了豆瓣練手壮啊,又爬了公司的論壇生成詞云分析年度關(guān)鍵詞嫉鲸。最近琢磨著2017又僅剩兩月了,我的年度關(guān)鍵詞是啥歹啼?
所以自然想到爬取下自己的微信朋友圈玄渗,來(lái)個(gè)詞頻分析座菠,生成屬于自己的年度關(guān)鍵詞詞云。
朋友圈的爬取是非常有難度的藤树,因?yàn)槲⑿鸥緵](méi)有暴露API入口去爬取數(shù)據(jù)浴滴。
但它山之石,可以攻玉也榄。
通過(guò)各種搜索發(fā)現(xiàn)巡莹,已經(jīng)有第三方工具可以做到朋友圈的導(dǎo)出司志。其中微信公眾號(hào)【出書啦】就提供了這樣一種服務(wù)甜紫,支持朋友圈導(dǎo)出,并排版生成微信書骂远。
而對(duì)朋友圈的爬取就是基于【出書啦】爬取朋友圈后生成網(wǎng)頁(yè)后的二次爬取囚霸。
有點(diǎn)爬蟲經(jīng)驗(yàn)的,只要拿到導(dǎo)出朋友圈的URL激才,后面的爬蟲就不足為道了拓型。但本著分享和總結(jié)的精神,還是和大家娓娓道來(lái)瘸恼。
=文中涉及個(gè)人隱私內(nèi)容做了特殊處理=
2.獲取朋友圈數(shù)據(jù)入口
上面已經(jīng)介紹過(guò)了朋友圈的數(shù)據(jù)爬取是基于【出書啦】微信公眾號(hào)生成的在線微信書數(shù)據(jù)的二次爬取劣挫。
具體步驟很簡(jiǎn)單:
- 關(guān)注【出書啦】微信公眾號(hào)
- 點(diǎn)擊【創(chuàng)作書籍】-->【微信書】-->【開始制作】-->【添加隨機(jī)分配的出書啦小編為好友即可】
- 稍等片刻,微信書制作完畢东帅,會(huì)收到小編發(fā)送的消息提醒压固,如下圖所示。
點(diǎn)擊上圖的鏈接靠闭,我們就可以看到按照月份重新排版的朋友圈數(shù)據(jù)帐我,如下圖所示:
至此,我們拿到朋友圈的數(shù)據(jù)入口——【出書啦】排版生成的微信書鏈接愧膀。
寫過(guò)爬蟲的拦键,后面就可以直接略過(guò)了。
當(dāng)然檩淋,沒(méi)寫過(guò)爬蟲也不想動(dòng)手的芬为,也可以把【出書啦】生成的微信書鏈接留言或私信給我,我?guī)湍惬@取年度關(guān)鍵詞蟀悦。
3.環(huán)境準(zhǔn)備
本文所寫爬蟲基于python2.7 + scrapy + jieba + wordcloud媚朦,使用VS Code IDE。
4.生成爬蟲項(xiàng)目
第一步:命令行執(zhí)行scrapy startproject weixin_moment
涎拉,生成Scrapy爬蟲項(xiàng)目瑞侮。
第二步:進(jìn)入創(chuàng)建的weixin_moment目錄的圆,執(zhí)行scrapy genspider 'moment' 'chushu.la'
創(chuàng)建朋友圈爬蟲。
執(zhí)行以上兩步后的文件夾結(jié)構(gòu)如下:
5.分析數(shù)據(jù)源
數(shù)據(jù)的準(zhǔn)確抓取半火,需要對(duì)數(shù)據(jù)源進(jìn)行準(zhǔn)確分析越妈。這一步我們就要來(lái)分析【出書啦】生成的微信書鏈接的數(shù)據(jù)加載方式。老規(guī)矩钮糖,F(xiàn)12開發(fā)者工具用起來(lái)梅掠。
從上圖我們可以看出這是一個(gè)get請(qǐng)求,返回的json類型格式數(shù)據(jù)店归。
點(diǎn)擊Preview頁(yè)簽可以看到如下圖所示的數(shù)據(jù):
從圖中可以看到返回的目錄導(dǎo)航數(shù)據(jù)包阎抒,其數(shù)據(jù)是按月份進(jìn)行加載的。當(dāng)點(diǎn)擊導(dǎo)航按鈕消痛,其加載對(duì)應(yīng)月份的朋友圈數(shù)據(jù)且叁。
我們點(diǎn)擊【2014-3】再觀察網(wǎng)絡(luò)請(qǐng)求,發(fā)現(xiàn)如下請(qǐng)求:
從以上數(shù)據(jù)我們可以明細(xì)看出秩伞,其采用的是用json傳參的post的方式請(qǐng)求數(shù)據(jù)包逞带。點(diǎn)擊Preview頁(yè)簽,看到返回的分頁(yè)JSON數(shù)據(jù)包纱新。
展開某個(gè)節(jié)點(diǎn)展氓,我們可以發(fā)現(xiàn)朋友圈數(shù)據(jù)藏在data/paras節(jié)點(diǎn)下。
至此脸爱,我們完成數(shù)據(jù)的來(lái)源分析遇汞。
6.蜘蛛來(lái)也
完成了數(shù)據(jù)源分析,我們只需構(gòu)造數(shù)據(jù)請(qǐng)求阅羹,并進(jìn)行正確的數(shù)據(jù)解析勺疼,即可拿到我們想要的數(shù)據(jù)!
6.1.請(qǐng)求導(dǎo)航數(shù)據(jù)包
修改moment.py定義start_requests
方法:
bookid = '12345678' #請(qǐng)?zhí)顚憽境鰰病糠祷劓溄又械臄?shù)字部分
def start_requests(self):
"""
使用get方式請(qǐng)求導(dǎo)航數(shù)據(jù)包
"""
url = 'http://chushu.la/api/book/chushula-{0}?isAjax=1'.format(self.bookid) #獲取目錄的url
yield scrapy.Request(url, callback=self.parse)
重載parse
方法捏鱼,解析獲取到的導(dǎo)航數(shù)據(jù)包:
def parse(self, response):
"""
處理獲取到的導(dǎo)航數(shù)據(jù)包
"""
json_body = json.loads(response.body) #加載json數(shù)據(jù)包
catalogs = json_body['book']['catalogs'] #獲取json中的目錄數(shù)據(jù)包
6.2. 發(fā)送導(dǎo)航請(qǐng)求执庐,抓取朋友圈數(shù)據(jù)
根據(jù)上面跟蹤到發(fā)出的http導(dǎo)航請(qǐng)求,要想抓取到朋友圈數(shù)據(jù)导梆,我們需要根據(jù)發(fā)出的請(qǐng)求參數(shù)構(gòu)造參數(shù)轨淌。
從上圖可知,主要包含五個(gè)參數(shù):
- type:"year_month"為默認(rèn)值
- year: 年份
- month: 月份
- index: 第幾頁(yè)
- value : 由年月拼接的字符串
繼續(xù)修改我們的parse
方法看尼,遍歷我們第一步抓取到的導(dǎo)航數(shù)據(jù)包構(gòu)造請(qǐng)求參數(shù):
def parse(self, response):
"""
處理獲取到的導(dǎo)航數(shù)據(jù)包
"""
json_body = json.loads(response.body) #加載json數(shù)據(jù)包
catalogs = json_body['book']['catalogs'] #獲取json中的目錄數(shù)據(jù)包
url = 'http://chushu.la/api/book/wx/chushula-{0}/pages?isAjax=1'.format(self.bookid) #分頁(yè)數(shù)據(jù)url
start_page = int(catalogs[0]['month']) #獲取起始月份作為index傳值
for catalog in catalogs:
year = catalog['year']
month = catalog['month']
formdata = {
"type": 'year_month',
"year": year,
"month": month,
"index": str(start_page),
"value": 'v_{0}{1}'.format(year, month)
}
start_page += 1
因?yàn)閺奈覀兏櫟降膆ttp請(qǐng)求來(lái)看是基于json傳參的post請(qǐng)求:
所以我們要這樣發(fā)起請(qǐng)求:
yield scrapy.Request(
url,
method='POST',
body=json.dumps(formdata),
headers={'Content-Type': 'application/json'},
callback=self.parse_moment)
同樣我們需要定義一個(gè)回調(diào)函數(shù)用來(lái)處理返回的朋友圈數(shù)據(jù)递鹉。定義parse_moment
方法,根據(jù)返回的json數(shù)據(jù)包進(jìn)行數(shù)據(jù)提炔卣丁:
def parse_moment(self, response):
"""
朋友圈數(shù)據(jù)處理
"""
json_body = json.loads(response.body)
pages = json_body['pages']
pattern = re.compile(u"[\u4e00-\u9fa5]+") #匹配中文
item = WeixinMomentItem()
for page in pages:
if (page['type'] == "weixin_moment_page"):# 僅抓取朋友圈分頁(yè)數(shù)據(jù)
paras = page['data']['paras']
if paras:
moment = ''
for content in paras[0]['rows']:
result = re.findall(pattern,
content['data']) #使用正則匹配所有中文朋友圈
moment += ''.join(result)
item['moment'] = moment
item['date'] = page['data']['dateText']#獲取時(shí)間
yield item
以上用到了定義的WeixinMomentItem
躏结。修改items.py,做如下修改:
class WeixinMomentItem(scrapy.Item):
"""
朋友圈Item
"""
# define the fields for your item here like:
# name = scrapy.Field()
date = scrapy.Field() #日期
moment = scrapy.Field() #朋友圈文字
至此我們完成爬蟲的書寫狰域。是不是迫不及待跑一下媳拴。
6.3. 蜘蛛爬起來(lái)
命令行執(zhí)行scrapy crawl moment -o moment.json
黄橘,稍等片刻,熱乎的朋友圈數(shù)據(jù)就生成到moment.json文件中了屈溉。
7. 分詞處理
jieba中文分詞提供了便利的接口用于分詞和詞頻統(tǒng)計(jì)塞关。我們直接調(diào)用jieba.cut
方法即可得到分詞結(jié)果。在此之前我們需要加載我們爬取的朋友圈數(shù)據(jù)子巾,即保存到moment.json文件中的數(shù)據(jù)帆赢,并拼接所有朋友圈文本傳參至jieba.cut
即可。
新添加一個(gè)analyse.py
文件线梗,定義analyse_words
方法:
# -*- coding: utf-8 -*-
"""分析導(dǎo)出的朋友圈數(shù)據(jù)"""
import json
import os
import jieba
from wordcloud import WordCloud
def analyse_words():
"""
分析抓取到的朋友圈數(shù)據(jù)椰于,使用jieba進(jìn)行分詞,使用wordcloud生成詞云
"""
curr_path = os.path.dirname(__file__) # 當(dāng)前文件文件夾所在目錄
parent_path = os.path.dirname(curr_path) # 上層目錄
file_path = os.path.join(parent_path, 'moment.json')
font_path = os.path.join(parent_path, "simhei.ttf")
if not os.path.isfile(file_path):
return
with open(file_path) as moment_file:
data = json.load(moment_file) # 使用json加載文件
moments = [item.get('moment', '') for item in data] # 獲取朋友圈文字?jǐn)?shù)組
contents = ' '.join(moments) # 拼接為長(zhǎng)文本
cut_texts = ' '.join(jieba.cut(contents)) # 使用結(jié)巴分詞進(jìn)行中文分詞
8. 生成關(guān)鍵詞詞云
詞云需要基于上一步的分詞結(jié)果生成詞云缠导。代碼也很簡(jiǎn)單:
cloud = WordCloud(font_path=font_path)
wordcloud = cloud.generate(cut_texts) #生成詞云
wordcloud.to_file('keys.png') #保存圖片
image = wordcloud.to_image() # 轉(zhuǎn)化為圖片
image.show() # 展示圖片
最后在文件末尾調(diào)用analyse_words()
廉羔,命令行執(zhí)行python analyse.py
即可生成關(guān)鍵詞溉痢!
你可能嫌棄以上生成的詞云比較丑僻造,沒(méi)關(guān)系,你可以使用wordart做出各種酷炫的效果孩饼。
9. 最后
因?yàn)椤境鰰病课赐晟品磁罊C(jī)制髓削,所以爬蟲寫下來(lái)也沒(méi)有什么難度,所以感興趣的不妨趕緊動(dòng)手試一試镀娶。本文出于學(xué)習(xí)分享立膛,無(wú)惡意竊取數(shù)據(jù)之意,也請(qǐng)讀者不要用于他途梯码!