簡介
pyspider是國人開發的相當好用的爬蟲框架。雖然網上教程不是很多,但是文檔詳細,操作簡單,非常適合用來做爬蟲練習或者實現一些抓取數據的需求。
本文就以抓取起點中文小說網的小說作品基礎信息作為目標,講解如何使用pyspider框架采集數據。
關于為何要選擇起點作為目標,其一、筆者作為網文愛好者,也想收集起點小說作品信息,找些熱門小說看;其二、起點作為比較成熟的小說網站,再反爬蟲方面應該有對應策略,剛好練習一下爬蟲怎么規避這些策略。
在閱讀本文之前,建議先看一下文檔及框架作者本人寫的中文教程
pyspider 爬蟲教程(一):HTML 和 CSS 選擇器
pyspider 爬蟲教程(二):AJAX 和 HTTP
pyspider 爬蟲教程(三):使用 PhantomJS 渲染帶 JS 的頁面
pyspider安裝及啟動
安裝很簡單,如果已安裝pip,直接執行命令
pip install pyspider
由于目前很多網站都是動態js生成頁面,需要安裝PhantomJS來獲得js執行后的頁面,而不是原本靜態的html頁面,我們再來裝一下
pip install phantomjs
待安裝完成后,我們先看一下pyspider對應的可執行命令
Usage: pyspider [OPTIONS] COMMAND [ARGS]...
A powerful spider system in python.
Options:
-c, --config FILENAME a json file with default values for subcommands.
{“webui”: {“port”:5001}}
--logging-config TEXT logging config file for built-in python logging
module [default: pyspider/pyspider/logging.conf]
--debug debug mode
--queue-maxsize INTEGER maxsize of queue
--taskdb TEXT database url for taskdb, default: sqlite
--projectdb TEXT database url for projectdb, default: sqlite
--resultdb TEXT database url for resultdb, default: sqlite
--message-queue TEXT connection url to message queue, default: builtin
multiprocessing.Queue
--amqp-url TEXT [deprecated] amqp url for rabbitmq. please use
--message-queue instead.
--beanstalk TEXT [deprecated] beanstalk config for beanstalk queue.
please use --message-queue instead.
--phantomjs-proxy TEXT phantomjs proxy ip:port
--data-path TEXT data dir path
--version Show the version and exit.
--help Show this message and exit.
在這里我們直接執行如下命令啟動,更復雜的命令參看文檔
pyspider --data-path 數據存放路徑 all
爬蟲腳本編寫&執行
首先看一下啟動成功后,瀏覽器訪問127.0.0.1:5000地址的界面如下
點擊Create,新建項目
點擊生成的項目名,進入腳本編寫&調試頁面
先看一下對應的爬蟲腳本
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Created on 2017-01-13 11:38:33
# Project: qidian_allbook
import time
from pyspider.libs.base_handler import *
import requests
import random
import re
PAGE_START = 1
PAGE_END = 27754
def get_proxy():
return requests.get("http://127.0.0.1:5001/get/").content
def get_all_proxy():
return requests.get("http://127.0.0.1:5001/get_all/").content
def delete_proxy(proxy):
requests.get("http://127.0.0.1:5001/delete/?proxy={}".format(proxy))
class Handler(BaseHandler):
headers= {
#"Host": "book.qidian.com",
#"Connection": "keep-alive",
#"Accept-Encoding": "gzip, deflate, sdch",
#"Accept-Language":"zh-CN,zh;q=0.8,en;q=0.6",
#"Referer":"https://www.baidu.com", "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
#"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36"
}
crawl_config = {
"headers" : headers,
"timeout" : 1000
}
def __init__(self):
self.baseUrl = "http://a.qidian.com/?size=-1&sign=-1&tag=-1&chanId=-1&subCateId=-1&orderId=&update=-1&month=-1&style=1&action=-1&vip=-1&page="
self.page_num = PAGE_START
self.total_num = PAGE_END
#self.proxy_all = get_all_proxy()
#pattern = re.compile(r'"(.+?)"',re.S)
#self.proxy = pattern.findall(self.proxy_all)
#self.index = 1
@every(minutes=24 * 60)
@config(priority=3)
def on_start(self):
while self.page_num <= self.total_num:
url = self.baseUrl + str(self.page_num)
self.crawl(url, callback=self.index_page)
self.page_num += 1
@config(age=10 * 24 * 60 * 60)
def index_page(self, response):
for each in response.doc('h4 > a').items():
self.crawl(each.attr.href, callback=self.detail_page)
def detail_page(self, response):
url = response.url
name = response.doc('h1 > em').text()
author = response.doc('h1 a').text()
word_count = response.doc('.book-info > p > em').eq(0).text()
click_count = response.doc('.book-info > p > em').eq(1).text()
recommend_count = response.doc('.book-info > p > em').eq(2).text()
return {
"url": url,
"name": name,
"author": author,
"word_count": word_count,
"click_count": click_count,
"recommend_count": recommend_count
}
腳本編寫及測試抓取遇到的問題
1.測試抓取時,運行一段時間后出現所有抓取鏈接均FetchError的報錯,抓取失敗
失敗原因:未設置User-Agent 及 抓取速率太快,導致IP被封禁
解決辦法:
1) 設置User-Agent,調整速率從1->0.7
2) 使用代理IP,防止被封禁,這里筆者嘗試使用搭建簡易免費代理IP池,但是由于免費代理大多不可用,會導致抓取不穩定,還是決定放棄使用
2.筆者本來是打算通過不斷抓取下一頁的鏈接,來遍歷所有小說作品的,可是由于這部分是JS動態生成的,雖然使用phantomjs,能解決這個問題(具體見作者教程3),但是使用phantomjs會導致抓取效率變低,后來還是選擇采用固定首尾頁數(PAGE_START,PAGE_END)的方法
self.crawl(url, callback=self.index_page,fetch_type='js')
3.當使用css選擇器有多個數據時,怎么獲取自己想要的
比如在小說詳細頁,有字數,點擊數,推薦數三個
其css selector均為 .book-info > p > em,要獲取對應的次數只能使用pyquery的.eq(index)的方法去獲取對應的文本數據了
word_count = response.doc('.book-info > p > em').eq(0).text()
click_count = response.doc('.book-info > p > em').eq(1).text()
recommend_count = response.doc('.book-info > p > em').eq(2).text()
4.如果遇到抓取的鏈接是https,而不是http的,使用self.crawl()方法時,需要加入參數validate_cert =False,同時需要確保pyspider --version 版本再0.3.6.0之上
具體解決方法,可以查看如下鏈接:
PySpider HTTP 599: SSL certificate problem錯誤的解決方法
執行結果及簡單數據分析
共采集到328615部小說的作品信息
點擊榜前十:
0: 斗破蒼穹 點擊次數為:150279300
1: 盤龍 點擊次數為:94564599
2: 吞噬星空 點擊次數為:84666200
3: 從零開始 點擊次數為:64794200
4: 斗羅大陸 點擊次數為:63158100
5: 遮天 點擊次數為:62921100
6: 莽荒紀 點擊次數為:61701100
7: 神墓 點擊次數為:52844700
8: 星辰變 點擊次數為:49905900
9: 陽神 點擊次數為:49869399
推薦榜前十:
0: 從零開始 推薦次數為:13328400
1: 盤龍 推薦次數為:7716700
2: 吞噬星空 推薦次數為:7463500
3: 江山美人志 推薦次數為:7307300
4: 一念永恒 推薦次數為:6909400
5: 大主宰 推薦次數為:6804100
6: 星辰變 推薦次數為:6714600
7: 我真是大明星 推薦次數為:6689900
8: 斗破蒼穹 推薦次數為:6644200
9: 完美世界 推薦次數為:6625200
字數榜前十:
0: 帶著農場混異界 字數為:22389600
1: 重生之妖孽人生 字數為:21814900
2: 從零開始 字數為:20180800
3: 暗黑破壞神之毀滅 字數為:15993400
4: 修神外傳 字數為:15944200
5: 煮酒點江山 字數為:15167600
6: 重生1991 字數為:13475700
7: 三國小兵之霸途 字數為:13108200
8: 校花的貼身高手 字數為:12295000
9: 重生之資源大亨 字數為:12141100
簡單數據分析之二
采用SCWS 中文分詞對所有作品名字進行分詞統計,得到出現頻率最高的排行
看起來如果寫小說,起個『重生之我的神魔異世界』這類標題是不是吊炸天
詞出現頻率排行:
之 出現次數為:54104
的 出現次數為:28373
神 出現次數為:12308
異 出現次數為:12051
天 出現次數為:11507
界 出現次數為:11280
我 出現次數為:10813
魔 出現次數為:10008
仙 出現次數為:8543
世 出現次數為:6408
重生 出現次數為:6225
劍 出現次數為:6202
網游 出現次數為:5533
世界 出現次數為:5472
修 出現次數為:5372
錄 出現次數為:4927
道 出現次數為:4658
戰 出現次數為:4632
魂 出現次數為:4332
天下 出現次數為:4129
簡單數據分析之三
簡單統計一下起點作者的作品數排序
武俠精品應該是起點的官方作者號吧,不然194本作品也太恐怖了
也發現了不少熟悉的大神,比如唐家三少,流浪的蛤蟆,骷髏精靈等,有些作品還是可以看看的
0: 作者:武俠精品 發布作品數:194
1: 作者:石三 發布作品數:15
2: 作者:愛妻族 發布作品數:13
3: 作者:唐家三少 發布作品數:12
4: 作者:流浪的蛤蟆 發布作品數:12
5: 作者:莊小街 發布作品數:11
6: 作者:殷揚 發布作品數:11
7: 作者:沐軼 發布作品數:11
8: 作者:飄零幻 發布作品數:10
9: 作者:天凈沙秋思 發布作品數:10
10: 作者:骷髏精靈 發布作品數:10
未完待續
由于要規避反爬蟲策略,導致目前抓取效率偏低,目前一天只能抓取9W條數據,有沒有方法優化?
目前抓取的數據保存在pyspider自帶的result.db (sqlite3數據庫)中,導致數據分析不太方便,可否有別的存儲方式?mongodb?mysql?
抓取時設定的是總共有27754頁,那按理來說總共應該有20*27754=55w數據才對,可是目前最后抓取出來總共只有328615的作品數據,哪里少了?是作品詳情鏈接無效了?還是抓取失敗了?