博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Scrapy爬取顶点小说
阅读量:5877 次
发布时间:2019-06-19

本文共 7124 字,大约阅读时间需要 23 分钟。

一、目标地址:

二、准备工作 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 executeexecute(['scrapy', 'crawl', 'dingdian'])复制代码

项目结构

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

import scrapyclass 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()复制代码

然后我们看一下入口地址

玄幻魔法:

武侠修真:

都市言情:

历史军事:

网游竞技:

科幻小说:

恐怖灵异:

女生小说:

其他:

全本:

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

import reimport scrapyfrom scrapy import Requestfrom bs4 import BeautifulSoupfrom dingdian.items import DingdianItem, DcontentItemfrom dingdian.mysqlpipelines.sql import Sqlclass 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.

# mysqlMYSQL_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.connectorfrom dingdian import settings# mysqlMYSQL_HOSTS = settings.MYSQL_HOSTSMYSQL_USER = settings.MYSQL_USERMYSQL_PASSWORD = settings.MYSQL_PASSWORDMYSQL_PORT = settings.MYSQL_PORTMYSQL_DB = settings.MYSQL_DBcnx = 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 Sqlfrom dingdian.items import DingdianItem, DcontentItemclass 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便可以执行爬虫。

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

你可能感兴趣的文章
Akka actor tell, ask 函数的实现
查看>>
windows10 chrome 调试 ios safari 方法
查看>>
Netty 4.1.35.Final 发布,经典开源 Java 网络服务框架
查看>>
详解Microsoft.AspNetCore.CookiePolicy
查看>>
SCDPM2012 R2实战一:基于SQL 2008 R2集群的SCDPM2012 R2的安装
查看>>
SQL SERVER中字段类型与C#数据类型的对应关系
查看>>
Linux lsof命令详解
查看>>
SVG path
查看>>
js判断checkbox是否选中
查看>>
多系统盘挂载
查看>>
MySQL函数怎么加锁_MYSQL 函数调用导致自动生成共享锁问题
查看>>
MR1和MR2的工作原理
查看>>
Eclipse中修改代码格式
查看>>
GRUB Legacy
查看>>
关于 error: LINK1123: failure during conversion to COFF: file invalid or corrupt 错误的解决方案...
查看>>
python实现链表
查看>>
java查找string1和string2是不是含有相同的字母种类和数量(string1是否是string2的重新组合)...
查看>>
Android TabActivity使用方法
查看>>
Eclipse的 window-->preferences里面没有Android选项
查看>>
《麦田里的守望者》--[美]杰罗姆·大卫·塞林格
查看>>