開發記錄_自學Python寫爬蟲程序爬取csdn我的博客信息

天天刷開csdn的博客,看到一整個頁面,其實對我而言,我只想看看訪問量有沒有上漲而已...php

因而萌生了一個想法:html

想寫一個爬蟲程序把csdn博客上邊的訪問量和評論數都爬下來。python

打算經過網絡各類蒐集資料,自學寫Python代碼。程序員

此次自學的歷程,也打算及時的整理下來,發佈在博客裏。web

 

/******************這是程序員風格的分割線******************/正則表達式

 

2013.11.3_開工

聽說Python並不難,看過了python的代碼以後也以爲確實,django

代碼很清爽,相比起C/C++,JS,PHP來說,python的代碼看起來就是舒服。ubuntu

看了幾段別人的爬蟲程序教程,還不太清楚Python的編譯器是怎麼處理代碼的。瀏覽器

每一句代碼的分割彷佛是經過換行,服務器

像是if/else這樣的多個語句在一塊兒的,也沒有看到大括號,而是像這樣:

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">import urllib, urllister  
  2.   
  3. def getURL(url):  
  4.     try:  
  5.         usock = urllib.urlopen(url)  
  6.     except:  
  7.         print 'get url excepton'  
  8.         return []  
  9.     parser = urllister.URLLister()  
  10.     parser.feed(usock.read())  
  11.     usock.close()  
  12.     parser.close()  
  13.     urls = parser.urls  
  14.     return urls</span>  


對於函數的定義,只要def就行了。

 

變量的申明只須要在用的時候起個名字就行了,不用規定int, char, float....

我是一個從C語言轉學來的鄉下孩子..這麼高端洋氣上檔次的定義方式真是驚了個呆。

因而瞬間對Python好感倍增。

 

看了不少人的爬蟲教程,以及代碼,仍是沒什麼思路,今天沒空了,等明天上手試試看。

 

2013.11.4_第一步大功告成

不錯不錯,今天試了試網上的教程,成功的把個人博客主頁抓下來了。

中間還趕上些坎坷,不過先感謝一下

 

月語凋凌 在他的百度空間 Endless Road 裏轉載的博客

用python爬蟲抓站的一些技巧總結

月語凋凌所說轉自  http://gae-django-cms.appspot.com  不過我這裏彷佛打不開這個網頁..

這篇文章真心的幫到了大忙,雖然小小吐槽一下排版很差..

在這裏系統的學到了Python爬蟲的入門部分。

這位博主請不要介意,我在這裏幫你整理一下縮進,再整理一下代碼方便你們吧~

<--注意:一直到下一個此相似標記前的內容,均轉自月語凋凌 的博客用python爬蟲抓站的一些技巧總結,我只是作了整理-->

 

 

原文轉自http://gae-django-cms.appspot.com/

這些腳本有一個共性,都是和web相關的,總要用到獲取連接的一些方法,再加上simplecd這 個半爬蟲半網站的項目,

累積很多爬蟲抓站的經驗,在此總結一下,那麼之後作東西也就不用重複勞動了。

 

1.最基本的抓站

 

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">import urllib2  
  2. content = urllib2.urlopen('http://XXXX').read()</span>  

 

 

2.使用代理服務器

這在某些狀況下比較有用,好比IP被封了,或者好比IP訪問的次數受到限制等等。

 

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">import urllib2  
  2. proxy_support = urllib2.ProxyHandler({'http':'http://XX.XX.XX.XX:XXXX'})  
  3. opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)  
  4. urllib2.install_opener(opener)  
  5. content = urllib2.urlopen('http://XXXX').read()</span>  


 

 

3.須要登陸的狀況

登陸的狀況比較麻煩我把問題拆分一下:

 

 

 

3.1 cookie的處理

 

 

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">import urllib2, cookielib  
  2. cookie_support= urllib2.HTTPCookieProcessor(cookielib.CookieJar())  
  3. opener = urllib2.build_opener(cookie_support, urllib2.HTTPHandler)  
  4. urllib2.install_opener(opener)  
  5. content = urllib2.urlopen('http://XXXX').read()</span>  


 

 

是的沒錯,若是想同時用代理和cookie,那就加入proxy_support而後operner改成

 

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">opener = urllib2.build_opener(proxy_support, cookie_support, urllib2.HTTPHandler)</span>  

 

 

 

3.2 表單的處理

 

 

登陸必要填表,表單怎麼填?首先利用工具截取所要填表的內容

 

好比我通常用firefox+httpfox插件來看看本身到底發送了些什麼包

 

這個我就舉個例子好了,以verycd爲例,先找到本身發的POST請求,以及POST表單項:

 

能夠看到verycd的話須要填username,password,continueURI,fk,login_submit這幾項,其中fk是隨機生 成的(其實不太隨機,看上去像是把epoch時間通過簡單的編碼生成的),須要從網頁獲取,也就是說得先訪問一次網頁,用正則表達式等工具截取返回數據中 的fk項。continueURI顧名思義能夠隨便寫,login_submit是固定的,這從源碼能夠看出。還有username,password那 就很顯然了。

 

-

 

好的,有了要填寫的數據,咱們就要生成postdata

 

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">import urllib  
  2. postdata=urllib.urlencode({  
  3.     'username':'XXXXX',  
  4.     'password':'XXXXX',  
  5.     'continueURI':'http://www.verycd.com/',  
  6.     'fk':fk,  
  7.     'login_submit':'登陸'  
  8.     })</span>  

 

 

而後生成http請求,再發送請求:

 

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">req = urllib2.Request(  
  2.     url = 'http://secure.verycd.com/signin/*/http://www.verycd.com/',  
  3.     data = postdata)  
  4. result = urllib2.urlopen(req).read()</span>  

 

 

 

3.3 假裝成瀏覽器訪問

 

 

某些網站反感爬蟲的到訪,因而對爬蟲一概拒絕請求

 

這時候咱們須要假裝成瀏覽器,這能夠經過修改http包中的header來實現

 

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">headers = {  
  2.     'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'  
  3. }  
  4. req = urllib2.Request(  
  5.     url = 'http://secure.verycd.com/signin/*/http://www.verycd.com/',  
  6.     data = postdata,  
  7.     headers = headers  
  8. )</span>  

 

 

 

3.4 反」反盜鏈」

 

 

某些站點有所謂的反盜鏈設置,其實說穿了很簡單,就是檢查你發送請求的header裏面,referer站點是否是他本身,因此咱們只須要像3.3同樣, 把headers的referer改爲該網站便可,以黑幕著稱地cnbeta爲例:

 

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">#...  
  2. headers = {  
  3.     'Referer':'http://www.cnbeta.com/articles'  
  4. }  
  5. #...</span>  

 

 

headers是一個dict數據結構,你能夠放入任何想要的header,來作一些假裝。例如,有些自做聰明的網站總喜歡窺人隱私,別人經過代理 訪問,他恰恰要讀取header中的X-Forwarded-For來看看人家的真實IP,沒話說,那就直接把X-Forwarde-For改了吧,能夠 改爲隨便什麼好玩的東東來欺負欺負他,呵呵。

 

-

 

 

3.5 終極絕招

 

 

有時候即便作了3.1-3.4,訪問仍是會被據,那麼沒辦法,老老實實把httpfox中看到的headers全都寫上,那通常也就好了。

 

再不行,那就只能用終極絕招了,selenium直 接控制瀏覽器來進行訪問,只要瀏覽器能夠作到的,那麼它也能夠作到。相似的還有pamie,watir,等等等等。

 

-

 

 

4.多線程併發抓取

單線程太慢的話,就須要多線程了,這裏給個簡單的線程池模板
這個程序只是簡單地打印了1-10,可是能夠看出是併發地。

 

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">from threading import Thread  
  2. from Queue import Queue  
  3. from time import sleep  
  4. #q是任務隊列  
  5. #NUM是併發線程總數  
  6. #JOBS是有多少任務  
  7. q = Queue()  
  8. NUM = 2  
  9. JOBS = 10  
  10. #具體的處理函數,負責處理單個任務  
  11. def do_somthing_using(arguments):  
  12.     print arguments  
  13. #這個是工做進程,負責不斷從隊列取數據並處理  
  14. def working():  
  15.     while True:  
  16.         arguments = q.get()  
  17.         do_somthing_using(arguments)  
  18.         sleep(1)  
  19.         q.task_done()  
  20. #fork NUM個線程等待隊列  
  21. for i in range(NUM):  
  22.     t = Thread(target=working)  
  23.     t.setDaemon(True)  
  24.     t.start()  
  25. #把JOBS排入隊列  
  26. for i in range(JOBS):  
  27.     q.put(i)  
  28. #等待全部JOBS完成  
  29. q.join()</span>  



 

 

5.驗證碼的處理

碰到驗證碼咋辦?這裏分兩種狀況處理:

 

 

 

1.google那種驗證碼,

 

 

 

涼拌

 

 

2.簡單的驗證碼:

 

 

字符個數有限,只使用了簡單的平移或旋轉加噪音而沒有扭曲的,這種仍是有可能能夠處理的,通常思路是旋轉的轉回來,噪音去掉,而後劃分 單個字符,劃分好了之後再經過特徵提取的方法(例如PCA) 降維並生成特徵庫,而後把驗證碼和特徵庫進行比較。這個比較複雜,一篇博文是說不完的,這裏就不展開了,具體作法請弄本相關教科書好好研究一下。

 

 

3.事實上有些驗證碼仍是很弱的,

 

 

這裏就不點名了,反正我經過2的方法提取過準確度很是高的驗證碼,因此2事實上是可行的。

 

 

6.總結

基本上我遇到過的全部狀況,用以上方法都順利解決了。

 


<--注意:以上內容均轉自月語凋凌 的博客用python爬蟲抓站的一些技巧總結,我只是作了整理,如下內容爲本人原創-->

 

利用這篇博客裏的例子,我先模仿了一個本身的Python爬蟲。

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">import urllib2  
  2. content = urllib2.urlopen('http://blog.csdn.net/yuri_4_vera').read()</span>  

 

我是用Sublime Text2 做爲編輯器,直接利用Mac裏安裝的Python編譯的,報出以下錯誤:

urllib2.HTTPError: HTTP Error 403: Forbidden

嗯~果真沒錯,csdn是反感爬蟲的,但是我只是想方便的知道個人訪問量和評論數嘛...

因而採用博客裏的3.3方法,把urllib2假裝成瀏覽器訪問,個人代碼就變成了這樣:

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">import urllib2  
  2.   
  3. headers = {  
  4.     'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'  
  5. }  
  6. req = urllib2.Request(  
  7.     url = 'http://blog.csdn.net/yuri_4_vera',  
  8.     headers = headers  
  9. )  
  10.   
  11. content = urllib2.urlopen(req).read()  
  12. print content</span>  





成功的抓下來個人整個博客主頁有木有!print輸出的全是HTML的代碼有木有!看到嘩啦啦的代碼刷下去太舒服了..
學校課程真緊..操做系統、數據挖掘兩個大實驗..今兒就到這兒吧。
就不附上主頁的代碼了,若是用Chrome的話,能夠右鍵審查元素。Safari的話能夠在偏好設置裏勾選「在菜單欄裏顯示開發菜單」,就能夠右鍵檢查元素了。


主要是須要這一部分的內容:

 

 

[html]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;"><ul id="blog_rank">  
  2.     <li>訪問:<span>308次</span></li>  
  3.     <li>積分:<span>41分</span></li>  
  4.     <li>排名:<span>千里以外</span></li>  
  5. </ul>  
  6. <ul id="blog_statistics">  
  7.     <li>原創:<span>4篇</span></li>  
  8.     <li>轉載:<span>0篇</span></li>  
  9.     <li>譯文:<span>0篇</span></li>  
  10.     <li>評論:<span>1條</span></li>  
  11. </ul></span>  


 

2013.11.5_抓到了!

抓到了!先看看抓到的東西長什麼樣:


 

最底下的部分就是抓出來的。

 

昨天已經抓到了整個主頁,今天主要是想辦法把須要用的這部份內容挑出來。

昨晚上簡單查了下,聽說須要使用正則表達式,聽到這個名字只以爲耳熟不以爲有數...

問了問同窗,發覺彷佛是離散數學仍是什麼課裏講過的一個部分……上課不聽課的默默去谷歌了..

到了中午回來,仍是不知道要怎麼具體操做,後來在Ubuntu中文搜到這樣一篇文章

Python正則表達式操做指南

其中有這麼一個部分 4.4 前向界定符

 

受益不淺!

嘗試了一下

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">result = re.findall(r'(?<=\<ul id=\"blog\_rank\"\>).+?(?=\<\/ul\>)',content)</span>  


很顯然..出錯了。

 

Python的轉義字符仍是不太懂的樣子..

後來嘗試

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">result = re.findall(r'(?<=<ul id="blog_rank">).+?(?=\</ul>)',content)</span>  


只能篩選出來「[]」,很奇怪……

 

後來就隨便一試,發現了

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">result = re.findall(r'(?<=<li>).+?(?=</li>)',content)</span>  


成功的抓出了我須要的部分!

 

 

[html]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. <span style="font-size:18px;">訪問:<span>308次</span>  
  2. 積分:<span>41分</span>  
  3. 排名:<span>千里以外</span>  
  4. 原創:<span>4篇</span>  
  5. 轉載:<span>0篇</span>  
  6. 譯文:<span>0篇</span>  
  7. 評論:<span>1條</span></span>  


熱淚盈眶……

 

 

 

完結!


附上代碼:

 

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. import urllib2  
  2. import re  
  3.   
  4. headers = {  
  5.     'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'  
  6. }  
  7. req = urllib2.Request(  
  8.     url = 'http://blog.csdn.net/yuri_4_vera',  
  9.     headers = headers  
  10. )  
  11.   
  12. content = urllib2.urlopen(req).read()  
  13. result = re.findall(r'(?<=<li>).+?(?=</li>)',content)  
  14.   
  15. for x in xrange(0,7):  
  16.     print result[x]  
  17.     pass  



最近忙別的事情,這裏再加一句替換,就能夠把煩人的<span>和</span>去掉,用php抓的那篇博文裏我有加,python最近還沒空繼續鑽...

 

/************2013-12-3**************/

更新:

感謝@laochx的反饋,對於編碼問題的解決,能夠添加如下代碼:

 

 

[python]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
    1. content = urllib2.urlopen(req).read()  
    2. content=content.decode("utf8")  
    3. result = re.findall(r'(?<=<li>).+?(?=</li>)',content)  
相關文章
相關標籤/搜索