一、設計方案
1.爬蟲名稱:爬取百度熱榜
2.爬取內容:爬取網頁熱搜排名,標題,熱度值。
3.方案概述:訪問網頁得到狀態碼200,分析網頁源代碼,找出所需要的的標簽,逐個提取標簽保存到相同路徑csv文件中,讀取改文件,進行數據清洗,數據模型分析,數據可視化處理,繪制分布圖,用最小二乘法分析兩個變量間的二次擬合方程和繪制擬合曲線。
技術難點:做數據分析,即求回歸系數,因為標題是文字,無法與數字作比較,需要把標題這一列刪除才可。由於不明原因,輸出結果經常會顯示超出列表范圍。
二、主題頁面的結構特征分析
1.主題頁面的結構與特征分析:發現前三的排名標簽是‘span.num-top’ 第四名開始的標簽又是‘span.num-normal’,所以找統一的標簽,即' td.first '.標題標簽為' a.list-title '.熱度標簽 ' span.icon-rise '.
2.頁面解析:



三、
1.
#獲取html網頁 url = 'http://top.baidu.com/buzz.php?p=top10&tdsourcetag=s_pctim_aiomsg&qq-pf-to=pcqq.c2c?' header={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36', 'Referer': 'http://top.baidu.com/'} #請求超時時間為30秒 r = requests.get(url,timeout = 30,headers=header) #如果狀態碼不是200,則引發日常 r.raise_for_status() #配置編碼 r.encoding = r.apparent_encoding #獲取源代碼 r.text html=r.text soup=BeautifulSoup(html,'html.parser') title = soup.find_all('a', class_='list-title') point = soup.find_all('span', class_='icon-rise') print('{:^55}'.format('百度熱搜榜')) print('{:^5}\t{:^40}\t{:^10}'.format('排名', '標題', '熱度')) num = 20 lst = [] for i in range(num): print('{:^5}\t{:^40}\t{:^10}'.format(i+1, title[i].string, point[i].string)) lst.append([i+1,title[i].string,point[i].string]) df = pd.DataFrame(lst,columns=['排名','標題','熱度']) rank = r'rank.xlsx' df.to_excel(rank)


2.
#讀取csv文件 rank=pd.DataFrame(pd.read_excel('rank.xlsx')) print(rank.head())

3.
#刪除無效列 #rank.drop('標題',axis=1,inplace=True) #print(rank.head)

4.
#檢查是否有重復值 print(rank.duplicated())

5.
#檢查是否有空值 print(rank['熱度'].isnull().value_counts())

6.
#異常值處理 print(rank.describe()) #發現“熱度”字段的最大值為4457381而平均值為1301384,假設異常值為4457381 print(top.replace([4457381,top['熱度'].mean()]))

7.
#數據分析 from sklearn.linear_model import LinearRegression X = df.drop("標題",axis=1) predict_model = LinearRegression() predict_model.fit(X,df['熱度']) print("回歸系數為:",predict_model.coef_)

8.
#繪制排名與熱度的回歸圖 import seaborn as sns sns.regplot(rank_df.排名,rank_df.熱度)

9.
#畫出散點圖 plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標簽 plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負號 N=10 x=np.random.rand(N) y=np.random.rand(N) size=50 plt.xlabel("排名") plt.ylabel("熱度") plt.scatter(x,y,size,color='r',alpha=0.5,marker="o") #散點圖 kind='reg' sns.jointplot(x="排名",y="熱度",data=rank,kind='reg') # kind='hex' sns.jointplot(x="排名",y="熱度",data=rank,kind='hex') # kind='kde' sns.jointplot(x="排名",y="熱度",data=rank,kind="kde",space=0,color='g')



10.
#選擇排名和熱度兩個特征變量,繪制分布圖,用最小二乘法分析兩個變量間的二次擬合方程和擬合曲線 colnames=[" ","排名","標題","熱度"] df = pd.read_excel('rank.xlsx',skiprows=1,names=colnames) X = df.排名 Y = df.熱度 Z = df.標題 def A(): plt.scatter(X,Y,color="blue",linewidth=2) plt.title("RM scatter",color="blue") plt.grid() plt.show() def B(): plt.scatter(X,Y,color="green",linewidth=2) plt.title("redu",color="blue") plt.grid() plt.show() def func(p,x): a,b,c=p return a*x*x+b*x+c def error(p,x,y): return func(p,x)-y def main(): plt.figure(figsize=(10,6)) p0=[0,0,0] Para = leastsq(error,p0,args=(X,Y)) a,b,c=Para[0] print("a=",a,"b=",b,"c=",c) plt.scatter(X,Y,color="blue",linewidth=2) x=np.linspace(0,20,20) y=a*x*x+b*x+c plt.plot(x,y,color="blue",linewidth=2,) plt.title("熱度值分布") plt.grid() plt.show() print(A()) print(B()) print(main())

四、完整代碼
from bs4 import BeautifulSoup import requests import pandas as pd import time import random from matplotlib import pyplot as plt import numpy as np from numpy import genfromtxt import matplotlib.pyplot as plt import matplotlib from scipy.optimize import leastsq #睡眠 模擬用戶 time.sleep(1+random.random()) #獲取html網頁 url = 'http://top.baidu.com/buzz.php?p=top10&tdsourcetag=s_pctim_aiomsg&qq-pf-to=pcqq.c2c?' header={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36', 'Referer': 'http://top.baidu.com/'} #請求超時時間為30秒 r = requests.get(url,timeout = 30,headers=header) #如果狀態碼不是200,則引發日常 r.raise_for_status() #配置編碼 r.encoding = r.apparent_encoding #獲取源代碼 r.text html=r.text soup=BeautifulSoup(html,'html.parser') title = soup.find_all('a', class_='list-title') point = soup.find_all('span', class_='icon-rise') print('{:^55}'.format('百度熱搜榜')) print('{:^5}\t{:^40}\t{:^5}'.format('排名', '標題', '熱度')) num = 20 lst = [] for i in range(num): print('{:^5}\t{:^40}\t{:^5}'.format(i+1, title[i].string, point[i].string)) lst.append([i+1,title[i].string,point[i].string]) df = pd.DataFrame(lst,columns=['排名','標題','熱度']) rank = r'rank.xlsx' df.to_excel(rank) #讀取csv文件 rank=pd.DataFrame(pd.read_excel('rank.xlsx')) print(rank.head()) #刪除無效列 #rank.drop('標題',axis=1,inplace=True) #print(rank.head) #檢查是否有重復值 print(rank.duplicated()) #檢查是否有空值 print(rank['熱度'].isnull().value_counts()) #異常值處理 print(rank.describe()) #發現“熱度”字段的最大值為4457381而平均值為1301384,假設異常值為4457381 #print(top.replace([4457381,top['熱度'].mean()])) #數據分析 from sklearn.linear_model import LinearRegression X = df.drop("標題",axis=1) predict_model = LinearRegression() predict_model.fit(X,df['熱度']) print("回歸系數為:",predict_model.coef_) #繪制排名與熱度的回歸圖 import seaborn as sns #sns.regplot(rank_df.排名,rank_df.熱度) #畫出散點圖 # 用來正常顯示中文標簽 plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示負號 plt.rcParams['axes.unicode_minus'] = False N=10 x=np.random.rand(N) y=np.random.rand(N) size=50 plt.xlabel("排名") plt.ylabel("熱度") plt.scatter(x,y,size,color='r',alpha=0.5,marker="o") #散點圖 kind='reg' sns.jointplot(x="排名",y="熱度",data=rank,kind='reg') # kind='hex' sns.jointplot(x="排名",y="熱度",data=rank,kind='hex') # kind='kde' sns.jointplot(x="排名",y="熱度",data=rank,kind="kde",space=0,color='g') #選擇排名和熱度兩個特征變量,繪制分布圖,用最小二乘法分析兩個變量間的二次擬合方程和擬合曲線 colnames=[" ","排名","標題","熱度"] df = pd.read_excel('rank.xlsx',skiprows=1,names=colnames) X = df.排名 Y = df.熱度 Z = df.標題 def A(): plt.scatter(X,Y,color="blue",linewidth=2) plt.title("RM scatter",color="blue") plt.grid() plt.show() def B(): plt.scatter(X,Y,color="green",linewidth=2) plt.title("redu",color="blue") plt.grid() plt.show() def func(p,x): a,b,c=p return a*x*x+b*x+c def error(p,x,y): return func(p,x)-y def main(): plt.figure(figsize=(10,6)) p0=[0,0,0] Para = leastsq(error,p0,args=(X,Y)) a,b,c=Para[0] print("a=",a,"b=",b,"c=",c) plt.scatter(X,Y,color="blue",linewidth=2) x=np.linspace(0,20,20) y=a*x*x+b*x+c plt.plot(x,y,color="blue",linewidth=2,) plt.title("熱度值分布") plt.grid() plt.show() print(A()) print(B()) print(main())
五、總結
1.經過對數據的分析和可視化,從回歸方程和擬合曲線可以看出散點大部分都落在曲線上,說明熱度是隨着排名的遞增而遞增的。又從散點圖可以看出熱度大部分停留在1-2W之間。
2.小結:在這次對百度熱榜的分析的過程中,我從中學會了不少函數及用法。很多次都卡在一個點上,絞盡腦汁去想解決問題的辦法,通過觀看b站的視頻,百度搜索等方法去找尋答案。這兩個星期來也養成了耐心和獨立思考的習慣,並且提高了我對Python的興趣。
