根据b站爬虫教程整理
一 架构与流程
1.1 架构
模块:
- Engine:Scrapy框架的核心;负责在各部件之间通信、传递数据;
- Spider:由用户自定义,发送目标URL、解析目标网页;请求的增强由Middleware实现;
- Scheduler:由用户自定义,接收引擎传递的请求后决定请求顺序、处理重复URL;
- Download:下载网页、数据;Download功能增强在Middleware中实现;
- Pipeline:用户自定义,保存目标数据到数据库或文件;
1.2 流程
- Spider创建Request对象,发送给引擎;
- 引擎将Request对象转发到调度器,调度器保存Request对象到队列中;
- 引擎从调度器队列中获取Request对象;
- 引擎将Request对象发送给下载器;
- 下载器发送请求、接收响应、创建Reponse对象,发送给引擎;
- 引擎将Reponse对象转发给Spider;
- Spider解析Response对象,获取数据,发送给引擎;
- 引擎将数据转发给Pipelines;Pipeline将数据保存到数据库或文件;
各模块之间的中间件Middleware可以对传输的对象或数据进行加工处理。
引擎的作用在于:各模块不用考虑相互的功能,直接用return返回结果、直接把其他模块结果作为参数使用,由引擎实现接收和转发。
二 安装与创建scrapy项目
2.1 安装scrapy框架
安装scrapy库:
pip install scrapy
安装pypiwin32库:
scrapy异步操作依赖于twisted库;twisted库在win平台上运行时需要win32支持
pip install pypiwin32
注意:
python第三方库默认安装目录为:\python\Scripts路径,需加入环境变量Path。
2.2 创建、启动scrapy项目
- pycharm创建一个纯python项目;
- 命令行中创建sracpy项目:
scrapy startproject [项目名]
- 命令行创建spider文件:
scrapy genspider [文件名] [爬虫目标域名]
需要先cd到 项目/spiders 目录
- 命令行/pycharm 启动scrapy项目:
法一:命令行启动
scrapy crawl [爬虫名字]
需要先cd到spider目录
法二:通过py文件调用命令行,直接在pycharm中启动
1)创建start.py
from scrapy import cmdline
cmdline.execute(["scrapy","crawl","[爬虫名]"])
2)运行start.py
注意:
1.scrapy项目、scrapy目录,必须通过命令行创建;
2.scrapy项目,必须通过命令行启动;
2.3 项目结构
- scrapy.cfg:项目配置文件;
- spider:爬虫代码目录;
- iterm.py:存储爬取数据的模型;
- middlewares.py:用于定义下载器中间件、爬虫中间件;
- pipelines:存储爬取的数据;
- settings:爬虫下载设置,请求头、cookie…
2.4 配置爬虫
settings配置爬虫
ROBOTSTXT_OBEY = False # 将值设置为False
DEFAULT_REQUEST_HEADERS={'User-Agent':'...',...} # 默认请求头
DOWNLOAD_DELAY # 爬虫延迟
三 模块实现
以爬取糗事百科为例:
3.1 spider 数据解析
3.1.1 创建
自定义爬虫类,继承scrapy.Spider类,必须实现以下方法和属性:
属性:
- name:爬虫的名字;
- allowed_domains:待爬取目标的域名;
- start_urls:初始URL;
方法:
- parse:以爬虫返回的Response为参数,进行解析;
3.1.2 调用
parse方法是spider类的核心,负责发送请求、解析数据、传输数据。parse方法由scrapy框架自动调用。
- 发送请求:创建Request对象,通过yield将Request对象发送给引擎;
- 解析数据:接收Response对象,使用HtmlReposne对象的xpath、css方法进行解析;
- 传输数据:返回解析的数据,由scrapy框架自动接收并传输到pipelines;
parse返回请求、数据的操作有两种:
- return语句:直接返回数据;
- yield语句:返回迭代器;【推荐】
另外,parse可以以dict方式返回数据,也可以封装为item对象返回。推荐的是后一种。
3.1.3 解析数据
Response为HtmlResponse对象,可执行xpath、css方法提取数据,提取结果为Selector、SelectorList类型,进一步通过get、getall提取处字符串
属性:
- body
- text(解码后的)
方法:
- xpath:使用xpath规则解析;
- css:使用css选择器解析,
- 不支持直接使用bs4进行解析,只能先获取返回数据再bs4解析
xpath方法:
返回值为selector或selectorList;当只存在一个符合xpath规则的结果时返回selector,有多个时返回selectorList;
selector对象:
- 继承自List类,可遍历;
- 有xpath、css、re、extract方法,可继续解析;
- 有get、getall方法,可获取HTML标签中的文本信息;get方法获取第一个标签下的文本返回为string,getall方法获取所有标签下的文本返回为list;
scrapy包的xpath方法和lxml包的xpath方法语法一致吗?API有差异
- lxml中解析后用text属性可得标签元素内的文本,response对象通过get获取;而且lxml中获取的不包含标签本身,而get获取的包含。
3.2 item 数据封装
spider将解析后的数据封装为item后传给引擎,引擎进一步传给pipelines,然后进行存储;
item为自定义类,继承自scrapy.Item,用于封装数据。
#定义Item类
class MyItem(scrapy.Item):
author=scrapy.Field()
content=scrapy.Field()
#Item类属性赋值
item=MyItem(author='abc')
item['content']='123'
注意:
- Item对象属性赋值时可以像字典一样使用;
- Item类比字典的好处在于结构更固定;
- Item类不能出现复杂嵌套,也就是说item相当于一个字典,而且每个字段值都只能是简单类型,不能是对象;
3.3 pipelines 数据存储
3.3.1 创建、调用
- 自定义pipelines类,直接继承自object对象;
- setting中通过ITEM_PIPELINES指定pipelines类,scrapy框架自动创建pipelines对象并调用方法。【类似与spring中IOC】
3.3.2 方法
pipelines中可定义以下三种方法,条件触发时,scrapy框架自动调用:
- open_spider:爬虫启动时调用;可用于打开文件(也可以在pipelines的构造函数中打开);
- process_item(self,item,spider):spider将item传给pipelines时调用;用于接收和写入数据;【json.dumps用于将dict转为json】
- close_spider:爬虫结束时调用;可用于关闭文件。
class MyPipeline(object):
def __init__(self):
self.fp=open("abc.txt",'w',encoding='utf-8')
def open_spider(self,spider):
print('爬虫开始了...')
def process_item(self,item,spider):
item_json=json.dumps(dict(item),ensure_ascii=False)
self.fp.write(item_json+'\n')
return item
def close_spider(self,spider):
self.fp.close()
print('爬虫结束了...')
注意:
从下载image、file来看,pipelines是先对item处理,然后才存储数据。普通的item直接通过pipeline中的方法即可写入文件,而ImagesPipeline、FilesPipeline会先根据item中的image_urls、file_urls字段下载文件。
3.3.3 存储为json文件
游戏脚本中的python_序列化、反序列化、json配置文件
Scrapy包提供JsonItemExporter、JsonLinesItemExporter方法用于将item对象序列化为json文件。不需要先转为dict,再用json.dump()。
注意:
只有存为item才能使用这两个API,而存为item就要求结构中不存在嵌套的,所以其实和json差不多
打算把数据存入mongodb,数据有一层嵌套,目前用自定义对象+json.dump实现;
报错:ERROR: Spider must return Request, BaseItem, dict or None,
JsonItemExporter、JsonLinesItemExporter:
- JsonItemExporter、JsonLinesItemExporter均以二进制方式写入数据,因此文件要以二进制方式(‘wb’)打开;
- JsonItemExporter:每次将item数据添加到内存中,最后同一写入磁盘;整个文件为一整个Json格式的List,每个item作为List中一项;
- JsonLinesItemExporter:每次将item数据作为一行写入磁盘;整个文件像多个Json文件合并,整体不符合Json格式,每个item符合Json格式
from scrapy.exporters import JsonItemExporter
fout=open('test.json','wb')
exporter=JsonItemExporter(fout,ensure_ascii=False,encoding='utf-8')
exporter.start_exporting()
exporter.export_item(item)
exporter.finish_exporting()
fout.close()
from scrapy.exporters import JsonLinesItemExporter
fout=open('test.json','wb')
exporter=JsonLinesItemExporter(fout,ensure_ascii=False,encoding='utf-8')
exporter.export_item(item)
fout.close()
补充:
yield:[原理见python 生成器]
https://www.liaoxuefeng.com/wiki/897692888725344/923029685138624
yeild功能上就是向引擎发送数据(对象)
引擎根据收到的数据的类型执行对应的操作
如果是scrapy.Item对象,则将对象传递给pipelines.py进行处理
如果是scrapy.Request对象,则会获取该对象指向的链接,并在请求完成后调用该对象的回调函数
https://www.jianshu.com/p/7c1a084853d8
P108Request和Response对象
Request对象:
属性
url:request对象发送请求的URL
callback:下载器下载完成后执行的回调函数,即解析函数
method:默认get,若要用post改用formrequest对象
headers:请求头,字典类型
meta:在不同请求或响应间传递数据,字典类型
encoding:默认utf-8
dont_filter:表示不由调度器过滤,默认调度器不会向相同的URL发送请求,若要重复请求则设置为false(如下载错误,需重新下载时)
errback:发送错误时执行的函数
from scrapy import Request
yeild Request(url,meta,callback='parse')
https://www.cnblogs.com/themost/p/7044673.html?utm_source=itdadao&utm_medium=referral
Response对象
1.meta:从其他请求传过滤的meta属性,可用于保持多个请求之间的数据连接
2.encoding:将返回当前字符串编码和解码的格式
3.text:网页返回的数据,scarpy已解码为unicode字符串
4.body:网页返回的数据,未经过解码,未bytes字符串,【有时scrapy解码可能不对需要自己对bytes解码】
【网络和硬盘之间通信的数据是bytes类型,即编码后的数据】
5.xpath:xpath选择器
6.css:css选择器