爬取多頁資訊——爬取自己CSDN部落格
阿新 • • 發佈:2018-12-31
在學完莫煩B站的爬蟲視訊的3.3節之後,我完成了這個專案,感覺收穫還是不小的。
體會是自己練手感覺進步挺快,能做出一個小專案給了我一些自信心,中間幾個棘手的問題:
1如何獲取下一頁:分析我的主頁的HTML,發現有一個總的文章數、有pagesize(每頁最多顯示幾篇文章),有此兩值就可以得到總的頁數。再發現每一頁的url是這樣的https://blog.csdn.net/liuchengzimozigreat/article/list/2,每頁連結基本一致,最後一個數字代表的是第幾頁,於是就可以用字串拼接來得到所有分頁的url;
2如何獲取各個文章的資訊:這主要去分析具體的HTML,看你需要的資訊具體在哪兒,然後用解析器幫助你得到想要的HTML模組,然後具體再去每個模組中提取資訊;
3我要存什麼資訊、怎麼存:好吧,這個問題其實是我最後碰到的,前面程式做的差不多了,最後才想著我要怎麼存、存什麼,當然之前你可能想過一些,但如果想的不是很清楚的話,這個時候你就要停下來好好想想了。這個問題關係有點大,如果一開始就瞭然於心,就可以讓我們思路更加順暢;
4正則表示式的問題:正則表示式還是推薦這篇文章,我看完莫煩的正則表示式之後,其實立馬就忘了好多,還是碰到具體問題之後,去看那篇文章中總結的表,於是從一開始啥也不會,到後面正則表示式越來越得心應手。
下面是我的程式碼,感覺寫得很亂,以後還是多寫一些函式才好,都寫在主函式裡,讓人不想看了簡直:
# Download amazing pictures from national geographic from bs4 import BeautifulSoup import requests import re import datetime import pandas as pd import os # 提取第一頁至最後一頁的url def get_allpages_url(page_info): '''<script> var currentPage = 1; var baseUrl = 'https://blog.csdn.net/liuchengzimozigreat/article/list' ; var pageSize = 20 ; var listTotal = 26 ; var pageQueryStr = ''; function getAllUrl(page) { return baseUrl + "/" + page + pageQueryStr; } </script>''' # 以上是script_lst[-9]的資訊,有三個數:currentPage, pageSize, listTotal '\d'只查詢數字一次,'\d+'則往後查詢數字無限次 currentPage, pageSize, listTotal = [int(num) for num in re.findall(r'\d+', page_info)] pageNum = listTotal//pageSize + 1 # 總的網頁數 print('總的網頁數:', pageNum, '總的文章數:', listTotal, 'pageSize:', pageSize) baseUrl = re.findall(r'\'(.*)\'', page_info)[0] # .*在正則表示式中表示匹配除了'\n'之外的任何字元0次或無限次,在DOTALL中也可以匹配'\n',執行後發現這裡就可以 allpages_url_lst = [baseUrl + '/' + str(page_num) for page_num in range(1, pageNum+1)] # 這裡左閉右開,所以總的網頁數要+1 return allpages_url_lst, listTotal # 獲取總體資訊 def get_general_info(soup): general_info = soup.find('div', {'class': 'data-info d-flex item-tiling'}) dl_info = general_info.find_all('dl') # print(info) self_article_num = dl_info[0]['title'] # 原創文章數量 fans_num = dl_info[1]['title'] # 粉絲數 like_num = dl_info[2]['title'] # 喜歡數 comment_num = dl_info[3]['title'] # 評論數 # print(self_article_num, fans_num, like_num, comment_num) return self_article_num, fans_num, like_num, comment_num # 獲取等級資訊 def get_grade_info(soup): grade_info = soup.find('div', {'class': 'grade-box clearfix'}) # print(grade_info) dd_info = grade_info.find_all('dd') grade = dd_info[0].a['title'].split(r',')[0] # 等級 total_read_num = dd_info[1]['title'] # 總閱讀量 earn_points = dd_info[2]['title'] # 積分 rank = grade_info.find_all('dl')[-1]['title'] # 排名 return grade, total_read_num, earn_points, rank if __name__ == '__main__': now_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") record_time = str(now_time) # 將作為資料存檔時間來儲存 date_today = datetime.datetime.now().date() # 今天的時間,將作為文章索引 # print(date_today, record_time) URL = "https://blog.csdn.net/liuchengzimozigreat" # 我的blog主頁(首頁/第一頁) # find list of image holder html = requests.get(URL).text soup = BeautifulSoup(html, 'lxml') # 提取總體資訊 self_article_num, fans_num, like_num, comment_num = get_general_info(soup) # 提取等級資訊 grade, total_read_num, earn_points, rank = get_grade_info(soup) # 儲存總體資訊 file_path = r'H:\learning like never feel tired\Scraping python\my_blog_info' # 儲存位置 general_info_file = file_path + '\\AAA_general_info.csv' # 前面加三個A好讓總體資訊出現在資料夾第一的位置 if os.path.exists(general_info_file): df = pd.read_csv(general_info_file, index_col=0, engine='python', encoding='utf_8_sig', parse_dates=[0]) else: df = pd.DataFrame(columns=('self_article_num', 'fans_num', 'like_num', 'comment_num', 'grade', 'total_read_num', 'earn_points', 'rank', 'record_time')) df.loc[date_today] = [self_article_num, fans_num, like_num, comment_num, grade, total_read_num, earn_points, rank, record_time] # 用當天日期作為索引,更新資訊 df.to_csv(general_info_file, encoding='utf_8_sig') # 提取各個文章的資訊 script_lst = soup.find_all('script') # 提取其中的script標籤tag,其中script_lst[-9]含有listtotal和pagesize資訊 allpages_url_lst, total_article_num = get_allpages_url(str(script_lst[-9])) # 提取各page的url page_article_lst = [] # 儲存所有網頁中article集合的資訊 for page_url in allpages_url_lst: html = requests.get(page_url).text soup = BeautifulSoup(html, 'lxml') article_info = soup.find_all('div', {'class': 'article-item-box csdn-tracking-statistics'}) page_article_lst.append(article_info) # print(type(article_info)) article_info_lst = [] # 將所有文章整合進同一個列表中 for elem in page_article_lst: mark = 0 for article in elem: if mark == 0: # 檢視HTML發現每一頁中都會在最開頭的地方多一篇未顯示的文章'帝都的凜冬' mark = 1 continue article_info_lst.append(article) # 提取文章屬性並儲存之,將儲存八個資訊:文章資訊記錄的日期——作為索引、文章名、文章型別、建立時間、閱讀量、評論量、文章url、該條記錄時間(精確到秒) for article in article_info_lst: # print(article) span = article.find_all('span') # span包含了四個屬性,依次是:article_type, date, read_num, comment_num article_type = re.split(r'\s*', str(span[0]))[3] # 文章型別 create_date = span[1].get_text() # 建立時間 read_num = span[2].get_text() # 閱讀數 comment_num = span[3].get_text() # 評論數 article_url = article.a['href'] # 文章連結 # 用'\n'分割結果類似:['', ' 原 ', ' linux命令學習彙總 '],需將最後一個字串左右兩邊空格去掉才是我們想要的結果 # 用' {2,}'——空格2到無限次分割結果類似:['', 'rails官方指南--建一個簡易部落格', ''],因此去列表中的第二個作為我們的article_name article_name = re.split(r' {2,}', re.split(r'\n*', str(article.find('a').get_text()))[-1])[1] # 文章名稱 當時我還不知道,其實換成re.sub()更好 file_name = re.sub(r'[\s\':,\.()]*', '', article_name) file = file_path + '\\' + re.findall(r'\w{2,20}', file_name)[0] + '.csv' # 檔名不宜太長,所以最多取20個\w——單詞字元[A-Za-z0-9],過短又會衝突 if os.path.exists(file): print('old article:', file) df = pd.read_csv(file, parse_dates=[0], index_col=0, engine='python', encoding='utf_8_sig') # 有時難免程式在一天內多次執行,讀入資料是保證一天只記錄一個數據 else: print('NEW ARTICLE:', article_name) df = pd.DataFrame(columns=('article_name', 'article_type', 'create_date', 'read_num', 'comment_num', 'article_url', 'record_time')) # print(date_today) df.loc[date_today] = [article_name, article_type, create_date, read_num, comment_num, article_url, record_time] # 用當天日期作為索引,更新資訊 df.to_csv(file, encoding='utf_8_sig') # 下面是一些HTML目標模組的資訊,也就這些資訊,才能提煉出你想要的結果 ''' # 每個article的原始資訊 <div class="article-item-box csdn-tracking-statistics" data-articleid="78773093"> <h4 class=""> <a href="https://blog.csdn.net/liuchengzimozigreat/article/details/78773093" target="_blank"> <span class="article-type type-2"> 轉 </span> postgresql資料庫常用操作命令及SQL語言 </a> </h4> <p class="content"> <a href="https://blog.csdn.net/liuchengzimozigreat/article/details/78773093" target="_blank"> 環境ubuntu,安裝了postgresql 截圖命令:shift+PrtSc可以有十字游標,任選截圖區域 alt+PrtSc擷取當前活動視窗 PrtSc擷取整個螢幕 1postgresql常用操作: (1)登入
[email protected] </a> </p> <div class="info-box d-flex align-content-center"> <p> <span class="date">2017-12-11 16:10:49</span> </p> <p> <span class="read-num">閱讀數:914</span> </p> <p> <span class="read-num">評論數:0</span> </p> </div> </div> ''' ''' # general_info的資訊,包含原創、粉絲、喜歡、評論 <div class="data-info d-flex item-tiling"> <dl class="text-center" title="22"> <dt><a href="https://blog.csdn.net/liuchengzimozigreat?t=1">原創</a></dt> <dd><a href="https://blog.csdn.net/liuchengzimozigreat?t=1"><span class="count">22</span></a></dd> </dl> <dl class="text-center" id="fanBox" title="2"> <dt>粉絲</dt> <dd><span class="count" id="fan">2</span></dd> </dl> <dl class="text-center" title="5"> <dt>喜歡</dt> <dd><span class="count">5</span></dd> </dl> <dl class="text-center" title="4"> <dt>評論</dt> <dd><span class="count">4</span></dd> </dl> </div> ''' ''' <div class="grade-box clearfix"> <dl> <dt>等級:</dt> <dd> <a href="https://blog.csdn.net/home/help.html#level" target="_blank" title="2級,點選檢視等級說明"> <svg aria-hidden="true" class="icon icon-level"> <use xlink:href="#csdnc-bloglevel-2"></use> </svg> </a> </dd> </dl> <dl> <dt>訪問:</dt> <dd title="7462"> 7462 </dd> </dl> <dl> <dt>積分:</dt> <dd title="304"> 304 </dd> </dl> <dl title="289473"> <dt>排名:</dt> <dd>28萬+</dd> </dl> </div> '''