11.Scrapy框架基础-使用Scrapy抓取数据并保存到mongodb
前面我们都是从头开始编写爬虫,发送请求、解析网页、数据存储等每一个功能模块都需要自己实现。这一章我们学习的Scrapy是一个爬虫框架,它将上述的所有功能都封装到框架里。这样我们使用较少的代码就能完成爬虫的工作。爬虫者往往会经历一个不用框架,到使用框架,再到不用框架的过程。初学者最开始只需要一个简单的小房子,所以使用Requests和bs4很方便。在学会使用Requests和bs4后,再使用Scra
目录
前面我们都是从头开始编写爬虫,发送请求、解析网页、数据存储等每一个功能模块都需要自己实现。这一章我们学习的Scrapy是一个爬虫框架,它将上述的所有功能都封装到框架里。这样我们使用较少的代码就能完成爬虫的工作。
爬虫者往往会经历一个不用框架,到使用框架,再到不用框架的过程。初学者最开始只需要一个简单的小房子,所以使用Requests和bs4很方便。在学会使用Requests和bs4后,再使用Scrapy框架,你会发现一个新大路,原来只需要几行代码就可以完成爬虫,发现Scrapy很好用。但是渐渐地,你需要一些定制化地功能,Scrapy的条条框框并不能满足地功能,所以你可能还是会回到最开始的方法,但是此时你已经可以自己建一座华丽的大房子了。(《Python网络爬虫从入门到实践》第7章)
Scrapy 是一个快速的高级网页抓取框架,用于抓取网站并从其页面中提取结构化数据。它可用于广泛的用途,从数据挖掘到监控和自动化测试。
Scrapy主要的组件有引擎、调度器、下载器、爬虫、管道、爬虫中间件和下载器中间件。
如下图绿色箭头代表着数据流,各个模块的功能可简要概括如下:
- 引擎:控制数据流在所有组件中的流动,开发者一般不需要修改
- 调度器:从引擎接受请求并将他们加入爬虫队列,开发者一般不需要修改
- 下载器:负责获取网页并提供给引擎,类似request获取网页,开发者一般不需要修改
- 爬虫:负责解析网页,提取数据,类似解析网页,根据爬取的对象修改代码
- 管道:负责存储数据,根据数据存储方式修改代码
一、Scrapy安装
1.mac系统
pip install Scrapy
2.windows系统
Scrapy也支持Python3了,在Windows下安装也很简单。但是强烈建议新创建一个scrapy_project的新工程,然后在这个工程的虚拟环境中安装scrapy,这是因为scrapy需要安装大量的第三方包文件。
pip install Scrapy
二、使用scrapy爬取数据
爬取某网的财经新闻数据,包含标题、链接、发布时间
1.新建一个scrapy工程
在pycharm的terminal中输入以下命令,这将为你创建一个新的Scrapy项目
scrapy startproject eastmoney_news_spider
最后一段字符串是你为这个Scrapy项目指定的名称,你可以根据自己的需要设定项目名称。
执行后,将会为你创建一个以项目名称命名的文件夹,该文件夹下的目录见下图。
- scrapy.cfg: 项目的配置文件。
- eastmoney_news_spider/: 该项目的python模块。之后您将在此加入代码。
- eastmoney_news_spide/items.py: 项目中的item文件,用来定义爬虫提取的数据标签。
- eastmoney_news_spide/middlewares.py: 项目中的中间件文件,用来设置请求头、cookies、代理ip池等。
- eastmoney_news_spide/pipelines.py: 项目中的pipelines文件,用来对提取到的数据进行处理或者保存。
- eastmoney_news_spide/settings.py: 项目的设置文件。
- eastmoney_news_spide/spiders/: 放置spider代码的目录,一个项目可能会有多个爬虫,这里的爬虫代码指的是负责从网页中提取数据的部分。
2.在spiders下新建一个爬虫文件
在终端中使用cd将路径切换到spiders下
cd spiders
执行以下命令,创建一个spider文件
scrapy genspider news finance.eastmoney.com
news是爬虫的名称
finance.eastmoney.com是待爬取网站的域名
执行此段命令后,在Pycharm工程文件的目录中就可以看到多出来了一个news.py文件。打开后,我们可以看到以下初始内容
class NewsSpider(scrapy.Spider):
name = 'news'
allowed_domains = ['finance.eastmoney.com']
start_urls = ['http://finance.eastmoney.com']
def parse(self, response):
3.提取网页数据
- 获取网页源代码
response.text
class NewsSpider(scrapy.Spider):
name = 'news'
allowed_domains = ['finance.eastmoney.com'] # 此地址为网站的域名
start_urls = ['http://finance.eastmoney.com/a/ccjdd.html'] # 修改为待爬取的网页网址
def parse(self, response):
print(response.text)
在终端中输入命令运行爬虫
scrapy crawl news(第三个参数是spider文件中name的值)
- 提取数据
使用lxml的xpath提取页面新闻的title、url、date数据。由于该网页新闻列表中有的楼层没有插入图片,导致不同楼层结构不一致,因此,设置了异常处理来解决这个问题。
使用scrapy中的xpath提取数据
在scrapy中可以直接使用xpath解析,但与lxml中使用XPath不同的是需要在XPath语句后面使用.extract()方法。
- 实现数据流
一个Scrapy工程可以有多个爬虫,在items文件中可以针对不同的爬虫定义不同的抓取内容item。
这里定义一个NewsSpiderItem类,并继承的是scrapy.Item类。这个类就是在spider中将要采用fieldname = scrapy.Field()格式进行定义,fieldname是自定的字段名称。
在spider中将这个类实例化。
from eastmoney_news_spider.items import NewsSpiderItem # 载入定义好的NewsSpiderItem
item = NewsSpiderItem() # 实例化这个类
接下了我们就可以向使用字典一样,使用item
item['title'] = 获取到的标题
item['url'] = 获取到的网址
item['date'] = 获取到的时间
完善后的代码:
import scrapy
from eastmoney_news_spider.items import NewsSpiderItem
class NewsSpider(scrapy.Spider):
name = 'news'
allowed_domains = ['finance.eastmoney.com/a/ccjdd.html']
start_urls = ['http://finance.eastmoney.com/a/ccjdd.html']
def parse(self, response):
for content in response.xpath('//ul[@id = "newsListContent"]/li'):
item = NewsSpiderItem()
try:
item['title'] = content.xpath('./div[2]/p[1]/a/text()').extract()[0].strip()
item['url'] = content.xpath('./div[2]/p[1]/a/@href').extract()[0]
item['date'] = content.xpath('./div[2]/p[3]/text()').extract()[0].strip()
except:
item['title'] = content.xpath('./div[1]/p[1]/a/text()').extract()[0].strip()
item['url'] = content.xpath('./div[1]/p[1]/a/@href').extract()[0]
item['date'] = content.xpath('./div[1]/p[3]/text()').extract()[0].strip()
yield item
注意,最后通过 yield 来发起一个请求,并通过 callback 参数为这个请求添加回调函数,在请求完成之后会将响应作为参数传递给回调函数。
scrapy框架会根据 yield 返回的实例类型来执行不同的操作,如果是 scrapy.Request 对象,scrapy框架会去获得该对象指向的链接并在请求完成后调用该对象的回调函数。
如果是 scrapy.Item 对象,scrapy框架会将这个对象传递给 pipelines.py做进一步处理。这里我们yield item调用的就是scrapy.Item对象,接下来,我们在pipeline中将数据保存到mongodb中。
三、保存数据到mongodb
这里提供三种方案:
方案一:在pipeline设置数据库所有参数,单个数据逐个插入数据库
import pymongo
class NewsSpiderPipeline:
def process_item(self, item, spider):
host = 'localhost'
port = 27017
db_name = 'spider'
client = pymongo.MongoClient(host=host,port=port)
db = client[db_name]
collection = db['eastmoney_news']
collection.insert_one(dict(item))
return item
方案二:在pipeline中引用settings,单个数据逐个插入数据库
- 在settings中对mongodb的参数进行设置
MONGODB_HOST = 'localhost'
MONGODB_PORT = 27017
MONGODB_NAME = 'spider'
MONGODB_DOCNAME = 'eastmoney_news'
在pipeline中引用settings
class NewsSpiderPipeline:
def process_item(self, item, spider):
host = spider.settings['MONGODB_HOST']
port = spider.settings['MONGODB_PORT']
db_name = spider.settings['MONGODB_NAME']
client = pymongo.MongoClient(host=host,port=port)
db = client[db_name]
collection = db[spider.settings['MONGODB_DOCNAME']]
collection.insert_one(dict(item))
return item
在pipeline中引用settings使用spider.settings,引用的方法和使用字典一样
在spider中引用settings使用self.settings,引用的方法和使用字典一样
方案三: 在pipeline中引用settings,在爬虫结束时,一次性批量插入数据库
我们知道mongodb应该少读少些,所以我们这里的策略是在spider开启时启动数据库;在每次回调时,将数据追加到一个列表中暂存起来;最后在spider关闭时,将数据一次性保存到数据库中,并关闭数据库。刚好pipleline中的组件完全支持这些需求。
class NewsSpiderPipeline:
def open_spider(self, spider):
host = spider.settings['MONGODB_HOST']
port = spider.settings['MONGODB_PORT']
db_name = spider.settings['MONGODB_NAME']
self.client = pymongo.MongoClient(host=host, port=port)
self.db = self.client[db_name]
self.collection = self.db[spider.settings['MONGODB_DOCNAME']]
self.item_list = []
def process_item(self, item ,spider):
self.item_list.append(dict(item))
return item
def close_spider(self,spider):
self.collection.insert_many(self.item_list)
print('{}条数据已存入数据库'.format(len(self.item_list)))
self.client.close()
print('数据库已关闭')
- 在setting中开启pipeline
以上所有pipeline操作的实现都基于在settings中开启了pipeline 。要想开启pipeline 非常简单,只需要在settings的以下位置取消掉注释即可。
四、再多学一点
1.添加请求头
在settings文件中设置默认的请求头,此时发送请求时会默认携带此请求头。
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'
# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'User-Agent': USER_AGENT
}
2.Robot.txt设置
默认为True,即默认遵守robot规则。根据实际需要可将此修改为False
# Obey robots.txt rules
ROBOTSTXT_OBEY = True
3.爬取多个页面
在之前的例子中,我们只是爬取了一页的20篇新闻,如果要爬取后面的多个页面,我们该如何做呢?
- 获取待爬取页面的链接
分析链接地址后发现页面所在的页数包含在链接地址中,即ccjdd_2代表第2页,ccjdd_3代表第3页。我们可以按此规律批量生成想要爬取的页面地址。
如下图红框中的第一行代码。
- 循环爬取每一页
yield在第二部分的最后已经介绍过,yield scrapy.Request会生成一个scrapy.Request的实例,将url作为参数发送请求,并获得一个response,然后将response作为参数传递给回调函数parse,实际上这里调用的是parse本身,作用是对完成每一页的爬取。
五、作业(这是一个考验)
目标网站:站长之家 https://top.chinaz.com/
背景:站长之家是一家专门针对中文站点提供资讯、技术、资源、服务的网站,网站现有上百万用户。拥有最专业的行业资讯频道、国内最大的建站源码下载中心、站长聚集的站长社区、最大的建站素材库、最实用的站长工具,以及最大的中文网站流量统计分析系统。(摘自"百度词条")
需求:在站长之家的网站排行板块中,提供了行业排名、地区排名等多种分类网站排行数据。现在请你任选一种感兴趣的排名方式,摘取其中前10页的数据。
字段要求:一共7个字段,分别是网站名称web_name、网站域名domain、百度权重:baidu_weight、PR:PR、排名:rank、得分:score、网站简介:abstract。
技术要求:使用scrapy编写爬虫,最终将提取到的数据一次性储存到mongodb中;在mongodb中使用数据库查询命令查询PR值大于等于4分的所有数据并按从大到小排序。
需要提交的资料:整个scrapy项目文件、mongodb查询结果在text mode下的截图。
更多推荐
所有评论(0)