距离上一篇博客时间过去了十几天,因学校毕业季以及考试的事情一直耽搁着。也在昨天小编成功收拾完宿舍的行李申请离校,跨出校门那一刻,挥手与舍友同学告别时,心头泛起说不清的感觉。但我知道“帷幕不会就此落下”,我们终将会有再“聚首”的时刻。在这里也预祝大家中秋国庆快乐!!! 回归正题,之前基本配置了虚拟机的环境,现在我们来认识了解scrapy框架爬虫的魅力。
操作环境: Windows10、Python3.6、Pycharm2019.3.1、谷歌浏览器、cmd、SQLyog
目标网址: http://www.99114.com/(中国网库)
相关文章: 分布式爬虫(一):配置虚拟机
分布式爬虫(二):配置安装Python以及redis
目录
一、Scrapy框架的介绍
Scrapy框架是学习爬虫道路上必不可少的一环,许多人都说:“requests是一辆汽车的零件,而Scrapy是一辆汽车”。通过比较可知晓用requests模块写爬虫需要我们一点点的组合配置起来才能“运行”,而scrapy框架已经帮我们搭建好了一辆汽车模型,我们只需要“操控、运行”它即可。
1.1、简介
Scrapy框架亦是现在大众最钟情的框架之一,它是一个开源的高级爬虫框架,用于爬取网页,提取结构性数据,并可将抓取得结构性数据较好的应用于数据分析和数据挖掘。scrapy框架有如下特点:
-
scrapy基于事件的机制,利用twisted的设计实现了非阻塞的异步操作。这相比于传统的阻塞式请求,极大的提高了CPU的使用率,以及爬取效率。(自带多线程)
-
配置简单,可以简单的通过设置一行代码实现复杂功能。
-
可拓展,插件丰富,比如分布式scrapy-redis、爬虫可视化等插件。
-
解析方便易用,scrapy封装了xpath、css等解析器,提供了更方便更高级的Selector对象构造器,可有效的处理破损的HTML代码和编码。
Scrapy框架的官方流程架构图:
- Scrapy Engine(引擎):负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,传递信号、数据等。
- Scheduler(调度器):负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。
- Downloader(下载器):负责下载Scrapy Engine(引擎)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理;
- Spider(爬虫):它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler。
- Item Pipeline(管道):它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方。
- Downloader Middlewares(下载中间件):自定义扩展引擎和下载中间的组件。
- Spider Middlewares(爬虫中间件):自定义扩展、操作引擎和爬虫中间通信的功能组件。
1.2、安装与创建
在Windows系统下,使用scrapy框架前先安装两个库:scrapy、pypiwin32,否则运行scrapy项目会报错。
pip install scrapy
pip install pypiwin32
创建scrapy项目:(注:打开终端,将路径切换到你所放置爬虫的目录下即可)
scrapy startproject [项目的名字]
创建scrapy爬虫:(注:先cd切换到刚刚创建的项目名称下)
scrapy genspider [爬虫名字] [目标网址的域名] (注意爬虫的名字和项目不可重复!)
scrapy项目创建完成效果如下:
1.3、简单配置
打开settings.py配置文件
- 加入LOG_LEVEL = “WARNING”,设置日志级别,把低于warning的日志全部屏蔽不显示。
- ROBOTSTXT_OBEY = False,将True改成False,将robots君子协议关掉。
- DEFAULT_REQUEST_HEADERS,将注释取消,打开请求头,添加User-gent等信息。
运行爬虫文件有两种方法:
- 终端cd切换到项目文件下,输入scrapy crawl spider_library 命令即可运行,注:spider_library为爬虫文件名;
- 在项目文件路径下新建run_spider.py文件,直接运行此文件即可。
# -*- coding = utf-8 -*-
from scrapy import cmdline
cmdline.execute("scrapy crawl spider_library".split())
二、中国网库
目标网址:http://www.99114.com/
中国网库为静态网站,直接请求获取即可,并不存在加密反爬措施。
2.1、分析网页
我们接下来要获取的数据在网页左侧的分类类别中:
由图片可看出需要获取的二级数据链接共有608个!
执行代码为:
contains: xpath语法中的指定一个class属性值;
import scrapy
class SpiderLibrarySpider(scrapy.Spider): # 继承Spider类
name = 'spider_library' # 爬虫的唯一标识,不能重复,启动爬虫文件的时候使用到
allowed_domains = ['99114.com'] # 限定域名,只能爬取该域名下的网页
start_urls = ['http://www.99114.com/'] # 开始爬取的链接url
def parse(self, response):
href_list = []
# 定义一级分类节点
lis = response.xpath('//div[contains(@class,"float-layer")]/div[contains(@class,"text-content")]/div[@class="content-left"]/div[@class="item-ctn"]/ul/li')
for li in lis:
# 获取二级分类详情链接
href_url = li.xpath('./a/@href').extract_first()
href_list.append(href_url)
print(len(href_list))
验证输出长度结果为608,与之前网页查看的一致,数据链接提取无误!
定义一个get_commodity_url函数,用于获取商品的详情链接,使用yield生成器将商品链接传给指定的函数进行解析。详细的yield生成器解析就不多介绍,大家可自行百度解答。
url: 请求的链接
callback: 回调函数,指定传输的函数名
class SpiderLibrarySpider(scrapy.Spider): # 继承Spider类
name = 'spider_library' # 爬虫的唯一标识,不能重复,启动爬虫文件的时候使用到
allowed_domains = ['99114.com'] # 限定域名,只能爬取该域名下的网页
start_urls = ['http://www.99114.com/'] # 开始爬取的链接url
def parse(self, response):
# 定义一级分类节点
lis = response.xpath('//div[contains(@class,"float-layer")]/div[contains(@class,"text-content")]/div[@class="content-left"]/div[@class="item-ctn"]/ul/li')
for li in lis:
# 获取二级分类详情链接
href_url = li.xpath('./a/@href').extract_first()
yield scrapy.Request(
url=href_url,callback=self.get_commodity_url
)
'''获取商品的详情链接'''
def get_commodity_url(self,response):
pass
2.2、列表页
这里以饼干商品为例解析:
每一个item属性的div标签为商品标签。
提取每个商品的详情页链接href_url,并将其传给get_detail_data函数进行数据提取;
获取下一页的链接,回调给自身函数,实现翻页效果。
'''获取商品的详情链接'''
def get_commodity_url(self,response):
divs = response.xpath('//div[@id="J_itemlistCont"]/div')
for div in divs:
# 商品的详情链接
href_url = div.xpath('.//div[@class="row row-2"]/a/@href').extract_first()
# print(href_url)
yield scrapy.Request(
url=href_url,callback=self.get_detail_data
)
# 翻页:
next_url = response.xpath('//div[@class="wraper"]//ul[@class="items"]/li[last()]/a/@href').extract_first()
if next_url:
next_url = 'http://gongying.99114.com' + next_url # 补全连接
yield scrapy.Request(
url=next_url,callback=self.get_commodity_url
)
2.3、详情页
使用红色框框括起来的字段皆为需要获取数据:
使用xpath语法提取数据以及正则re.sub替换清理数据。(获取的字段数据:标题、现价、市场价、累积成交量、发货期、发货地、库存、公司名、卖家电话、卖家名称、经营模式、主营业务)
'''获取详情数据'''
def get_detail_data(self,response):
title = response.xpath('//div[@class="proTitle textC"]/h1/text()').extract_first().strip() # 标题
# 现价
Current_price = ''.join(response.xpath('//tr[@class="tr-first"]/td[2]//text()').extract())
Current_price = re.sub(r'[\n\t\s]','',Current_price)
tr1 = response.xpath('//div[@class="dp-table2 xunpanT"]/table//tr[1]')
# 市场价
market_price = ''.join(tr1.xpath('./td[1]//text()').extract())
market_price = re.sub(r'[市场价:\n\t\s]','',market_price)
# 累积成交量
Cumulative_volume = tr1.xpath('./td[2]//span/text()').extract_first()
tr4 = response.xpath('//div[@class="dp-table2 xunpanT"]/table//tr[4]')
# 发货期
Delivery_date = ''.join(tr4.xpath('./td[1]//text()').extract())
Delivery_date = re.sub(r'[发货期:\n\t\s]','',Delivery_date)
# 发货地
Delivery_place = tr4.xpath('./td[2]/span/text()').extract_first()
Delivery_place = '' if not Delivery_place else re.sub(r'[\n\t\s]','',Delivery_place)
# 库存
stock = response.xpath('//dl[@class="clearfix interval_bean"]/dd[2]/text()').extract_first()
# 公司
company = response.xpath('//p[@class="companyname"]/span/a/text()').extract_first()
# 卖家电话、名称
seller_phone = response.xpath('//div[@class="hotline"]/em/text()').extract_first()
seller_name = response.xpath('//i[@class="bold"]/text()').extract_first()
# 经营模式
Business_model = response.xpath('//div[@class="sj-line sjDiv2"]/p[2]/span[2]/text()').extract_first()
# 主营业务
Main_business = ''.join(response.xpath('//div[@class="sj-line sjDiv2"]/p[3]/span[2]//text()').extract())
输出的部位结果展示:
三、Item Pipeline(管道)
3.1、Item
itmes.py:用来存放各种爬虫获取下来字段数据的模型。
还需要在爬虫文件spider_library.py文件中导入items.py文件中定义的类ChinaLibraryItem
from ..items import ChinaLibraryItem
item = ChinaLibraryItem(
title=title,Current_price=Current_price,market_price=market_price,Cumulative_volume=Cumulative_volume,
Delivery_date=Delivery_date,Delivery_place=Delivery_place,stock=stock,company=company,
seller_phone=seller_phone,seller_name=seller_name,Business_model=Business_model,
Main_business=Main_business
)
print(item)
yield item
3.2、Pipeline
pipelines.py:用来将items的模型存储到本地磁盘中。(持久化存储:csv、excel、mysql等等)
使用pipelines.py存储数据前,首先将settings.py配置文件里的ITEM_PIPELINES注释打开:
pipelines.py文件,存入mysql数据库。
import pymysql
class MySQLPipeline:
def __init__(self):
# 建立MySQL链接对象
self.connect = pymysql.connect(
host='localhost', # ip
port=3306, # 端口
user='root', # 用户名
password='cjs122374', # 密码
charset='utf8' # 编码格式
)
self.cursor = self.connect.cursor() # 执行命令游标
# 判断是否存在数据库,不存在则创建,并进入数据库
self.cursor.execute("drop database if exists China_spider")
self.cursor.execute("create database if not exists China_spider character set 'utf8'")
self.cursor.execute("use China_spider")
# 判断是否存在数据表,不存在则创建
self.cursor.execute("drop table if exists spider_library;")
china_sql = '''CREATE TABLE spider_library(title varchar(250),Current_price varchar(50),market_price varchar(50),
Cumulative_volume varchar(20),Delivery_date varchar(20),Delivery_place varchar(150),stock varchar(50),
company varchar(150),seller_phone varchar(11),seller_name varchar(50),Business_model varchar(150),
Main_business varchar(255));'''
self.cursor.execute(china_sql)
def process_item(self, item, spider):
try:
self.cursor.execute("insert into spider_library values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
list(dict(item).values())) # 插入代码语句
print('插入成功')
except Exception as e:
print('插入错误:', e) # 打印插入错误原因
self.connect.rollback() # 回滚事务,不插入数据
else:
self.connect.commit() # 提交事务,插入成功数据
return item
def close_spider(self, spider):
self.cursor.close() # 关闭游标
self.connect.close() # 关闭数据库链接
print('爬虫已结束')
执行爬虫文件run_spider.py;等待半小时,使用SQLyog可视化端查看获取的数据量:12386条数据。
# 查询spider_library表
SELECT * FROM spider_library;
四、项目总结
本次爬虫项目只为分布式爬虫写个基础框架代码,方便大家了解框架爬虫的运行步骤文件的搭配使用。下一篇博客将以此份代码为基础,修改为scrapy-redis分布式爬虫,部署到虚拟机上进行双系统同时进行爬取!!!
“赠人玫瑰,手有余香”,看完的小伙伴记得点赞收藏,感谢!
若有小伙伴有疑惑的地方,可在评论区留言,小编看到会尽快一一回复;此项目有需要改进的地方,也请大佬不吝赐教,感谢!
注:本项目仅用于学习用途,若用于商业用途,请自行负责!!