最近業餘在作一個基於.NET Core的搜索項目,奈何基層代碼寫好了,沒有看起來很華麗的數據供測試。很巧的也是博客搜索,因而乎想到了博客園。C#也能作作頁面數據抓取的,不過在博客園看到的大部分都是python實現,因此就臨時想了一下看看python究竟是什麼東東,不看基礎語法,不看語言功能,直接上代碼,哪裏不會搜哪裏。代碼完成總共用時大概4個小時,其中搭建環境加安裝BeautifulSoup大概1個小時。解析HTML用時間最多了,邊看demo邊解析,大概2個小時,剩下的時間就是調試加保存數據了。html
既然用python,那麼天然少不了語言環境。因而乎到官網下載了3.5版本的。安裝完以後,隨機選擇了一個編輯器叫PyCharm,話說python編輯器還真挺多的。因爲本人是小白,因此安裝事項不在過多贅述。python
建好項目,打開編輯器,直接開工。原本以前用C#寫的時候,大致思路就是獲取網頁內容,而後正則匹配。後來發現網上的帖子也不少。不過在搜索過程當中發現,不建議用正則來匹配HTML。有正好個人正則不太好,因此我就搜了一下HTML解析工具,果不其然,人家都作好了,直接拿來用吧。沒錯就是這個東東:BeautifulSoup 。安裝也很簡單,不過中間出了個小插曲,就是bs4沒有。繼續搜,而後須要用pip安裝一下就行了。(固然我並不知道ps4和pip是什麼鬼)git
博客嗎,我固然就對準了博客園,因而乎,進入博客園首頁,查看請求。json
固然我不知道python是怎麼進行網絡請求的,其中還有什麼2.0和3.0的不一樣,中間曲曲折折了很多,最終仍是寫出了最簡單的一段請求代碼。網絡
import urllib.parse import urllib.request # params CategoryId=808 CategoryType=SiteHome ItemListActionName=PostList PageIndex=3 ParentCategoryId=0 TotalPostCount=4000 def getHtml(url,values): user_agent='Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36' headers = {'User-Agent':user_agent} data = urllib.parse.urlencode(values) response_result = urllib.request.urlopen(url+'?'+data).read() html = response_result.decode('utf-8') return html #獲取數據 def requestCnblogs(index): print('請求數據') url = 'http://www.cnblogs.com/mvc/AggSite/PostList.aspx' value= { 'CategoryId':808, 'CategoryType' : 'SiteHome', 'ItemListActionName' :'PostList', 'PageIndex' : index, 'ParentCategoryId' : 0, 'TotalPostCount' : 4000 } result = getHtml(url,value) return result
其實博客園這個請求仍是挺標準的,哈哈正好適合抓取。由於他返回的就是一段html。(若是返回json那不是更好。。。。)mvc
上文已經提到了,用到的是BeautifulSoup,好處就是不用本身寫正則,只要根據他的語法來寫就行了,在屢次的測試以後終於完成了數據的解析。先上一段HTML。而後在對應下面的代碼,也許看起來更輕鬆一些。app
<div class="post_item"> <div class="digg"> <div class="diggit" onclick="DiggPost('hyper-xl',6417741,281238,1)"> <span class="diggnum" id="digg_count_6417741">1</span> </div> <div class="clear"></div> <div id="digg_tip_6417741" class="digg_tip"></div> </div> <div class="post_item_body"> <h3><a class="titlelnk" href="http://www.cnblogs.com/hyper-xl/p/6417741.html" target="_blank">Python 字符串格式化</a></h3> <p class="post_item_summary"> <a href="http://www.cnblogs.com/hyper-xl/" target="_blank"> <img width="48" height="48" class="pfs" src="//pic.cnblogs.com/face/795666/20160421231717.png" alt="" /> </a> 轉載請註明出處 Python2.6+ 增長了str.format函數,用來代替原有的'%'操做符 。它使用比'%'更加直觀、靈活。下面詳細介紹一下它的使用方法。 下面是使用'%'的例子: 格式很像C語言的printf是否是?因爲'%'是一個操做符,只能在左右 兩邊各放一個參數,所以右邊多個值須要用元組或 ... </p> <div class="post_item_foot"> <a href="http://www.cnblogs.com/hyper-xl/" class="lightblue">新月的力量_141</a> 發佈於 2017-02-19 23:07 <span class="article_comment"> <a href="http://www.cnblogs.com/hyper-xl/p/6417741.html#commentform" title="" class="gray"> 評論(0) </a> </span> <span class="article_view"> <a href="http://www.cnblogs.com/hyper-xl/p/6417741.html" class="gray"> 閱讀 (138) </a> </span> </div> </div> <div class="clear"></div> </div>
經過上文的HTML代碼能夠看到幾點。首先每一條數據都在 div(class="post_item")下。而後 div("post_item_body")下有用戶信息,標題,連接,簡介等信息。逐一根據樣式解析便可。代碼以下:編輯器
from bs4 import BeautifulSoup import request import re #解析最外層 def blogParser(index): cnblogs = request.requestCnblogs(index) soup = BeautifulSoup(cnblogs, 'html.parser') all_div = soup.find_all('div', attrs={'class': 'post_item_body'}, limit=20) blogs = [] #循環div獲取詳細信息 for item in all_div: blog = analyzeBlog(item) blogs.append(blog) return blogs #解析每一條數據 def analyzeBlog(item): result = {} a_title = find_all(item,'a','titlelnk') if a_title is not None: # 博客標題 result["title"] = a_title[0].string # 博客連接 result["href"] = a_title[0]['href'] p_summary = find_all(item,'p','post_item_summary') if p_summary is not None: # 簡介 result["summary"] = p_summary[0].text footers = find_all(item,'div','post_item_foot') footer = footers[0] # 做者 result["author"] = footer.a.string # 做者url result["author_url"] = footer.a['href'] str = footer.text time = re.findall(r"發佈於 .+? .+? ", str) result["create_time"] = time[0].replace('發佈於 ','') comment_str = find_all(footer,'span','article_comment')[0].a.string result["comment_num"] = re.search(r'\d+', comment_str).group() view_str = find_all(footer,'span','article_view')[0].a.string result["view_num"] = re.search(r'\d+', view_str).group() return result def find_all(item,attr,c): return item.find_all(attr,attrs={'class':c},limit=1)
上邊一堆代碼下來,着實花費了我很多時間,邊寫邊調試,邊百度~~不過還好最終仍是出來了。等數據都整理好以後,而後我把它保存到了txt文件裏面,以供其餘語言來處理。原本想寫個put直接put到ElasticSearch中,奈何沒成功。後邊在試吧,畢竟個人重點只是導數據,不在抓取這裏。函數
import match import os import datetime import json def writeToTxt(list_name,file_path): try: #這裏直接write item 便可,不要本身給序列化在寫入,會致使json格式不正確的問題 fp = open(file_path,"w+",encoding='utf-8') l = len(list_name) i = 0 fp.write('[') for item in list_name: fp.write(item) if i<l-1: fp.write(',\n') i += 1 fp.write(']') fp.close() except IOError: print("fail to open file") #def getStr(item): # return json.dumps(item).replace('\'','\"')+',\n' def saveBlogs(): for i in range(1,2): print('request for '+str(i)+'...') blogs = match.blogParser(i,5) #保存到文件 path = createFile() writeToTxt(blogs,path+'/blog_'+ str(i) +'.json') print('第'+ str(i) +'頁已經完成') return 'success' def createFile(): date = datetime.datetime.now().strftime('%Y-%m-%d') path = '/'+date if os.path.exists(path): return path else: os.mkdir(path) return path result = saveBlogs() print(result)
上邊呢,我取了一百頁的數據,也就是大概2000條作測試。工具
廢了好大勁終於寫完那些代碼以後呢,就能夠享受勝利的果實了,雖然是初學者,代碼寫的很渣,這參考一下,那參考一下,不過仍是有些收穫的。運行效果以下:
生成的文件:
文件內容:
一個簡單的抓取程序就寫完了,python還真是TM的好用。之後有空再研究研究吧。代碼行數算上空行和註釋總共 100 (50+25+25) 行。湊個整數好看點~~如今認識字我感受就能夠上手寫程序了。這裏百度一下,那裏google一下,問題就解決了,程序也出來了,大功告成。
是時候該和python暫時告別了,繼續個人.NET事業。話說上次作rss採集的時候,好多「.NET要完蛋了」,「爲何咱們不招.NET」 是什麼鬼。 小夥伴們,下次見。