坑:抄完代码准备开爬时,出现了目标计算机积极拒绝balabala的错误,查了半天也没解决,就放弃了。结果在用pip 安装时也报了这个错突然想起来我之前也遇见过这个错误。是由于用过vpn导致的。解决办法是打开控制面板/网络和Internet/Internet选项/连接/局域网设置/把代理服务器×了就ok了
eg.方便数据库排序
在spiders文件夹下建立sjzh.py
'''
实现了中文向阿拉伯数字转换
用于从小说章节名提取id来排序
'''
chs_arabic_map = {'零': 0, '一': 1, '二': 2, '三': 3, '四': 4,
'五': 5, '六': 6, '七': 7, '八': 8, '九': 9,
'十': 10, '百': 100, '千': 10 ** 3, '万': 10 ** 4,
'〇': 0, '壹': 1, '贰': 2, '叁': 3, '肆': 4,
'伍': 5, '陆': 6, '柒': 7, '捌': 8, '玖': 9,
'拾': 10, '佰': 100, '仟': 10 ** 3, '萬': 10 ** 4,
'亿': 10 ** 8, '億': 10 ** 8, '幺': 1,
'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5,
'7': 7, '8': 8, '9': 9}
num_list = ['1','2','4','5','6','7','8','9','0','一','二','三','四','五','六','七','八','九','十','零','千','百',]
def get_tit_num(title):
result =''
for char in title:
if char in num_list:
result+=char
return result
def Cn2An(chinese_digits):
result = 0
tmp = 0
hnd_mln = 0
for count in range(len(chinese_digits)):
curr_char = chinese_digits[count]
curr_digit = chs_arabic_map[curr_char]
# meet 「亿」 or 「億」
if curr_digit == 10 ** 8:
result = result + tmp
result = result * curr_digit
# get result before 「亿」 and store it into hnd_mln
# reset `result`
hnd_mln = hnd_mln * 10 ** 8 + result
result = 0
tmp = 0
# meet 「万」 or 「萬」
elif curr_digit == 10 ** 4:
result = result + tmp
result = result * curr_digit
tmp = 0
# meet 「十」, 「百」, 「千」 or their traditional version
elif curr_digit >= 10:
tmp = 1 if tmp == 0 else tmp
result = result + curr_digit * tmp
tmp = 0
# meet single digit
elif curr_digit is not None:
tmp = tmp * 10 + curr_digit
else:
return result
result = result + tmp
result = result + hnd_mln
return result
# test
print (Cn2An(get_tit_num('第一千三百九十一章 你妹妹被我咬了!')))
老规矩先建项目和spider
创建项目
scrapy startproject biquge
进入文件夹
cd biquge
生成爬虫文件
scrapy genspider xsphspider qu.la
编写items
import scrapy
class BiqugeItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 小说名字
bookname = scrapy.Field()
#章节名
title = scrapy.Field()
#正文
body = scrapy.Field()
#排序用id
order_id = scrapy.Field()
编写spider
由于我们的spider爬取顺序是这样的:
首先: 爬取排行榜页面,找到每一本小说的页面
接着: 爬取小说页面, 找到小说每一章的链接
最后: 爬取每一章节页面,找到文章标题和正文内容
我们再来复习一下 spider是怎么运作的:
首先: 从start_urls里发起请求,返回response
接着: 自动调用 parse函数
中间: 一系列我们自己添加的功能
最后: 返回item,给PIPELINE处理
为了实现我们定好的spider逻辑,我们得调用Scrapy内置的requests函数,
来介绍一下Scrapy.request函数:
class Request(url, callback=None, method='GET', headers=None, body=None, cookies=None, meta=None, encoding='utf-8', priority=0, dont_filter=False, errback=None)
# 这里其实和我们一直用的request模块也差不多,最主要需要注意的参数:
# callback 这个参数的意思是回调函数,就是会自动运行的函数,并将request获得的response自动传进去。
# -*- coding: utf-8 -*-
import scrapy
from biquge.items import BiqugeItem
# 导入我们自己写的函数
from .sjzh import Cn2An,get_tit_num
class XsphspiderSpider(scrapy.Spider):
name = "xsphspider"
allowed_domains = ["qu.la"]
start_urls = ['http://www.qu.la/paihangbang/']
novel_list = []
def parse(self, response):
# 找到各类小说排行榜名单
books = response.xpath('.//div[@class="index_toplist mright mbottom"]')
# 找到每一类小说排行榜的每一本小说的下载链接
for book in books[0:1]:
links = book.xpath('.//div[2]/div[2]/ul/li')
for link in links[0:2]:
url = 'http://www.qu.la' + \
link.xpath('.//a/@href').extract()[0]
self.novel_list.append(url)
# 简单的去重
self.novel_list = list(set(self.novel_list))
for novel in self.novel_list:
yield scrapy.Request(novel, callback=self.get_page_url)
def get_page_url(self, response):
'''
找到章节链接
'''
page_urls = response.xpath('.//dd/a/@href').extract()
for url in page_urls:
yield scrapy.Request('http://www.qu.la' + url,callback=self.get_text)
def get_text(self, response):
'''
找到每一章小说的标题和正文
并自动生成id字段,用于表的排序
'''
item = BiqugeItem()
# 小说名
item['bookname'] = response.xpath(
'.//div[@class="con_top"]/a[2]/text()').extract()[0]
# 章节名 ,将title单独找出来,为了提取章节中的数字
title = response.xpath('.//h1/text()').extract()[0]
item['title'] = title
# 找到用于排序的id值
item['order_id'] = Cn2An(get_tit_num(title))
# 正文部分需要特殊处理
body = response.xpath('.//div[@id="content"]/text()').extract()
# 将抓到的body转换成字符串,接着去掉\t之类的排版符号,
text = ''.join(body).strip().replace('\u3000', '')
item['body'] = text
return item
编写pipeline处理爬到的数据
import pymysql
class BiqugePipeline(object):
def process_item(self, item, spider):
'''
将爬到的小数写入数据库
'''
# 首先从items里取出数据
name = item['bookname']
order_id = item['order_id']
body = item['body']
title = item['title']
# 与本地数据库建立联系
# 和本地的scrapyDB数据库建立连接
connection = pymysql.connect(
host='localhost', # 连接的是本地数据库
user='root', # 自己的mysql用户名
passwd='********', # 自己的密码
db='bqgxiaoshuo', # 数据库的名字
charset='utf8mb4', # 默认的编码方式:
cursorclass=pymysql.cursors.DictCursor)
try:
with connection.cursor() as cursor:
# 数据库表的sql
sql1 = 'Create Table If Not Exists %s(id int,zjm varchar(20),body text)' % name
# 单章小说的写入
sql = 'Insert into %s values (%d ,\'%s\',\'%s\')' % (
name, order_id, title, body)
cursor.execute(sql1)
cursor.execute(sql)
# 提交本次插入的记录
connection.commit()
finally:
# 关闭连接
connection.close()
return item
配置settings
ITEM_PIPELINES = {
'biquge.pipelines.BiqugePipeline': 300,
}
中断后恢复任务
由于这次我们需要爬得数据量非常的大,
就算有强大的多线程也不是一时半会就能爬完的,
所以这里我们得知道如果爬虫爬到一半断了,我们如何从断的地方接着工作,
而不是从头开始
Job 路径
要启用持久化支持,你只需要通过 JOBDIR 设置 job directory 选项。这个路径将会存储 所有的请求数据来保持一个单独任务的状态(例如:一次spider爬取(a spider run))。必须要注意的是,这个目录不允许被不同的spider 共享,甚至是同一个spider的不同jobs/runs也不行。也就是说,这个目录就是存储一个 单独 job的状态信息。
如何使用?
要启用一个爬虫的持久化,运行以下命令:
scrapy crawl somespider -s JOBDIR=crawls/somespider-1
然后,你就能在任何时候安全地停止爬虫(按Ctrl-C或者发送一个信号)。
恢复这个爬虫也是同样的命令:
scrapy crawl somespider -s JOBDIR=crawls/somespider-1
战术总结
爬取过程中出现DEBUG: Redirecting (301) to <GET ht。。。。。。是正常现象不会错过一些网页,原作者sjzh.py设置数据库id的代码有小bug