sfgg最近才把博客系統開放給大衆申請使用,可是不得不說因爲寫做體驗良好,人氣在一步一步的增長。突發奇想,想統計一下sfgg博客的一些信息,搞起我最熟悉的python語言,開始寫爬蟲,而後整理數據,作一個簡單的數據統計,歷時一個晚上加一個上午,終於成功完成。下面是個人過程記錄和代碼分享,順帶問高手們幾個寫的過程當中遇到的問題。php
http://segmentfault.com/blogs/newest?page=1
這是sfgg博客分頁列表的格式,我只要將page後面的參數遍歷一遍,那麼全部的博客連接就能夠成功的捕捉下來了,因而配置好python的beautifulsoup包之後,幾句代碼就完成了工做。python
getUrl.pymysql
import bs4 import urllib.request as req urlList=open(r'E:\source\python\urlList.txt','w') for i in range(47): content=req.urlopen("http://segmentfault.com/blogs/newest?page="+str(1+i)) soups=bs4.BeautifulSoup(content.read()) for soup in soups('h2',{ "class" : "title" }): urlList.write(soup.a['href']+'\n') print('write',soup.a['href'],'done!') urlList.close() print("All url done!")
這個過程也沒有什麼效率上面的考慮,由於速度已經夠用的。程序員
初版的規劃仍是用beautifulsoup來獲取信息,而後將數據放在pandas包的DataFrame中,而後一次性存入數據庫。
getMeta.pysql
import pandas as pd import mysql.connector as conn import urllib.request as req import bs4 data=pd.DataFrame() urlList=open(r'E:\source\python\urlList.txt','r') for url in urlList: content=req.urlopen(url) soup=bs4.BeautifulSoup(content.read()) author=soup('div',{'class':'author-status'})[0].h4.a.text title=soup('h1',{'class':'post-title'})[0].text vote=int(soup('span',{'id':'article-rank'})[0].text) bookmark=int(soup('a',{'class':'btn btn-rank bookmark'})[0].span.text) if soup('a',{'href':'#comments'})[0].text == "沒有評論": comment=0 else: comment=int(soup('a',{'href':'#comments'})[0].text[:-2]) pageview=int(soup('span',{'class':'views'})[0].text[:-2]) data=data.append({'url':url,'author':author,'vote':vote,'bookmark':bookmark,'comment':comment,'pageview':pageview},ignore_index=True) print("Finish",url,"operation!") data.index=range(data['url'].count()) cnx = conn.connect(user='root', password='xxxxxxxx', host='127.0.0.1', database='sfgg') data.to_sql("meta",cnx,flavor='mysql',if_exits='fail')
出現了幾個蛋疼的問題:數據庫
author=soup('div',{'class':'author-status'})[0]
會出現越界訪問。mysql
時,中文字符會報1366 (HY000): Incorrect string value:
的錯誤,谷歌了好一下子,揭示大概就是mysql不支持三個bytes以上的utf-8格式,好吧,反正我是不知道如何解決。因而我就開始尋思着寫第二個版本,加入線程池提升速度,數據庫也採用sqlite
。參考了sfgg中的一行python實現並行化的文章。因而就有了第二版:
getMetav2.pysegmentfault
import bs4 import base64 import pickle import sqlite3 import pandas as pd import urllib.request as req from multiprocessing.dummy import Pool as ThreadPool data=pd.DataFrame() urlList=open(r'E:\source\python\urlList.txt','r') def getData(url): try: content=req.urlopen(url) soup=bs4.BeautifulSoup(content.read()) title=soup('h1',{'class':'post-title'})[0].text author=soup('div',{'class':'author-status'})[0].h4.a.text vote=int(soup('span',{'id':'article-rank'})[0].text) bookmark=int(soup('a',{'class':'btn btn-rank bookmark'})[0].span.text) if soup('a',{'href':'#comments'})[0].text == "沒有評論": comment=0 else: comment=int(soup('a',{'href':'#comments'})[0].text[:-2]) pageview=int(soup('span',{'class':'views'})[0].text[:-2]) print(url) return {'url':url,'author':author,'vote':vote,'bookmark':bookmark,'title':title,'comment':comment,'pageview':pageview} except: errorList=open(r'E:\source\python\errorList.txt','w+') errorList.write(url) errorList.close() pool=ThreadPool(50) result=pool.map(getData, urlList) pool.close() pool.join() print('finish meta data') with open(r'E:\source\python\result','wb') as f: pickle.dump(result,f) for ob in result: data=data.append(ob,ignore_index=True) data.index=range(data['url'].count()) cnx = sqlite3.connect(r'E:\SQLite\meta.db') data.to_sql("meta",cnx) print('All finished!')
這個版本,首先考慮了對出錯連接的存儲,而後在連接遍歷完後持久化保存相關變量,使用sqlite保存數據。api
誰是最高產的博主?服務器
誰的得票數最多?app
誰是最有珍藏價值的博主,趕快抱回家?
誰是單篇文章認同最高的博主?
誰的博客一呼百應?
出鏡率最高的文章?
這裏的title太長了,我仍是不寫在圖上面了。
0.每一個人都得經歷挫折和不斷成長的過程:從退學到創業的這幾年!
1.SegmentFault.php
2.皮包公司的祕密 - 人口販子公司
3.技術人攻略訪談十九:iOS大V養成記
4.Gulp.js:比 Grunt 更簡單的自動化的項目構建利器
5.Vim 的哲學(一)
6.技術人攻略訪談二十五:運維人的野蠻生長
7.告別碼農,成爲真正的程序員
8.從13到14(一個沒有文采的女同窗)
9.小團隊,大夢想:尋找小夥伴,成爲SegmentFault 的小夥伴
出鏡率最高的博主?
標題必定要長?
瀏覽量對標題長度作迴歸,獲得的結果是:
pageview = 0.6495*titleLength + 231.0448
貌似看上去二者相關係數是正的,可是係數的t統計量的置信度才50%,基本上來講,是不可信的。 標題必定要長是扯淡的。
代碼:
analysis.py
import sqlite3 from pylab import * import pandas as pd import statsmodels.formula.api as sm conn=sqlite3.connect(r'E:\SQLite\meta.db') data=pd.read_sql(r'select * from meta',conn) #誰是最高產的博主 topAuthor=data['author'].value_counts()[:10] show(topAuthor.plot(kind='bar',rot=270)) #誰的得票數最多 topVote=data['vote'].groupby(data['author']).sum() topVote.sort(ascending=False) topVote=topVote[:10] show(topVote.plot(kind='bar',rot=270)) #誰是最有珍藏價值的博主,趕快抱回家 topMark=data['bookmark'].groupby(data['author']).sum() topMark.sort(ascending=False) topMark=topMark[:10] show(topMark.plot(kind='bar',rot=270)) #誰是單篇文章最高的博主 topValue=(data['vote']+data['bookmark']).groupby(data['author']).sum() topValue.sort(ascending=False) topValue=topValue[:10] show(topValue.plot(kind='bar',rot=270)) #誰的博客一呼百應 topComment=data['comment'].groupby(data['author']).sum() topComment.sort(ascending=False) topComment=topComment[:10] show(topComment.plot(kind='bar',rot=270)) #出鏡率最高的文章 topViewSingle=data['pageview'].groupby(data['title']).sum() topViewSingle.sort(ascending=False) topViewSingle=topViewSingle[:10] show(topViewSingle.plot(kind='bar',rot=270)) #出鏡率最高的博主 topViewSum=data['pageview'].groupby(data['author']).sum() topViewSum.sort(ascending=False) topViewSum=topViewSum[:10] show(topViewSum.plot(kind='bar',rot=270)) #標題長度和瀏覽量的關係 titleLength=[len(x) for x in data['title']] lengthData=pd.DataFrame({'titleLength':titleLength,'pageview':data['pageview']}) res = sm.ols('pageview~titleLength',data = lengthData).fit() print(res.summary())
1.getMetav2.py
中,我試過使用進程池,也就是from multiprocessing import Pool as ThreadPool
,這個除了會把我cpu跑滿之外,沒有任何效果,這是爲嘛?這裏是什麼緣由致使線程池和進程池效果不一?
2. 但願你們指出我這個代碼中很差的習慣,有些東西本身很難意識到,感謝各位
感謝sfgg的管理員在我爬的時候沒有封我IP,服務器質量很高嘛,不錯;感謝各位原創博主的不懈努力,sfgg纔有這麼多優秀的文章;感謝python,讓我一天不到完成了這個想法。最後感謝一下沙渺同窗,很大度的讓我爬sfgg的文章。