背景:
PySpider:一個國人編寫的強大的網路爬蟲系統並帶有強大的WebUI。採用Python語言編寫,分散式架構,支援多種資料庫後端,強大的WebUI支援指令碼編輯器,任務監視器,專案管理器以及結果檢視器。線上示例: http://demo.pyspider.org/
官方文件: http://docs.pyspider.org/en/l…
Github : https://github.com/binux/pysp…
本文爬蟲程式碼 Github 地址:https://github.com/zhisheng17…
更多精彩文章可以在微信公眾號:猿blog 閱讀到,歡迎關注。
說了這麼多,我們還是來看正文吧!
前提:
你已經安裝好了Pyspider 和 MySQL-python(儲存資料)
如果你還沒安裝的話,請看看我的前一篇文章,防止你也走彎路。
我所遇到的一些錯誤:
首先,本爬蟲目標:使用 Pyspider 框架爬取 V2EX 網站的帖子中的問題和內容,然後將爬取的資料儲存在本地。
V2EX 中大部分的帖子檢視是不需要登入的,當然也有些帖子是需要登陸後才能夠檢視的。(因為後來爬取的時候發現一直 error ,檢視具體原因後才知道是需要登入的才可以檢視那些帖子的)所以我覺得沒必要用到 Cookie,當然如果你非得要登入,那也很簡單,簡單地方法就是新增你登入後的 cookie 了。
我們在 https://www.v2ex.com/ 掃了一遍,發現並沒有一個列表能包含所有的帖子,只能退而求其次,通過抓取分類下的所有的標籤列表頁,來遍歷所有的帖子: https://www.v2ex.com/?tab=tech 然後是 https://www.v2ex.com/go/progr… 最後每個帖子的詳情地址是 (舉例): https://www.v2ex.com/t/314683…
建立一個專案
在 pyspider 的 dashboard 的右下角,點選 “Create” 按鈕
替換 on_start 函式的 self.crawl 的 URL:
1 2 3 |
@every(minutes=24 * 60) def on_start(self): self.crawl('https://www.v2ex.com/', callback=self.index_page, validate_cert=False) |
- self.crawl 告訴 pyspider 抓取指定頁面,然後使用 callback 函式對結果進行解析。
- @every) 修飾器,表示 on_start 每天會執行一次,這樣就能抓到最新的帖子了。
- validate_cert=False 一定要這樣,否則會報 HTTP 599: SSL certificate problem: unable to get local issuer certificate錯誤
首頁:
點選綠色的 run 執行,你會看到 follows 上面有一個紅色的 1,切換到 follows 皮膚,點選綠色的播放按鈕:
第二張截圖一開始是出現這個問題了,解決辦法看前面寫的文章,後來問題就不再會出現了。
Tab 列表頁 :
在 tab 列表頁 中,我們需要提取出所有的主題列表頁 的 URL。你可能已經發現了,sample handler 已經提取了非常多大的 URL
程式碼:
1 2 3 4 |
@config(age=10 * 24 * 60 * 60) def index_page(self, response): for each in response.doc('a[href^="https://www.v2ex.com/?tab="]').items(): self.crawl(each.attr.href, callback=self.tab_page, validate_cert=False) |
- 由於帖子列表頁和 tab列表頁長的並不一樣,在這裡新建了一個 callback 為 self.tab_page
- @config(age=10 24 60 * 60) 在這表示我們認為 10 天內頁面有效,不會再次進行更新抓取
Go列表頁 :
程式碼:
1 2 3 4 |
@config(age=10 * 24 * 60 * 60) def tab_page(self, response): for each in response.doc('a[href^="https://www.v2ex.com/go/"]').items(): self.crawl(each.attr.href, callback=self.board_page, validate_cert=False) |
帖子詳情頁(T):
你可以看到結果裡面出現了一些reply的東西,對於這些我們是可以不需要的,我們可以去掉。
同時我們還需要讓他自己實現自動翻頁功能。
程式碼:
1 2 3 4 5 6 7 8 9 |
@config(age=10 * 24 * 60 * 60) def board_page(self, response): for each in response.doc('a[href^="https://www.v2ex.com/t/"]').items(): url = each.attr.href if url.find('#reply')>0: url = url[0:url.find('#')] self.crawl(url, callback=self.detail_page, validate_cert=False) for each in response.doc('a.page_normal').items(): self.crawl(each.attr.href, callback=self.board_page, validate_cert=False) #實現自動翻頁功能 |
去掉後的執行截圖:
實現自動翻頁後的截圖:
此時我們已經可以匹配了所有的帖子的 url 了。
點選每個帖子後面的按鈕就可以檢視帖子具體詳情了。
程式碼:
1 2 3 4 5 6 7 8 9 10 |
@config(priority=2) def detail_page(self, response): title = response.doc('h1').text() content = response.doc('div.topic_content').html().replace('"', '\"') self.add_question(title, content) #插入資料庫 return { "url": response.url, "title": title, "content": content, } |
插入資料庫的話,需要我們在之前定義一個add_question函式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#連線資料庫 def __init__(self): self.db = MySQLdb.connect('localhost', 'root', 'root', 'wenda', charset='utf8') def add_question(self, title, content): try: cursor = self.db.cursor() sql = 'insert into question(title, content, user_id, created_date, comment_count) values ("%s","%s",%d, %s, 0)' % (title, content, random.randint(1, 10) , 'now()'); #插入資料庫的SQL語句 print sql cursor.execute(sql) print cursor.lastrowid self.db.commit() except Exception, e: print e self.db.rollback() |
檢視爬蟲執行結果:
- 先debug下,再調成running。pyspider框架在windows下的bug
- 設定跑的速度,建議不要跑的太快,否則很容易被發現是爬蟲的,人家就會把你的IP給封掉的
- 檢視執行工作
- 檢視爬取下來的內容
然後再本地資料庫GUI軟體上查詢下就可以看到資料已經儲存到本地了。
自己需要用的話就可以匯入出來了。
在開頭我就告訴大家爬蟲的程式碼了,如果詳細的看看那個project,你就會找到我上傳的爬取資料了。(僅供學習使用,切勿商用!)
當然你還會看到其他的爬蟲程式碼的了,如果你覺得不錯可以給個 Star,或者你也感興趣的話,你可以fork我的專案,和我一起學習,這個專案長期更新下去。
最後:
程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# created by 10412 # !/usr/bin/env python # -*- encoding: utf-8 -*- # Created on 2016-10-20 20:43:00 # Project: V2EX from pyspider.libs.base_handler import * import re import random import MySQLdb class Handler(BaseHandler): crawl_config = { } def __init__(self): self.db = MySQLdb.connect('localhost', 'root', 'root', 'wenda', charset='utf8') def add_question(self, title, content): try: cursor = self.db.cursor() sql = 'insert into question(title, content, user_id, created_date, comment_count) values ("%s","%s",%d, %s, 0)' % (title, content, random.randint(1, 10) , 'now()'); print sql cursor.execute(sql) print cursor.lastrowid self.db.commit() except Exception, e: print e self.db.rollback() @every(minutes=24 * 60) def on_start(self): self.crawl('https://www.v2ex.com/', callback=self.index_page, validate_cert=False) @config(age=10 * 24 * 60 * 60) def index_page(self, response): for each in response.doc('a[href^="https://www.v2ex.com/?tab="]').items(): self.crawl(each.attr.href, callback=self.tab_page, validate_cert=False) @config(age=10 * 24 * 60 * 60) def tab_page(self, response): for each in response.doc('a[href^="https://www.v2ex.com/go/"]').items(): self.crawl(each.attr.href, callback=self.board_page, validate_cert=False) @config(age=10 * 24 * 60 * 60) def board_page(self, response): for each in response.doc('a[href^="https://www.v2ex.com/t/"]').items(): url = each.attr.href if url.find('#reply')>0: url = url[0:url.find('#')] self.crawl(url, callback=self.detail_page, validate_cert=False) for each in response.doc('a.page_normal').items(): self.crawl(each.attr.href, callback=self.board_page, validate_cert=False) @config(priority=2) def detail_page(self, response): title = response.doc('h1').text() content = response.doc('div.topic_content').html().replace('"', '\\"') self.add_question(title, content) #插入資料庫 return { "url": response.url, "title": title, "content": content, } |