概述
Scrapy异步爬虫框架
- 一、scrapy简介
- (一)scrapy 的工作流程
- (二)要用到的方法
- 二、scrapy 的快速入门
- (一)前期准备
- (二)items.py封装文件
- (三)settings.py配置项文件
- (三)爬虫程序名字.py文件
- (四)pipelines.py爬虫管道文件
- (五)middlewares.py中间键文件
- 三、scrapy 的多种请求方式
- (一)get请求
- (二)post请求
- (三)添加cookies
- (四)设置代理ip
- 四、crawlspider自动获取url
- (一)准备程序
- (二)爬虫程序名字.py文件
- 五、scrapy-redis分布式爬虫
- (一)scrapy_redis工作流程
- (二)实现分布式爬虫步骤
- 六、scrapy 的实现案例
一、scrapy简介
什么是Scrapy
- Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架,我们只需要实现少量的代码,就能够快速的抓取
Scrapy,基于异步,使用了Twisted异步网络框架,可以加快我们的下载速度
优点
- 可配置、可扩展性非常高
- 比较灵活,让稳定和快速
- 基于异步,内部封装了这个twisted异步网络框架,复杂,采用了大量闭包
- 也提供了可控的速度
异步和非阻塞的区别
- 异步:调用在发出之后,这个调用就直接返回,不管有无结果
- 非阻塞:关注的是程序在等待调用结果时的状态,指在不能立刻得到结果之前,该调用不会阻塞当前线程
(一)scrapy 的工作流程
Scrapy 功能组成 | 作用 | 是否还要编写 |
---|---|---|
Scrapy engine(引擎) | 总指挥:负责数据和信号的在不同模块间的传递 发动机、统筹全局、整个框架的核心 | 不需要 , scrapy已经实现 |
Scheduler(调度器) | 一个队列,存放引擎发过来的request请求(接收从引擎发过来的url,入列,然后向引擎发送request请求,直到url全部取完) | 不需要 |
Downloader(下载器) | 接收引擎发过来的请求,发出网页请求,得到相应结果,源码给引擎 | 不需要 |
Spider(爬虫) | 处理引擎发来的response,提取数据,提取url,并交给引擎 | 需要手写 |
Item Pipline(管道) | 处理引擎传过来的数据,比如存储(数据处理、存储数据) | 需要手写 |
Downloader Middlewares(下载中间件) | 可以设置headers、代理IP等),处理引擎和下载器之间的请求和响应 | 一般不用手写 |
Spider Middlewares(爬虫中间件) | 可以自定义requests请求和进行response过滤。处理下载器之间的 请求与响应、和发出新的请求) | 一般不用手写 |
(二)要用到的方法
一些方法 | 作 用 |
---|---|
response.body | 返回网页源代码,未解码 |
response.text | 返回网页源代码,解码str形式 |
response.xpath(xpath路径) | xapth路径,和普通xpath一样 |
scrapy.Request() | 返回给下载器,翻页和爬详情页会用到 ,参数:callback回调函数、dont_filter=True,默认false去重,meta={‘item’:item}用来给回调函数传参 |
meta覆盖问题
- 利用meta参数在进行不同的解析方法之间传递数据的时候,如果需要继续的交给调度器去请求,会出现item被覆盖的问题
- 解决方案:1 用deepcopy 2 创建新的item对象
二、scrapy 的快速入门
(一)前期准备
第一步 先创建scrapy项目 (dos命令行 pycharm终端)
- scrapy startproject mySpider(scrapy项目的名称)
scrapy startproject gsw
第二步 创建爬虫程序
- scrapy genspider demo “demo.cn” (demo是你爬虫的名字 demo.cn 爬取的范围)
- demo的名字最好不要和scrapy项目的名称重合
- 记得切换工作环境 cd
scrapy genspider gs "gushiwen.cn"
第三步 运行scrapy的命令
- 1 在终端 scrapy crawl 爬虫程序名字(例如db)
- 2 可以定义一个提供运行py文件,可以存在scrapy.cfg并列的文件夹里,例如start.py,用start来运行整个框架程序
# 运行py文件,写入
from scrapy import cmdline
#cmdline.execute('scrapy crawl gs'.split())
cmdline.execute(['scrapy','crawl','gs'])
(二)items.py封装文件
- 这里封装的是爬取下来的数据item,将其封装到类里面
import scrapy class GswItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() title = scrapy.Field() author = scrapy.Field() dynasty = scrapy.Field() content = scrapy.Field() pass
(三)settings.py配置项文件
为什么需要配置文件:
- 配置文件存放一些公共的变量(比如数据库的地址,账号密码等)
方便自己和别人修改 - 一般用全大写字母命名变量名 SQL_HOST = ‘192.168.0.1’
- settings文件详细信息:https://www.cnblogs.com/cnkai/p/7399573.html
OBOTSTXT_OBEY = False网站的君子协议
- 我们不遵守,改成False
CONCURRENT_REQUESTS = 16并发量
- 默认的并发量16
DOWNLOAD_DELAY = 1下载延迟
- 下载延迟默认3秒,改为1秒
COOKIES_ENABLED = False
- 默认是注释的,禁用
- 取消注释
- False 找到是settings请求抱头的cookies
- True 找到是下载中间键的cookies
DEFAULT_REQUEST_HEADERS请求抱头
- DEFAULT_REQUEST_HEADERS = {
‘user-agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36’,
‘Accept’: ‘text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8’,
‘Accept-Language’: ‘en’,
}
SPIDER_MIDDLEWARES爬虫中间件
- 一般不改变,取消注释,开启自定义中间键
DOWNLOADER_MIDDLEWARES下载中间键
- 一般不改变,取消注释,开启自定义中间键
- 如果不需要添加随机请求头的话,就不必打开注释
DOWNLOADER_MIDDLEWARES = { # 'tencent.middlewares.TencentDownloaderMiddleware': 543, # 这个是middlewares.py中自定义的UserAgentDownloaderMiddleware的函数 'tencent.middlewares.UserAgentDownloaderMiddleware': 543 }
ITEM_PIPELINES管道
- 取消注释,开启管道
- 从pipeline的字典形可以看出来,pipeline可以有多个,而且确实pipeline能够定义多个
- 为什么需要多个pipeline:
1 可能会有多个spider,不同的pipeline处理不同的item的内容
2 一个spider的内容可以要做不同的操作,比如存入不同的数据库中
- 为什么需要多个pipeline:
- 注意:
1 pipeline的权重越小优先级越高
2 pipeline中process_item方法名不能修改为其他的名称
(三)爬虫程序名字.py文件
class GsSpider--------------这里定义爬虫程序
-
类属性:
- name = ‘gs’ # 说明爬虫程序的名字
- allowed_domains = [‘gushiwen.cn’] # 爬取的范围 给一个最大的范围 添加多个范围 可以修改
- start_urls = [‘https://www.gushiwen.cn/default_1.aspx’]
- 起始的url地址 可修改,后面要增添的url不在这里添加,这里仅仅是一个导火索的作用
- 把它修改成正确的url
-
def parse(self, response) 解析网页
解析response
:一般在这里定义第一种的页面爬取- 支持xpath。response.xpath(‘xpath路径’)
- get()返回一条数据,getall() 返回多条数据
- extract_first() 返回一条数据,extract() 返回多条数据
- 默认都是get请求
- 支持xpath。response.xpath(‘xpath路径’)
启动item,数据封装
- item = GswItem() item[‘href’] = href 存储数据
把结果返回
- 给管道:yield item
- 有翻页的,给下载器:yield scrapy.Request(url=next_url,callback=self.parse) + yield item
- 有详情页,给下载器:因为详情页的数据也要传给管道保存,这时就在解析详情页的函数detail_parse里面yield item
yield scrapy.Request( url=next_url, callback=self.detail_parse meta={'item':item} )
爬取详情页
- callback=self.detail_content回调函数,因为详情页与本也的解析方式不一致,所以这里必须指定详情页的函数
- meta={‘item’:item} 是用作函数之间数据进行交流的,因为我们想把详情页的数据同本业的数据一并保存下来,并且一一对应
detail_url = self.two_url.format(post_id) yield scrapy.Request( url=detail_url, callback=self.detail_content, meta={'item':item} )
翻页
:不需要重写解析网页的方法,区别爬取详情页- 1.找url规律
for i in range(2,5): #这里就不包含第一页 next_url = yield scrapy.Request( url=next_url, callback=self.parse # 回调函数,如果解析逻辑一样的话,可以省略 )
- 2.下一页标签里有href链接
- 对于动态加载的数据,直接翻页的没用,这时候还是得找接口url的规律
next_href = response.xpath('//a[@id="amore"]/@href').get() if next_href: next_url = response.urljoin(next_href) # urljoin()可以进行url地址的不全 yield scrapy.Request( url=next_url, callback=self.parse # 如果这个逻辑是这个parse 就可以省略 )
- 1.找url规律
-
def detail_content(self,response): 解析详情页
- 这里要把数据传给管道,因为有了详情页,所以需要联系以前的数据并保存详情页的数据
# item = response.meta['item'],两种都可以,链接主页面的数据 item = response.meta.get('item') data = json.loads(response.text) # 将详情页的信息也保存在item里面 item['job_duty'] = data['Data']['Responsibility'] # 将合并的数据item传给管道 yield item
- 这里要把数据传给管道,因为有了详情页,所以需要联系以前的数据并保存详情页的数据
(四)pipelines.py爬虫管道文件
定义数据item的保存方式
-
def open_spider(self,spider):
- 可写可不写,定义爬虫开始的动作,函数名固定
- 必须传spider进去,是爬虫对象,可以不引用
- 只会运行一次
-
def process_item(self, item, spider)
- 在这里可以数据保存方式,函数名固定
-
def close_spider(self,spider):
- 可写可不写,定义爬虫结束的动作,函数名固定
- 必须传spider进去,是爬虫对象,可以不引用
- 只会运行一次
# txt保存
import json
class GswPipeline:
def open_spider(self,spider):
self.fp = open('gsw.txt','w',encoding='utf-8')
def process_item(self, item, spider):
# 这个Item从爬虫文件过来的时候不是一个字典对象。它是通过GswItem()实例化出来的对象
item_json = json.dumps(dict(item),ensure_ascii=False)
self.fp.write(item_json + 'n')
# print(item)
return item
def close_spider(self,spider):
self.fp.close()
# csv保存
import csv
class TencentPipeline:
def open_spider(self, spider):
print('开始写入')
self.fp = open('hr.csv', 'w', encoding='utf-8',newline='')
header = ['RecruitPostId', 'RecruitPostName', 'LocationName', 'BGName', 'CategoryName', 'ProductName', 'Responsibility','Requirement', 'PostURL']
self.Dwriter = csv.DictWriter(self.fp,header)
self.Dwriter.writeheader()
self.flag = 1
def process_item(self, item, spider):
print(self.flag)
self.Dwriter.writerow(dict(item))
self.flag+=1
(五)middlewares.py中间键文件
def process_request(self,request,spider) (重点)
-
当每个request通过下载中间件的时候,该方法调用,下载器中间件是引擎和下载器之间通信的中间件 我们可以设置代理 达到反反爬的目的
需要实现2个方法 -
参数
- request就是拦截的请求
- spider 爬虫类实例化的对象
-
返回值
- 1 返回None:它正常的操作 scrapy正常的取处理request对象 执行对应的方法
- 2 返回response对象 :就是中间件直接把返回的response对象给引擎
- 3 返回request对象 :就是中间件把它返回的request对象给下载器 就是根据这个request对象返回数据
-
实现随机ua
- 第一步 在middlewares这个文件当中 添加随机ua
- 第二步 重写process_request()方法
from fake_useragent import UserAgent import random class UserAgentDownloaderMiddleware: USER_AGENTS = [ "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)", "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)", "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0", "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5"] # 第一种方式 里面改变策略 # def process_request(self, request, spider): # user_agent = random.choice(self.USER_AGENTS) # request.headers['User-Agent'] = user_agent # 第二种方式 def process_request(self, request, spider): ua = UserAgent() user_agent = ua.random request.headers['User-Agent'] = user_agent
- 第三步 千万不要忘记在settings里面开启下载中间件
DOWNLOADER_MIDDLEWARES = { # 'tencent.middlewares.TencentDownloaderMiddleware': 543, 'tencent.middlewares.UserAgentDownloaderMiddleware': 543 }
def process_response(self,request,spider)
- 当下载器完成http请求的时候,该方法调用
- 可以直接在这里直接把结果返回
- 不常用
三、scrapy 的多种请求方式
(一)get请求
scrapy.Request
- 该方法当中所有的参数都可以作为强求对象request的属性
- scrapy.Request()常用的参数:url callback meta cookies headers
(二)post请求
scrapy.FormRequest
data = {
'commit': commit,
'authenticity_token': authenticity_token,
'login': login,
'password': password,
'webauthn-support': 'supported',
'webauthn-iuvpaa-support': 'unsupported',
'timestamp': timestamp,
'timestamp_secret': timestamp_secret,
}
# 携带数据发送post请求
yield scrapy.FormRequest(
# 目标Url
url='https://github.com/session',
# 提交的数据
formdata=data,
# 响应的方法
callback=self.after_login
)
scrapy.FormRequest.from_response
yield scrapy.FormRequest.from_response(
# 请求的响应结果
response=response,
# 提交数据,这里的data并没有携带其他数据,因为页面响应有的不需要携带
formdata={'login':'LogicJerry','password':'12122121zxl'},
# 回调方法
callback=self.after_login
)
(三)添加cookies
- cookies 必须是键值对的格式
# 用字典列表推导式将cookies改成键值对格式
cookies = {i.split('=')[0]:i.split('=')[1] for i in cookies.split('; ')}
在爬虫程序中重写start_requests()方法
def start_requests(self):
# cookie在scrapy当中要以字典的类型呈现
cookies = 'pgv_pvi=225631232; pgv_pvid=3623680301; RK=TYZUGzQ3QB; ptcz=237e85c55089b96cc7064002ea3d08b08ce735ca2eba518b13b4280097904bc3; QZ_FE_WEBP_SUPPORT=1; tvfe_boss_uuid=2346e65d4114028d; LW_uid=01v5p9u7y342R3C1u5B107k433; eas_sid=p1f5i9k7v3U2E3Q1D5q167Q5k2; iip=0; pac_uid=1_378110341; livelink_pvid=2660289536; o_cookie=378110341; ied_qq=o0192149641; __Q_w_s__QZN_TodoMsgCnt=1; nutty_uuid=df85c844-0c6a-4cd8-891e-f13e7322e341; qz_screen=1366x768; ptui_loginuin=378110341; Qs_lvt_323937=1611384174%2C1611665667%2C1617629806%2C1618812254%2C1622096214; Qs_pv_323937=3235112205715580400%2C3742927204160170000%2C820459265641302400%2C403055695262391800%2C1978804417902919700; LW_sid=W1U6Q2g2q182K4D044M6b5n1q0; luin=o0378110341; lskey=00010000f3b05d9b59e2b6aa0dba2c8409431c23907eb00efbf45105f1687bca7d8e70b3a8e712b046be2106; _qpsvr_localtk=0.9563249296320062; pgv_info=ssid=s2794478150; uin=o0192149641; skey=@O3mDMCGnR; p_uin=o0192149641; pt4_token=cyLoQou4IcUFiADXykpz1VeW*XWVwTMAKqHP9AyOlos_; p_skey=*iAEoWdPw0AXu967mPcXO88Uhrju*lBmEDNEsf28UNw_; Loading=Yes; 192149641_todaycount=0; 192149641_totalcount=2000; cpu_performance_v8=24'
# 用字典列表推导式将cookies改成键值对格式
cookies = {i.split('=')[0]:i.split('=')[1] for i in cookies.split('; ')}
# 发送请求
yield scrapy.Request(
url=self.start_urls[0],
callback=self.parse,
cookies=cookies
)
setting中添加.
- setting中 取消COOKIES_ENABLED = False的注释
- 在DEFAULT_REQUEST_HEADERS请求抱头中添加cookies
下载中间键添加
- setting中改为 COOKIES_ENABLED = True
def process_request(self, request, spider):
cookies =
request.cookies = cookies
(四)设置代理ip
下载中间键添加.
# meta的作用:1.在不同解析函数之间传递数据 2.可以定义ip代理
def process_request(self, request, spider):
proxy =
request.meta['proxy'] = proxy
四、crawlspider自动获取url
引入
- 之前的代码中,我们有很大一部分时间在寻找下一页的URL地址或者内容的URL地址上面,这个过程能更简单一些吗?
- 思路:
1.从response中提取所有的标签对应的URL地址
2.自动的构造自己resquests请求,发送给引擎
定义
- 是scrapy另一种爬取数据的方式
- 继承了spider爬虫类,用来简化scrapy部分功能
- 用于静态网页会更好,动态网页反而会变得复杂了
特点
- 自动根据规则提取链接发送引擎
- 如果ur规律简单,还是很方便的
缺点
- 并不常用,因为实现自动构造的请求是依照某种规则,这个规则就是正则表达式,如果正则表达式写的不准确,爬的或多或少或错误都是无法控制的
- 所以针对url规律不明显,正则表达式写起来复杂的url就并不适用了
(一)准备程序
创建scrapy项目
- scrapy startproject mySpider(scrapy项目的名称)
- 和普通的一样
创建crawlspider的爬虫程序py文件
- 生成crawlspider的命令:scrapy genspider -t crawl 爬虫名字 域名
- -t 以某种方式,以crawl方式创建
(二)爬虫程序名字.py文件
rules
- 定义链接提取的规则,rules是元组格式,定义在类属性里
- rules = (
Rule(LinkExtractor(allow=r’Items/’), callback=‘parse_item’, follow=True),
)- LinkExtractor: 链接提取器 用于设置链接提取的规则,allow:允许的规则,定义就是正则匹配的url
- callback:
- 实现回调的方法 这里填的是方法的字符串格式
- 满足这个规则的url,应该要执行哪个回调函数。
- 因为CrawlSpider使用了parse作为回调函数,因此不要覆盖parse作为回调函数自己的回调函数。
- follow: 决定是否在链接提取器提取的链接继续跟进 为True就根据 否则不跟进
- process_links: 从link_extractor中获取到链接后会传递给这个函数,用来过滤不需要爬取的链接
import scrapy
from scrapy.linkextractors import LinkExtractor
# Rule 是类
from scrapy.spiders import CrawlSpider, Rule
class CgsSpider(CrawlSpider):
name = 'cgs'
allowed_domains = ['gushiwen.org','gushiwen.cn']
start_urls = ['https://www.gushiwen.cn/default_1.aspx']
# rules 链接提取的规则
rules = (
# 列表页,获取前两页的url
Rule(LinkExtractor(allow=r'https://www.gushiwen.cn/default_[1,2].aspx'), follow=True),
# 通过对列表查询这个详情页 因为url中有特殊字符,所以这里要转义
Rule(LinkExtractor(allow=r'https://so.gushiwen.cn/shiwenv_w+.aspx'), callback='parse_item'),
)
def parse_item(self, response):
item = {}
#item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
#item['name'] = response.xpath('//div[@id="name"]').get()
#item['description'] = response.xpath('//div[@id="description"]').get()
gsw_divs = response.xpath('//div[@class="left"]/div[@class="sons"]')
for gsw_div in gsw_divs:
# 结果有误,爬多了,因为详情页的匹配并不准确
title = gsw_div.xpath('.//h1/text()').get()
print(title)
return item
- 其他文件和普通的一样
五、scrapy-redis分布式爬虫
定义
- scrapy-redis,基于redis数据库 运行在scrapy之上的一个组件,可以让scrapy支持分布式开发了、支持主从同步
scrapy和scrapy-redis有啥区别?
- scrapy python的爬虫框架 爬取效率极高 具有高度的定制型,但不支持分布式
- scrapy_redis支持分布式开发了、支持主从同步
分布式与集群
- 分布式:多个人在一起做不同的事
- 集群:多个人在一起做相同的事
分布式爬虫的优点
- 1 可以充分利用多态机器的带宽
- 2 可以充分利用不同电脑的ip
- 3 多台机器爬取效率更高
(一)scrapy_redis工作流程
redis内保存的数据
- dmoz:requests 存放的是待爬取的requests对象
- dmoz:item 爬取到的信息
- dmoz:dupefilter 已经爬取的requests
(二)实现分布式爬虫步骤
要求:
-
安装包:
- redis
- scrapy_redis:这个要求我们爬虫程序继承RedisSpider类,但是start_urls 就要改写成 reids_key,并打开redis往里面添加初始url
-
爬取的时候redis服务器需要开启
- 第一步 创建scrapy项目,创建爬虫文件 - 第二步 实现逻辑 - 第三步 修改成分布式爬虫,并往redis加入dangdang:start_urls 的初始url # 往redis加入dangdang:start_urls 的初始url lpush 爬虫文件名字:start_urls url ''' 爬虫文件修改 ''' # 1 导入模块 from scrapy_redis.spiders import RedisSpider # 2 修改继承的父类------ RedisSpider class DangdangSpider(RedisSpider): # 第二步 # 3 把start_urls 改写成 reids_key='爬虫文件名字:start_urls' # 4 settings文件当中的内容修改 ''' settings.py配置文件修改 ''' # 去重过滤 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # scheduler队里 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 数据持久化 SCHEDULER_PERSIST = True ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400, }
六、scrapy 的实现案例
- 爬取古诗文网站 - scarpy - python爬虫案例
- 爬取腾讯招聘网站 - scrapy - python爬虫案例
- 爬取汽车之家图片 - scrapy- crawlspider - python爬虫案例
最后
以上就是强健汉堡为你收集整理的Scrapy- 异步爬虫框架-分布式爬虫scrapy-redis-python爬虫知识点8一、scrapy简介二、scrapy 的快速入门三、scrapy 的多种请求方式四、crawlspider自动获取url五、scrapy-redis分布式爬虫六、scrapy 的实现案例的全部内容,希望文章能够帮你解决Scrapy- 异步爬虫框架-分布式爬虫scrapy-redis-python爬虫知识点8一、scrapy简介二、scrapy 的快速入门三、scrapy 的多种请求方式四、crawlspider自动获取url五、scrapy-redis分布式爬虫六、scrapy 的实现案例所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复