對sfgg全部博客的數據統計

sfgg最近才把博客系統開放給大衆申請使用,可是不得不說因爲寫做體驗良好,人氣在一步一步的增長。突發奇想,想統計一下sfgg博客的一些信息,搞起我最熟悉的python語言,開始寫爬蟲,而後整理數據,作一個簡單的數據統計,歷時一個晚上加一個上午,終於成功完成。下面是個人過程記錄和代碼分享,順帶問高手們幾個寫的過程當中遇到的問題。php

url列表的獲取

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')

出現了幾個蛋疼的問題:數據庫

  1. 有幾篇博文竟然沒有做者信息,致使author=soup('div',{'class':'author-status'})[0]會出現越界訪問。
  2. 數據存入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

簡單的統計分析

  1. 誰是最高產的博主?服務器

    topAuthor

  2. 誰的得票數最多?app

    topVote

  3. 誰是最有珍藏價值的博主,趕快抱回家?

    topMark

  4. 誰是單篇文章認同最高的博主?

    topValue

  5. 誰的博客一呼百應?

    topComment

  6. 出鏡率最高的文章?

    topViewSingle

    這裏的title太長了,我仍是不寫在圖上面了。

    0.每一個人都得經歷挫折和不斷成長的過程:從退學到創業的這幾年!
    1.SegmentFault.php
    2.皮包公司的祕密 - 人口販子公司
    3.技術人攻略訪談十九:iOS大V養成記
    4.Gulp.js:比 Grunt 更簡單的自動化的項目構建利器
    5.Vim 的哲學(一)
    6.技術人攻略訪談二十五:運維人的野蠻生長
    7.告別碼農,成爲真正的程序員
    8.從13到14(一個沒有文采的女同窗)
    9.小團隊,大夢想:尋找小夥伴,成爲SegmentFault 的小夥伴

  7. 出鏡率最高的博主?

    topViewSum

  8. 標題必定要長?
    瀏覽量對標題長度作迴歸,獲得的結果是:

    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的文章。

相關文章
相關標籤/搜索