神刀安全网

使用pyspider抓取起点中文网小说数据


简介

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 all --data-path  数据存放路径

爬虫脚本编写&执行

首先看一下启动成功后,浏览器访问127.0.0.1:5000地址的界面如下

使用pyspider抓取起点中文网小说数据

点击Create,新建项目

使用pyspider抓取起点中文网小说数据

点击生成的项目名,进入脚本编写&调试页面

使用pyspider抓取起点中文网小说数据

Paste_Image.png

先看一下对应的爬虫脚本

#!/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')
使用pyspider抓取起点中文网小说数据

  1. 当使用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()
使用pyspider抓取起点中文网小说数据

执行结果及简单数据分析

共采集到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

未完待续

  1. 由于要规避反爬虫策略,导致目前抓取效率偏低,目前一天只能抓取9W条数据,有没有方法优化?

  2. 目前抓取的数据保存在pyspider自带的result.db (sqlite3数据库)中,导致数据分析不太方便,可否有别的存储方式?mongodb?mysql?

  3. 抓取时设定的是总共有27754页,那按理来说总共应该有20*27754=55w数据才对,可是目前最后抓取出来总共只有328615的作品数据,哪里少了?是作品详情链接无效了?还是抓取失败了?

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 使用pyspider抓取起点中文网小说数据

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址