Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。
(一)scrapy整体架构图
(二)Scrapy主要组件
- 1、引擎(Scrapy): 用来处理整个系统的数据流处理, 触发事务(框架核心)。
- 2、调度器(Scheduler): 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址。
- 3、下载器(Downloader): 用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)。
- 4、爬虫(Spiders): 爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面。
- 5、项目管道(Pipeline): 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
- 6、下载器中间件(Downloader Middlewares): 位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
- 7、爬虫中间件(Spider Middlewares): 介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
- 8、调度中间件(Scheduler Middewares): 介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。
scrapy startproject yaowang #创建项目
下面来简单介绍一下各个主要文件的作用:
scrapy.cfg:项目的配置文件
yaowang/:项目的Python模块,将会从这里引用代码
yaowang/items.py:项目的字段定义文件
yaowang/pipelines.py:项目的管道文件
yaowang/settings.py:项目的设置文件
yaowang/spiders/:存储爬虫代码目录
(三)定义存储对象(yaowangl/items.py)
Items是装载我们抓取数据的容器。它们工作像简单的Python字典,它提供更多的保护,比如对未定义的字段提供填充功能防止出错.
Itmes.py
(四)制作爬虫 (yaowang/spiders/)
1、在当前目录下输入命令,将在yaowang/spider目录下创建一个名为的爬虫,并指定爬取域的范围:
scrapy genspider yao 111.com.cn #创建爬虫
2、打开 tutorial/spider目录里的 hsw.py,默认增加了下列代码:
yao.py
# -*- coding: utf-8 -*-
import scrapy
from lxml import etree
from yaowang.items import YaowangItem
class YaoSpider(scrapy.Spider):
name = 'yao'
allowed_domains = ['111.com.cn']
start_urls = []
for i in range(1,51):
url = f'https://www.111.com.cn/categories/953710-j{i}.html'
start_urls.append(url)
def parse(self, response):
# with open('yaowang.html','w',encoding='utf-8')as fp:
# fp.write(response.body.decode('gbk'))
# 提取HTML页面
tree = etree.HTML(response.body.decode('gbk'))
# 所有药品的盒子
li_list = tree.xpath('//ul[@id="itemSearchList"]/li')
# print(len(li_list))
for li in li_list:
# 实例化item
item = YaowangItem()
# 药名
name = li.xpath('./div[1]/p[@class="titleBox"]/a/text()')
name = [i.strip() for i in name]
name = ''.join(name).strip()
# print(name)
# 图片链接
href = li.xpath('./div[1]/a//img/@src')[0]
# print(href)
# 价格
price = li.xpath('./div[1]/p[@class="price"]//span/text()')[0]
price = price.strip()
# print(price)
# 药店
store = li.xpath('./div[1]/div[@class="sell_type_div"]/span[2]//text()')[0]
# print(store)
# 详情url
url = li.xpath('./div[1]/a/@href')[0]
full_url = "https:" + url
# print(full_url)
item['name'] = name
item['href'] = href
item['price'] = price
item['store'] = store
item['url'] = full_url
# 默认过滤相同的url请求
yield scrapy.Request(url=full_url,callback=self.parse_detail,meta={'data':item})
# yield item
# 二级页面,详情页数据提取
def parse_detail(self,response):
# 继承上一页页面的item数据
item = response.meta['data']
# with open('yaowang_detail.html','w',encoding='utf-8')as fp:
# fp.write(response.body.decode('gbk'))
tree = etree.HTML(response.body.decode('gbk'))
# 商品详情信息
# div = tree.xpath('//div[@class="goods_intro"]/table')
# 商品名称
shop_name = tree.xpath('//div[@class="goods_intro"]//tr[1]/td/text()')[0]
print("========",shop_name)
# 品牌
brand = tree.xpath('//div[@class="goods_intro"]//tr[2]/td[1]/text()')[0]
# print("========", brand)
# 规格
standard = tree.xpath('//div[@class="goods_intro"]//tr[2]/td[2]/text()')[0]
# print("========", standard)
# 重量
weight = tree.xpath('//div[@class="goods_intro"]//tr[3]/td[1]/text()')[0]
# print("========", weight)
# 生产厂商
produce = tree.xpath('//div[@class="goods_intro"]//tr[3]/td[2]/text()')[0]
# print("========", produce)
# 批准文号
approval_num = tree.xpath('//div[@class="goods_intro"]//tr[4]/td[1]//text()')
approval_num = [i.strip() for i in approval_num]
approval_num = ''.join(approval_num)
# print("========", approval_num)
# 产品类型
produce_type = tree.xpath('//div[@class="goods_intro"]//tr[4]/td[2]/text()')[0]
produce_type = produce_type.strip()
# print("========", produce_type)
# item存储数据
item['shop_name'] = shop_name
item['brand'] = brand
item['standard'] = standard
item['weight'] = weight
item['produce'] = produce
item['approval_num'] = approval_num
item['produce_type'] = produce_type
yield item
3、要建立一个Spider, 你必须用scrapy.Spider类创建一个子类,并确定了三个强制的属性 和 一个方法。
- ① name ="":这个爬虫的识别名称,必须是唯一的,在不同的爬虫必须定义不同的名字。
- ②allow_domains=[]是搜索的域名范围,也就是爬虫的约束区域,规定爬虫只爬取这个域名下的网页,不存在的URL会被忽略。
- ③ start_urls=():爬取的URL元祖/列表。爬虫从这里开始抓取数据,所以,第一次下载的数据将会从这些urls开始。其他子URL将会从这些起始URL中继承性生成。
- ④ parse(self,response):解析的方法,每个初始URL完成下载后将被调用,调用的时候传入从每一个URL传回的Response对象来作为唯一参数,主要作用如下:
负责解析返回的网页数据(response.body),提取结构化数据(生成item) b) 生成需要下一页的URL请求。
4、修改parse方法,添加parse_item方法
现在我们修改yao.py文件将start_urls的值修改为需要爬取的初始url
start_urls = []
for i in range(1,51):
url = f'https://www.111.com.cn/categories/953710-j{i}.html'
start_urls.append(url)
这里不使用response自带的xpath,使用lxml xpath
from lxml import etree
5、然后运行一下看看,在yaowang目录下执行:
scrapy crawl yao -o yao.csv
scrapy保存信息的最简单的方法主要有四种,-o 输出指定格式的文件,命令如下:
json格式,默认为Unicode编码
scrapy crawl yao -o yao.json
json lines格式,默认为Unicode编码
scrapy crawl yao -o yao.jsonl
csv 逗号表达式,可用Excel打开
scrapy crawl yao -o yao.csv
xml格式
scrapy crawl yao -o yao.xml
输出日志到指定文件,但是日志就不会输出到屏幕上了
scrapy crawl yao -s LOG_FILE=yao.log
6、利用pipelines.py:项目的管道文件进行数据的保存
mysql保存::
class YaowangPipeline(object):
def __init__(self):
# 建立数据库连接
self.con = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='q1q1q1',
database='aabbcc',
charset='utf8')
# 创建操作游标
self.cursor = self.con.cursor()
def process_item(self, item, spider):
print("="*30)
# data_list.append(dict(item))
# with open('yaowang.json','w',encoding='utf-8')as fp:
# json.dump(data_list,fp,ensure_ascii=False)
name = item['name']
href = item['href']
price = item['price']
store = item['store']
url = item['url']
shop_name = item['shop_name']
brand = item['brand']
standard = item['standard']
weight = item['weight']
produce = item['produce']
approval_num = item['approval_num']
produce_type = item['produce_type']
# 定义sql语句
# sql = "INSERT INTO yaowang_er(name,href,price,store,url,shop_name,brand,standard,weight,produce,approval_num,produce_type) values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
#
# # 执行sql语句
# self.cursor.execute(sql,(name,href,price,store,url,shop_name,brand,standard,weight,produce,approval_num,produce_type))
# # 保存修改
# self.con.commit()
return item
def __del__(self):
# 关闭操作游标
self.cursor.close()
# 关闭数据库连接
self.con.close()
MongoDB保存:
class YaowangMongoPipeline(object):
def __init__(self):
# 创建客户端
self.client = pymongo.MongoClient('localhost')
# 创建数据库
self.db = self.client['yaowang']
# 创建集合
self.collection = self.db['yao_info']
def process_item(self, item, spider):
print("="*30)
self.collection.insert(dict(item))
return item
然后settings.py 中使用管道文件配置,进行数据保存
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
# 'taoche.pipelines.TaochePipeline': 300,
'taoche.pipelines.TaocheMongoPipeline': 300,
}
运行爬虫:
scrapy crawl yao