Scrapy框架架构
框架介绍
写一个爬虫,我们需要发送网络请求、数据分析、数据存储、反反爬虫机制(更换ip代理,设置请求头等),异步请求等。scrapy把这些基础的东西都已经写好了,可以提高爬虫的爬取效率与开发效率。
Scrapy架构图
- 1.spider(爬虫)给scheduler(调度器)发送请求
- 2.scheduler接收请求,将其存储在自己的队列中
- 3.引擎(engine)不断地向scheduler提取请求
- 4.engine将请求给downloader(下载器),下载器到互联网上下载资源
- 5.engine接收到downloader返回的响应
- 6.engine将响应返回给spider
- 7.spider进行数据的分析与提取,将提取后的数据送给engine
- 8.engine将提取后的数据送给pipeline,指定数据存储方式
模块功能
- Spider Engine(引擎):Scrapy的核心部分,负责在其他模块中间通信
- Spider(爬虫):发生需要爬取的链接给引擎,最后引擎把其他模块响应的数据返回给爬虫,由爬虫对数据进行分析提取,由开发者自行决定
- Scheduler(调度器):负责接收引擎发送过来的请求,并按照一定方式排列整理,负责调度请求的顺序
- Downloader(下载器):负责接收引擎传过来的下载请求,然后去下载对应的数据再交还给引擎
- Item Pipeline(管道):负责存储爬虫传递的数据,由开发者决定数据存储位置
- Downloader Middlewares(下载中间件):可以扩展下载器和引擎之间通信功能的中间件
- Spider Middlewares(Spider中间件):可以扩展爬虫和引擎之间通信功能的中间件
安装scrapy
- 在windows下安装scrapy,首先要保证已经安装python环境
- 安装anaconda
- 在开始的地方找到Anaconda Prompt运行,输入以下指令:
conda install scrapy #安装 scrapy #检测是否安装成功
创建scrapy项目
先自己新建一个文件夹用来放scrapy项目,打开anaconda环境,即上面提及的Anaconda Prompt,使用命令行进入该文件夹。
项目结构
使用Pycharm打开项目文件夹
创建爬虫
在Anaconda Prompt下进入刚刚创建的目录下,新建爬虫
scrapy genspider 爬虫名 "爬取的域名"
scrapy genspider xixi "cr137.com"
上述命令创建成功后会在init.py下多一个xixi.py,该文件是爬虫的main文件
设置settings文件
设置请求头,伪装身份,必要时设置DOWNLOAD_DELAY。如果要存储数据要将此文件中pipelines的注释去掉
init.py文件
打开新建的xixi.py文件,parse()函数是用于接收scrapy爬虫架构第6步返回的响应
运行爬虫
在Anaconda Prompt中执行以下命令即可执行爬虫
scrapy crawl 爬虫名
查看响应类型可采用的方法
查看响应的类型,xixi.py:
def parse(self,response):
print('='*40)
print(type(response))
print('='*40)
在xixi.py里加入以下语句:
from scrapy.http.response.html import HtmlResponse
查看方法的方式是鼠标选中要查看的类,按下ctrl+b(或长按ctrl用鼠标点击类名),在上述例子中我们想查看HtmlResponse的方法,鼠标选中HtmlResponse,按下ctrl+b,得到下图
由上图可知HtmlResponse继承自TextResponse类,继续查看TextResponse类的方法
快捷执行方式
在爬虫项目下新建一个python文件start.py,写入以下代码:
from scrapy import cmdline
cmdline.execute("scrapy crawl xixi".split())
执行此文件即可开始爬虫,就不需要再打开Anaconda Prompt
CrawlSpider
CrawlSpider继承自Spider,只不过比Spider多了可以定义爬取的url的规则,以后scrapy碰到满足条件的url都进行爬取,而不用手动的yield Request.
创建CrawlSpider爬虫
通过以下命令在Anaconda Prompt里创建CrawlSpider:
scrapy genspider -t crawl [爬虫名] [域名]
LinkExtractors链接提取器
可以在所有爬的页面中找到满足规则的url,实现自动的爬取。
class scrapy.linkextractors.LinkExtractor{
allow = (),
deny = (),
allow_domains = (),
deny_domains = (),
restrict_xpaths = (),
tags = ('a','area'),
attrs = ('href'),
canonicalize = True,
unique = True,
process_value = None
}
主要参数:
- allow:允许的url,所有满足这个正则表达式的url都会被提取。
- deny:禁止的url,所有满足这个正则表达式的url都不会被提取
- allow_domain:允许的域名,只有在这个里面指定的域名的url才会被提取
- deny_domain:禁止的域名,所有在这个里面指定的域名的url都不会被提取
- restrict_xpaths:严格的xpaths,和allow共同的过滤链接
Rule规则类
定义爬虫的规则类:
class scrapy.spider.Rule{
link_extractors,
callback = None,
cb_kwargs = None,
follow = None,
process_links = None,
process_request = None
}
主要参数:
- link_extractor:一个LinkExtractor对象,用于定义爬取规则。
- callback:满足这个规则的Url,应该执行哪个回调函数。因为CrawlSpider使用了parse作为回调函数,因此不要覆盖parse作为回调函数自己的回调函数
- follow:指定根据该规则从response提取的链接是否需要跟进
- process_links:从link_extractor中获取到链接后会传递给这个函数,用来过滤不需要爬取的链接。
CrawlSpider实战
创建CrawlSpider项目,在项目下创建爬虫文件wxapp.py,打开微信小程序社区的教程部分,找规律
点击不同的页数的页面,观察url变化
发现只有page参数变化点击不同的文章进行查看
配置准备
设置setting文件,新建文件start.py执行爬虫,运行即可
页面爬取
在wxapp.py文件里修改rules,对爬取信息进行筛选,如上面的url爬取。
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class WxSpider(CrawlSpider):
name = 'wx'
allowed_domains = ['wxapp-union.com']
start_urls = ['http://www.wxapp-union.com/portal.php?mod=list&catid=2&page=1']
#翻页
rules = (
Rule(LinkExtractor(allow=r'.+mod=list&catid=2&page=\d'),
follow=True), #此处不需要指定回调,自动爬取即可
#进一步解析相同域名,即进一步爬取每篇文章的详细内容
)
#点击查看具体页面
Rule(LinkExtractor(allow=r'.+article-.+\.html'),
callback="parse_item",follow=False) #不需要跟进,不用再继续解析
def parse_item(self, response):
title = response.xpath("//h1[@class='ph']/text()").get()
print(title)
提取作者时间
wx.py:
def parse_item(self, response):
author_p = response.xpath(".//p[@class='authors']") #返回响应这个对象,类型为object
author = author_p.xpath(".//a/text()").get() #get()返回响应内容,类型为字符串 .//表示在当前环境
time = author_p.xpath(".//span/text()").get()
print("author:%s / time:%s" % (author,time))
print("="*40)
提取前言部分
wx.py
def parse_item(self, response):
text_p = response.xpath("//div[@class='blockquote']")
text = text_p.xpath(".//p/text()").get()
print(text)
print("="*40)
存储以上提取的所有信息
items.py
#用来描述自定义数据包含哪些字段信息
class WxappItem(scrapy.Item):
title = scrapy.Field()
author = scrapy.Field()
time = scrapy.Field()
text = scrapy.Field()
pipeline.py
from scrapy.exporters import JsonLinesItemExporter #JsonLinesExporter将数据直接存到硬盘中,而不需要存在内存中
class WxappPipeline(object):
def __init__(self):
self.fp = open("wx.json",'wb')
self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding='utf-8')
def process_item(self, item, spider):
self.exporter.export_item(item)
return item
#关闭爬虫
def close_spider(self,spider):
self.fp.close()
wx.py
def parse_item(self, response):
title = response.xpath("//h1[@class='ph']/text()").get()
author_p = response.xpath(".//p[@class='authors']") # 返回响应这个对象,类型为object
author = author_p.xpath(".//a/text()").get() # get()返回响应内容,类型为字符串
time = author_p.xpath(".//span/text()").get()
text_p = response.xpath("//div[@class='blockquote']")
text = text_p.xpath(".//p/text()").get()
item = WxappItem(title=title,author=author,time=time,text=text)
return item
在同等文件夹下创建新文件
os_path.py
import os
path = os.path.join(os.path.dirname(os.path.dirname(__file__)),'文件夹名') #在该文件的相同文件夹下新建一个文件 os.path.dirname表示上一级目录
if not os.path.exists(path):
os.mkdir(path)
else:
print("文件夹存在")