Scrapy爬取顶点小说

一、目标地址:www.23us.so/

二、准备工作 1.开发环境 MacOS + PyCharm + Python3.5.3 + Scrapy + MySQL 2.安装Scrapy和MySQL自行解决

三、开始建项目和编写爬虫 在终端新建scrapy项目

scrapy startproject dingdian
复制代码
  • 因为我们要将数据保存到MySQL数据库中,所以在这里需要自定义MySQL的Pipeline,用PyCharm打开项目,在项目文件夹中新建一个python模块‘mysqlpipelines’,区分框架自带的Pipeline。
  • 在spiders文件夹下创建我们自己的spider(dingdian)
  • 新建一个run.py文件,用作运行爬虫的入口

run.py的内容,'dingdian'是spider的唯一名称,在定义spider的时候定义

from scrapy.cmdline import execute

execute(['scrapy', 'crawl', 'dingdian'])
复制代码

项目结构

建立模型item,在items.py中写入

import scrapy


class DingdianItem(scrapy.Item):
    # define the fields for your item here like:

    name = scrapy.Field()

    author = scrapy.Field()

    novelurl = scrapy.Field()

    # 状态
    serialstatus = scrapy.Field()

    # 字数
    serialnumber = scrapy.Field()

    # 类别
    category = scrapy.Field()

    # 编号
    name_id = scrapy.Field()

class DcontentItem(scrapy.Item):
    # 小说编号
    id_name = scrapy.Field()

    # 章节内容
    chaptercontent = scrapy.Field()

    # 用于绑定章节顺序
    num = scrapy.Field()

    # 章节地址
    chapterurl = scrapy.Field()

    # 章节名字
    chaptername = scrapy.Field()
复制代码

然后我们看一下入口地址 www.23us.so/

玄幻魔法:www.23us.so/list/1_1.ht…

武侠修真:www.23us.so/list/2_1.ht…

都市言情:www.23us.so/list/3_1.ht…

历史军事:www.23us.so/list//4_1.h…

网游竞技:www.23us.so/list/5_1.ht…

科幻小说:www.23us.so/list/6_1.ht…

恐怖灵异:www.23us.so/list/7_1.ht…

女生小说:www.23us.so/list/8_1.ht…

其他:www.23us.so/list/9_1.ht…

全本:www.23us.so/full.html

当然对于上面的地址,通过base_url + '_d'的方式请求

import re
import scrapy
from scrapy import Request
from bs4 import BeautifulSoup
from dingdian.items import DingdianItem, DcontentItem
from dingdian.mysqlpipelines.sql import Sql

class MySpider(scrapy.Spider):
    name = "dingdian"
    allowed_domains = ['23us.so']
    base_url = 'https://www.23us.so/list/'

    def start_requests(self):
        for i in range(1, 10):
            url = self.base_url + str(i) + '_1' + '.html' #小说分类的url
            yield Request(url, self.parse)
            # 全本
        yield Request('https://www.23us.so/full.html', callback=self.parse)
复制代码

对于上面的代码,创建一个类 Myspider,这个类继承自scrapy.Spider,定义name:dingdian (请注意,这name就是在run.py文件中的第三个参数!),此Name的名字在整个项目中有且只能有一个,名字不可重复!

定义了一个allowed_domains;这个不是必须的,但是在某些情况下需要用得到,比如使用爬取规则的时候就需要了,它的作用是只会跟进存在于allowed_domains中的URL,不存在的URL会被忽略。使用字符串拼接的方式实现了上面发现的小说分类的所有URL。

最后使用parse函数接受上面request获取到的response,返回的response中的url便是每个小说分类的链接,每个分类下有很多页的内容,我们需要拿到页码

找到此处页码的标签

 def parse(self, response):
        max_num = response.css('div.pagelink a.last::text').extract_first()

        for num in range(1, int(max_num) + 1):
            next_page = str(response.url)[:-7] + '_' + str(num) + '.html'
            if next_page is not  None:
                yield Request(next_page, callback=self.get_name)
复制代码

然后通过字符串拼接出每一页的链接next_page,当next_page存在的时候,便去请求,这里response.css是通过css选择器来查找标签的,查找标签的方式有很多,可以用css,xpath,或者BeautifulSoup。可以通过chrome或者Firefox快速获取到指定标签的css和xpath路径。在chrome中打开代码检查,点击这个图标

然后在网页上选择你要查看的内容,便会自动跳转到指定内容的html标签,然后在标签上右键便可复制css和xpath路径,这样取出的路径会比自己写的长,所以我还是选择自己写比较简洁。

上面两个函数就彻底的把整个网站的所有小说的页面URL的提取出来了,并将每个页面的response交给了get_name函数处理。

    def get_name(self, response):
        tds = BeautifulSoup(response.text, 'lxml').find_all('tr', bgcolor='#FFFFFF')
        for td in tds:
            novelname = td.find('a').get_text()
            novelurl = td.find('a')['href']
            yield Request(novelurl, callback=self.get_chapterurl, meta={'name': novelname, 'url': novelurl})
复制代码

获取小说name和url,通过reques的meta将额外参数传递给get_chapterurl函数

    def get_chapterurl(self, response):
        item = DingdianItem()

        item['name'] = str(response.meta['name']).replace('\xa0', '')
        item['novelurl'] = response.meta['url']
        category = response.css('table a::text').extract_first()
        author = response.css('table td::text').extract()[1]

        # 最新章节
        bash_url = response.css('p.btnlinks a.read::attr(href)').extract_first()

        name_id = str(bash_url).split('/')[-2]

        item['category'] = str(category).replace('/', '')
        item['author'] = str(author).replace('/', '')
        item['name_id'] = name_id
        yield item

        yield Request(url=bash_url, callback=self.get_chapter, meta={'name_id': name_id})
复制代码

将需要的数据,复制给item[key] (注意这儿的Key就是前面在item文件中定义的那些字段)

注意!response.meta[key]:这个是提取从上一个函数传递下来的值。

return item 就是返回我们的字典了,然后Pipelines就可以开始对这些数据进行处理了。比如存储到MySQL中。

遍历每个小说的章节和之前的操作类似,都是查找标签,接下来我们说说通过Pipeline存储到MySQL的问题

新建两张表,一张存储 书名 + 作者 + 分类,另一张存储 章节名称 + 内容,我是用navicate for mysql管理的数据库

DROP TABLE IF EXISTS `dd_name`;
CREATE TABLE `dd_name` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `xs_name` varchar(255) DEFAULT NULL,
  `xs_author` varchar(255) DEFAULT NULL,
  `category` varchar(255) DEFAULT NULL,
  `name_id` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8mb4;
复制代码
DROP TABLE IF EXISTS `dd_chaptername`;
CREATE TABLE `dd_chaptername` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `xs_chaptername` varchar(255) DEFAULT NULL,
  `xs_content` text,
  `id_name` int(11) DEFAULT NULL,
  `num_id` int(11) DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2726 DEFAULT CHARSET=gb18030;
SET FOREIGN_KEY_CHECKS=1;
复制代码

在settings.py文件中定义好MySQL的配置文件,账户密码端口和数据库都根据自己本地的配置填写,‘DingDianBooks’是我为这个项目建的数据库,默认的端口一般都是3306.

# mysql
MYSQL_HOSTS = '127.0.0.1'
MYSQL_USER = 'root'
MYSQL_PASSWORD = '11111'
MYSQL_PORT = '3306'
MYSQL_DB = 'DingDianBooks'
复制代码

Python连接MySQL数据库需要下载另外的包,我这里使用的是mysql-connector,通过pip安装管理。

下面是我们的sql.py文件:

import mysql.connector
from dingdian import settings

# mysql
MYSQL_HOSTS = settings.MYSQL_HOSTS
MYSQL_USER = settings.MYSQL_USER
MYSQL_PASSWORD = settings.MYSQL_PASSWORD
MYSQL_PORT = settings.MYSQL_PORT
MYSQL_DB = settings.MYSQL_DB

cnx = mysql.connector.connect(user=MYSQL_USER, password=MYSQL_PASSWORD, host=MYSQL_HOSTS, database=MYSQL_DB)
cur = cnx.cursor(buffered=True)

class Sql:
    # 插入书名 + 作者 + 分类
    @classmethod
    def insert_dd_name(cls , xs_name, xs_author, category, name_id):

        sql = 'INSERT INTO dd_name (`xs_name`, `xs_author`, `category`, `name_id`) VALUES (%(xs_name)s, %(xs_author)s, %(category)s, %(name_id)s)'

        value = {
            'xs_name' : xs_name,
            'xs_author': xs_author,
            'category': category,
            'name_id': name_id
        }

        cur.execute(sql, value)
        cnx.commit()

    # 去重
    @classmethod
    def select_name(cls, name_id):
        sql = 'SELECT EXISTS(SELECT 1 FROM dd_name WHERE name_id=%(name_id)s)'
        value = {
            'name_id': name_id
        }
        cur.execute(sql, value)
        return cur.fetchall()[0]
复制代码

初始化了一个MySQL的操作游标,将函数中的四个变量写入数据库,select_name是一个去重函数,这个函数会查找name_id这个字段,如果存在则会返回 1 不存在则会返回0。

sqi.py这一部分完成,现在开始写pipeline:

from .sql import Sql
from dingdian.items import DingdianItem, DcontentItem

class DingDianPipeline(object):
    def process_item(self, item, spider):
        if isinstance(item, DingdianItem):
            name_id = item['name_id']
            ret = Sql.select_name(name_id)
            if ret[0] == 1:
                print('已经存在')
                pass
            else:
                xs_name = item['name']
                xs_author = item['author']
                category = item['category']
                Sql.insert_dd_name(xs_name, xs_author, category, name_id)
复制代码

建立了一个DingdianPipeline的类,别忘了一定要继承object,定义了一个process_item函数并有item和spider这两个参数,这两个参数是必须的,当item中存在DingdianItem,先执行去重,然后就从item中取出值然后存入数据库。 另一种表的存取方式是类似的,详细的可以去看代码。

到此,真个爬虫差不多完成了,只需要在PyCharm中运行run.py便可以执行爬虫。

项目地址GitHub

转载于:https://juejin.im/post/5ba342cf6fb9a05cdc498400

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值