Scrapy爬取数据并存储到MySQL
一、框架简介
1.1、简介
Scrapy框架是用纯Python实现的一个为了爬取网站数据、提取结构性数据而编写的可扩展的开源应用框架,只需要少量代码就能够快速地实现数据爬取。往往手写一个爬虫需要进行发送网络请求、数据解析、数据存储、反反扒机制、异步请求等步骤,如果都从零开始写是比较浪费时间的,同时会降低开发的效率。Scrapy框架已经帮我们把这些基础的东西都进行了封装,只需要按照模板编写自己的爬虫模块和逻辑就可以轻松实现一个爬虫,进而提高爬虫和开发的工作效率。
二、Scrapy架构与流程
2.1、架构图及组件功能
2.1.1、架构图
2.1.2、组件功能
- Spiders:爬虫,负责处理所有的Responses,从中分析并提取数据(Item字段需要的数据),同时将需要继续跟进的URL提交给引擎,再次进入调度器。Engine:引擎,负责爬虫、管道、下载器、调度器中间的通信、信号以及数据的传递等,是框架的核心,用来触发事务和处理整个系统的数据流。
- Scheduler:调度器,接收引擎发送的请求,对发送的请求进行去重,并按照一定方式进行整理、排列、入队,当引擎需要时返回队列当中的请求给引擎。
- Downloader:下载器,负责下载引擎发送的请求对应的网页的内容,将请求结果交给引擎,由引擎交给爬虫处理。
- ItemPipeline:管道,主要负责处理爬虫中获取到的Item,并进行后期处理(分析、过滤、存储)。
- Downloader Middlewares:下载中间件,介于引擎和下载器之间,主要处理引擎与下载器之间的请求及响应。
- Spider Middlewares: 爬虫中间件,介于引擎和爬虫之间,主要工作是处理爬虫的响应输入和请求输出(如进入Spider的Responses和从Spider出去的Requests)。
2.2、流程图及流程描述
2.2.1、流程图
2.2.2、流程描述
1、爬虫将需要发送的请求提交给引擎。
2、引擎把从爬虫获取到的请求传递给调度器。
3、调度器接收引擎发来的请求,并进行整理、排列、入队生成Request交还给引擎。
4、引擎拿到从调度器中传递过来的Request,通过MIDDLEWARE进行层层过滤后发送给下载器。
5、下载器去互联网中下载并获取Response数据,通过MIDDLEWARE层层过滤后将Response数据返回给引擎。
6、引擎获取到下载器返回的Response数据传递给爬虫。
7、爬虫获取到Response数据,通过parse()方法提取Items数据和Requests并返回给引擎。
8、引擎接收到Items和Requests,将Items传递给Piplines进行分析过滤、存储,把Requests继续传给调度器,直到调度器不存在任何Requests了,整个程序停止,对于失败的URL也会进行重新下载。
三、Scrapy的使用
3.1、Scrapy的安装
pip install scrapy
3.2、Scrapy的相关命令
创建项目
scrapy startproject [项目名称]
创建爬虫
scrapy genspider [爬虫名称] [域名]
3.3、Scrapy项目结构及文件作用
3.3.1、项目结构图
3.3.2、项目文件作用
1.items.py:用来存放爬虫爬取下来的数据的模型
2.middlewares.py:用来存放各种中间的文件
3.pipelines:用来将items的模型存储到本地磁盘中
4.settings.py:爬虫的一些配置信息(如请求头、多久发送一次请求、ip代理)
5.scrapy.cfg:项目的配置文件
6.spiders:存放所有的爬虫文件
3.3.3、开始一个爬虫
1、配置爬虫
打开settings修改配置如下
# 将遵守机器人协议改成False,否则有些会爬取不到内容
ROBOTSTXT_OBEY = False
# 设置默认请求头
DEFAULT_REQUEST_HEADERS = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9','Accept-Language': 'zh-CN,zh;q=0.9','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36','cookie': 'ASP.NET_SessionId=22da5okb1anxk5a3iezvbsuw; Hm_lvt_9007fab6814e892d3020a64454da5a55=1600827557,1601015578,1601015610,1601190738; codeyzgswso=e488b2230b94cd65; gsw2017user=1292023%7cE3869858693238EAC1DDA4FEC499C6DF; login=flase; wxopenid=defoaltid; gswZhanghao=18397716181; gswPhone=18397716181; Hm_lpvt_9007fab6814e892d3020a64454da5a55=1601190860',
}
# 设置MySql数据库
DB_SETTINGS = {'db1': {'host': '127.0.0.1','db': 'ancient_poetry','user': 'root','password': 'root','port': 3306,'cursorclass': pymysql.cursors.DictCursor, # 指定cursor类型,},
}
# 激活pipelines,需要添加才能够使用pipelines
ITEM_PIPELINES = {'ancient_poetry.pipelines.AnsynchroDBPipeline': 300,
}
2、新建item
新建一个PoemsItem继承scrapy的Item,定义数据字段。
class PoemsItem(scrapy.Item):title = scrapy.Field() # 标题author = scrapy.Field() # 作者dynasty = scrapy.Field() # 作者所属朝代content = scrapy.Field() # 诗歌正文
3、创建编写爬虫
切换到项目根路径ancient_poetry,使用命令scrapy genspider poems_spider www.gushiwen.org创建一个爬虫或者手动在spiders文件夹下创建爬虫,编写爬虫逻辑。
# -*- coding: utf-8 -*-
import scrapy
from ancient_poetry.items import PoemsItemclass PoemsSpiderSpider(scrapy.Spider):name = 'poems_spider'allowed_domains = ['www.gushiwen.org']start_urls = ['http://www.gushiwen.org/']url = 'https://www.gushiwen.cn/'# 每个爬虫的自定义pipeline设置custom_settings = {'ITEM_PIPELINES': {'ancient_poetry.pipelines.AncientPoetryPipeline': 500,},}def start_requests(self):yield scrapy.Request(url=self.url, method='GET', callback=self.parse)def parse(self, response):item = PoemsItem()for x in response.xpath('//div[@class="main3"]//div[@class="left"]//div[@class="cont"]'):title = x.xpath('./p//b//text()').get()item['title'] = titledynasty_author = x.xpath('.//p[@class="source"]/a/text()').getall()if len(dynasty_author) == 2:item['dynasty'] = dynasty_author[0]item['author'] = dynasty_author[1]content = x.xpath('./div[@class="contson"]/text()').extract()item['content'] = '</br>'.join(content)yield item
4、编写pipeline
在pipelines.py中新增AncientPoetryPipeline,用来处理Item传过来的数据。
class AncientPoetryPipeline(object):def __init__(self):self.fp = open('ancient_poetry.json', 'wb')self.exporter = JsonItemExporter(self.fp, ensure_ascii=False, encoding='utf-8')def open_spider(self, spider):self.exporter.start_exporting()print("爬虫开始...")def process_item(self, item, spider):self.exporter.export_item(item)return itemdef close_spider(self, spider):self.exporter.finish_exporting()self.fp.close()print('爬虫结束!')
5、运行爬虫
在spiders的同级目录下创建一个main.py文件,运行main.py执行爬虫,最终会在spiders的同级目录下生成一个ancient_poetry.json文件
"""作者: 彭三青
日期: 2020/9/9 15:32
描述: 运行爬虫程序"""
import os
import sysfrom scrapy.cmdline import executeif __name__ == '__main__':sys.path.append(os.path.dirname(os.path.abspath(__file__)))# 执行爬虫程序execute(['scrapy', 'crawl', 'poems_spider'])
6、JsonItemExporter和JsonLinesItemExporter:
保存json数据的时候可以使用这两个类,让操作变得简单。
1.JsonItemExporter:每次把数据添加到内存中,最后统一写入到磁盘,优点是存储的数据是一个满足json规则的数据,但是当数据量较大时所有的数据都存在内存中,比较耗内存。
"""
当数据量大时比较耗内存export_item以列表的形式存储到内存当中,当finish_exporting时写入到文件当中
"""
class AncientPoetryPipeline(object):def __init__(self):self.fp = open('ancient_poetry.json', 'wb')self.exporter = JsonItemExporter(self.fp, ensure_ascii=False, encoding='utf-8')def open_spider(self, spider):self.exporter.start_exporting()print("爬虫开始...")def process_item(self, item, spider):self.exporter.export_item(item)return itemdef close_spider(self, spider):self.exporter.finish_exporting()self.fp.close()print('爬虫结束!')
2.JsonLinesItemExporter:每次调用export_item的时候就把这个item存储到硬盘中,每次处理的数据直接存放到硬盘中,比较节省内存,同时数据也比较安全,但是存储的数据每个字典是一行,整个文件不满足Json格式文件
class AncientPoetryPipeline2(object):def __init__(self):self.fp = open("ancient_poetry2.json", "wb")self.exporter = JsonLinesItemExporter(self.fp, ensure_ascii=False, encoding='utf-8')def open_spider(self, spider):print("爬虫开始了!")def process_item(self, item, spider):self.exporter.export_item(item)return itemdef close_spider(self, spider):self.fp.close()print("爬虫结束了...")
3.3.4、爬虫笔记
1.reponse是一个scrapy.http.response.html.HtmlReponse对象,可以通过xpath和css语法来提取数据。通过xpath提取出来的数据是一个Selector或SelectorList对象,可以通过执行getall()、get()、extract()、extract_first()方法来获取其中的字符串。
2.getall()和extract()获取Selector中所有的文本返回的是一个列表,get()和extract_first()获取Selector中第一个文本返回的是一个str。
3.解析得到的数据可以通过yield逐条返回,或者收集所有的item最后统一使用return返回给pipeline处理。
4.一般在items.py中定义好模型,将数据存储在item中(不再使用字典)。
5.pipeline专门用来保存数据,其中有三个方法是经常使用的。
open_spider(self, spider):当爬虫被打开的时候执行。
process_item(self, item, spider):当爬虫有item传过来的时候被调用。
close_spider(self, spider):当爬虫关闭的时候会被调用。
要激活pipeline,要在settings.py中设置ITEM_PIPLINES或者爬虫里面设置custom_settings。
四、数据存储到MySQL
4.1、修改item
修改item增加table_name和table_fields
class PoemsItem(scrapy.Item):title = scrapy.Field() # 标题author = scrapy.Field() # 作者dynasty = scrapy.Field() # 作者所属朝代content = scrapy.Field() # 诗歌正文table_fields = scrapy.Field() # 字段名称table_name = scrapy.Field() # 插入表的名称
4.2、同步插入和异步插入
1、同步插入pipeline
class SynchroDBPipeline(object):"""同步插入数据库"""def __init__(self, host, db, port, user, password):self.host = hostself.db = dbself.port = portself.user = userself.password = passwordself.connect = pymysql.connect(self.host, self.user, self.password, self.db, port=self.port, charset='utf8')self.cursor = self.connect.cursor()logging.info('数据库连接成功 => %s' + '主机:', self.host + ' 端口:' + self.db)@classmethoddef from_crawler(cls, crawler):db_name = crawler.settings.get('DB_SETTINGS')db_params = db_name.get('db1')return cls(host=db_params.get('host'),db=db_params.get('db'),user=db_params.get('user'),password=db_params.get('password'),port=db_params.get('port'),)def process_item(self, item, spider):table_fields = item.get('table_fields')table_name = item.get('table_name')if table_fields is None or table_name is None:raise Exception('必须要传表名table_name和字段名table_fields,表名或者字段名不能为空')values_params = '%s, ' * (len(table_fields) - 1) + '%s'keys = ', '.join(table_fields)values = ['%s' % str(item.get(i, '')) for i in table_fields]insert_sql = 'insert into %s (%s) values (%s)' % (table_name, keys, values_params)try:self.cursor.execute(insert_sql, tuple(values))logging.info("数据插入成功 => " + '1')except Exception as e:logging.error("执行sql异常 => " + str(e))passfinally:# 要提交,不提交无法保存到数据库self.connect.commit()return itemdef close_spider(self, spider):self.connect.close()self.cursor.close()
2、异步插入pipeline
class AnsynchroDBPipeline(object):"""异步插入"""def __init__(self, db_pool):self.db_pool = db_pool@classmethoddef from_settings(cls, settings):"""建立数据库的连接:param settings::return: db_pool数据库连接池"""# 获取数据库配置参数db_name = settings.get('DB_SETTINGS')db_params = db_name.get('db1')# 连接数据池ConnectionPool,使用pymysql,连接需要添加charset='utf8',否则中文显示乱码db_pool = adbapi.ConnectionPool('pymysql', **db_params, charset='utf8')return cls(db_pool)def process_item(self, item, spider):"""使用twisted将MySQL插入变成异步执行。通过连接池执行具体的sql操作,返回一个对象:param item::param spider::return:"""self.db_pool.runInteraction(self.do_insert, item)@staticmethoddef do_insert(cursor, item):table_fields = item.get('table_fields')table_name = item.get('table_name')if table_fields is None or table_name is None:raise ParamException('必须要传表名table_name和字段名table_fields,表名或者字段名不能为空')values_params = '%s, ' * (len(table_fields) - 1) + '%s'keys = ', '.join(table_fields)values = ['%s' % str(item.get(i)) for i in table_fields]# 对数据库进行插入操作,并不需要commit,twisted会自动commitinsert_sql = 'insert into %s (%s) values (%s)' % (table_name, keys, values_params)try:cursor.execute(insert_sql, tuple(values))logging.info("数据插入成功 => " + '1')except Exception as e:logging.error("执行sql异常 => " + str(e))pass
4.3、编写ancient_spider文件
class PoemsSpiderSpider(scrapy.Spider):name = 'poems_spider'allowed_domains = ['www.gushiwen.org']start_urls = ['http://www.gushiwen.org/']url = 'https://www.gushiwen.cn/'# 每个爬虫的自定义pipeline设置custom_settings = {# 此处修改不同的pipeline进行测试'ITEM_PIPELINES': {'ancient_poetry.pipelines.AnsynchroDBPipeline': 500,},}def __init__(self):self.table_name = 'ancient_poetry'self.table_fields = ['title', 'author', 'dynasty', 'content', 'update_time']def start_requests(self):yield scrapy.Request(url=self.url, method='GET', callback=self.parse)def parse(self, response):item = PoemsItem()item['table_fields'] = self.table_fieldsitem['table_name'] = self.table_namecurrent_date = datetime.datetime.now().strftime('%F %T')item['update_time'] = current_datefor x in response.xpath('//div[@class="main3"]//div[@class="left"]//div[@class="cont"]'):title = x.xpath('./p//b//text()').extract_first()item['title'] = titledynasty_author = x.xpath('.//p[@class="source"]/a/text()').extract()if len(dynasty_author) == 2:item['dynasty'] = dynasty_author[0]item['author'] = dynasty_author[1]content = x.xpath('./div[@class="contson"]/text()').extract()item['content'] = '</br>'.join(content)yield item
4.4、创建ancient_poetry表
CREATE TABLE `ancient_poetry` (`sid` int(20) NOT NULL AUTO_INCREMENT,`title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`author` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`dynasty` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`content` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`update_time` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`sid`) USING BTREE
)
4.5运行结果
Scrapy爬取数据并存储到MySQL相关推荐
- Scrapy 爬取起点中文网存储到 MySQL 数据库(自定义 middleware)
Scrapy 爬取起点中文网 1. 创建项目 2. 创建爬虫实例 3. 创建一个启动文件 main.py 4. 项目的总体树结构 5. settings.py 6. items.py 7. qidia ...
- Scrapy爬取北京公交并保存MYSQL数据库实例
前言就不过多赘述了,大家只要把scrapy的基本了解之后就可以完成这个项目. 一:创建scrapy项目: 打开控制台输入 scrapy startproject beibus(这个是项目名称,可以自己 ...
- php 爬数据库,php爬虫爬取数据并存储至数据库
php爬虫爬取数据并存储至数据库 准备:php环境,phpspider请自行下载 下载地址:https://github.com/owner888/phpspider 文档:https://doc.p ...
- [python爬虫] Selenium爬取内容并存储至MySQL数据库
前面我通过一篇文章讲述了如何爬取CSDN的博客摘要等信息.通常,在使用Selenium爬虫爬取数据后,需要存储在TXT文本中,但是这是很难进行数据处理和数据分析的.这篇文章主要讲述通过Selenium ...
- python 接入百度地图数据包下载_Python爬虫-利用百度地图API接口爬取数据并保存至MySQL数据库...
首先,我这里有一份相关城市以及该城市的公园数量的txt文件: 分析-02.png 其次,利用百度地图API提供的接口爬取城市公园的相关信息. 所利用的API接口有两个: 1.http://api.ma ...
- mysql打印语句_大数据挖掘—(八):scrapy爬取数据保存到MySql数据库
(大数据挖掘-(七):读懂MySql数据库操作)(大数据挖掘神器--scrapy spider爬虫框架(五):解析多层网页) 通过往期的文章分享,我们了解了如何爬取想要的数据到Items中,也了解了如 ...
- 记录使用scrapy爬取新闻网站最新新闻存入MySQL数据库,每天定时爬取自动更新
爬取每天更新的新闻,使用scrapy框架,Python2.7,存入MySQL数据库,将每次的爬虫日志和爬取过程中的bug信息存为log文件下.定义bat批处理文件,添加到计划任务程序中,自动爬取. 额 ...
- python用scrapy爬虫豆瓣_python爬虫,用Scrapy爬取豆瓣Top250,存入MySQL
小白大四生,虽然是计算机专业,但是对学的几门编程语言缘分不深,然后自学了python.(这是我后来补得,因为我发现我写的太笼统了并不适合给新手看,对不起!所以希望大家轻点喷,后面我会从特别特别特别详细 ...
- python从网上获取数据失败怎么解决_求助:scrapy爬取数据失败,反复调试都不成功...
目标:爬取某一学习网站上课程信息,前期调试仅获取课程名称 爬虫文件: import scrapy from xtzx.items import XtzxItem from scrapy.http im ...
最新文章
- C++多线程实例(_beginThreadex创建多线程)
- 四种类型转换 cast
- IP 基础知识“全家桶”,45 张图一套带走!
- C/C++线程与多线程工作笔记0004---认识C中的线程和多线程
- ASP.NET MVC 5 SmartCode Scaffolding for Visual Studio.Net
- rsync工具介绍,rsync常用选项,rsync通过ssh同步
- mysql导入hdfs参数_导入数据到HDFS
- 杂记之BOOTLOAD和U-BOOT
- odi连接oracle数据库,通过ODI接口把数据从Oracle数据库抽到HIVE数据库怎么解决
- gorm中一对一,多对多关系
- Steaming SQL for Apache Kafka 学习
- NAACL 2022事件相关(事件抽取、事件关系抽取、事件预测等)论文汇总
- funcode小游戏暑假大作业,开源,新颖,游戏名:凿空,免费。
- 在线计算机手册,1. 计算机应用基础学习手册.pdf
- 维克森林大学计算机科学专业好不好,备受推崇的维克森林大学到底是什么样的?...
- 浙江理工大学英语平台Unipus自动答题
- 异步电动机的机械特性(转速/转矩-电流特性)
- CentOS系统下使用vsftpd成功搭建了FTP服务器,
- AppleWebKit/537.36(KHTML,likeGecko)与cdn讲解
- 通过对象实现圆柱体体积计算
热门文章
- Oracle创始人:拉里.埃里森
- Facebook 错误 不允许用户查看应用程序
- Thumbnails使用方法(图片处理工具类)
- NullPointerException:method 'android.content.BroadcastReceiver.onReceive' on a null object reference
- 查询MAC地址所属生产厂商
- modeller建模(单模板建模、多模板建模)
- Linux测验考试实验
- playbook模块
- 权限修饰符| 重写 和 重载之间的区别| super和this的区别|Object类|equals 和 ==的区别|多态|转型 cast的总结
- React学习-event.preventDefault()方法的简单介绍