scarpy爬虫框架

  • 2020 年 4 月 11 日
  • 筆記

架构介绍

Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。但目前Scrapy的用途十分广泛,可用于如数据挖掘、监测和自动化测试等领域,也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。

​ Scrapy 是基于twisted框架开发而来,twisted是一个流行的事件驱动的python网络框架。因此Scrapy使用了一种非阻塞(又名异步)的代码来实现并发。整体架构大致如下

IO多路复用

# 引擎(EGINE)(大总管)  引擎负责控制系统所有组件之间的数据流,并在某些动作发生时触发事件。有关详细信息,请参见上面的数据流部分。  # 调度器(SCHEDULER)  用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL的优先级队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址  # 下载器(DOWLOADER)  用于下载网页内容, 并将网页内容返回给EGINE,下载器是建立在twisted这个高效的异步模型上的  # 爬虫(SPIDERS)  SPIDERS是开发人员自定义的类,用来解析responses,并且提取items,或者发送新的请求  # 项目管道(ITEM PIPLINES)  在items被提取后负责处理它们,主要包括清理、验证、持久化(比如存到数据库)等操作      # 两个中间件  -爬虫中间件  -下载中间件(用的最多,加头,加代理,加cookie,集成selenium)      

安装创建和启动

# 1 框架 不是 模块  # 2 号称爬虫界的django(你会发现,跟django很多地方一样)  # 3 安装  	-mac,linux平台:pip3 install scrapy    -windows平台:pip3 install scrapy(大部分人可以)    	- 如果失败:        1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https://www.lfd.uci.edu/~gohlke/pythonlibs        3、pip3 install lxml        4、pip3 install pyopenssl        5、下载并安装pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/        6、下载twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted        7、执行pip3 install 下载目录Twisted-17.9.0-cp36-cp36m-win_amd64.whl        8、pip3 install scrapy   # 4 在script文件夹下会有scrapy.exe可执行文件  	-创建scrapy项目:scrapy startproject 项目名   (django创建项目)    	-创建爬虫:scrapy genspider 爬虫名 要爬取的网站地址   # 可以创建多个爬虫     # 5 命令启动爬虫  		-scrapy crawl 爬虫名字    		-scrapy crawl 爬虫名字 --nolog   # 没有日志输出启动   # 6 文件执行爬虫(推荐使用)  	-在项目路径下创建一个main.py,右键执行即可    	from scrapy.cmdline import execute      # execute(['scrapy','crawl','chouti','--nolog'])  # 没有设置日志级别      execute(['scrapy','crawl','chouti'])			  # 设置了日志级别  

配置文件目录介绍

-crawl_chouti   # 项目名    -crawl_chouti # 跟项目一个名,文件夹      -spiders    # spiders:放着爬虫  genspider生成的爬虫,都放在这下面      	-__init__.py        -chouti.py # 抽屉爬虫        -cnblogs.py # cnblogs 爬虫      -items.py     # 对比django中的models.py文件 ,写一个个的模型类      -middlewares.py  # 中间件(爬虫中间件,下载中间件),中间件写在这      -pipelines.py   # 写持久化的地方(持久化到文件,mysql,redis,mongodb)      -settings.py    # 配置文件    -scrapy.cfg       # 不用关注,上线相关的          # 配置文件settings.py  ROBOTSTXT_OBEY = False   # 是否遵循爬虫协议,强行运行  USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'    # 请求头中的ua,去浏览器复制,或者用ua池拿  LOG_LEVEL='ERROR' # 这样配置,程序错误信息才会打印,  	#启动爬虫直接 scrapy crawl 爬虫名   就没有日志输出    	# scrapy crawl 爬虫名 --nolog  # 配置了就不需要这样启动了        # 爬虫文件  class ChoutiSpider(scrapy.Spider):      name = 'chouti'   # 爬虫名字      allowed_domains = ['https://dig.chouti.com/']  # 允许爬取的域,想要多爬就注释掉      start_urls = ['https://dig.chouti.com/']   # 起始爬取的位置,爬虫一启动,会先向它发请求        def parse(self, response):  # 解析,请求回来,自动执行parser,在这个方法中做解析          print('---------------------------',response)  

爬取数据,并解析

# 1 解析,可以使用bs4解析  from bs4 import BeautifulSoup  soup=BeautifulSoup(response.text,'lxml')  soup.find_all()  # bs4解析  soup.select()  # css解析    # 2 内置的解析器  response.css  response.xpath    # 内置解析    # 所有用css或者xpath选择出来的都放在列表中    # 取第一个:extract_first()    # 取出所有extract()  # css选择器取文本和属性:      # .link-title::text  # 取文本,数据都在data中      # .link-title::attr(href)   # 取属性,数据都在data中  # xpath选择器取文本和属性      # .//a[contains(@class,"link-title")/text()]      #.//a[contains(@class,"link-title")/@href]    # 内置css选择期,取所有  div_list = response.css('.link-con .link-item')  for div in div_list:      content = div.css('.link-title').extract()      print(content)  

数据持久化

# 方式一(不推荐)    -1 parser解析函数,return 列表,列表套字典      # 命令   (支持:('json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle')      # 数据到aa.json文件中    -2 scrapy crawl chouti -o aa.json  # 代码:  lis = []  for div in div_list:      content = div.select('.link-title')[0].text      lis.append({'title':content})      return lis      # 方式二 pipline的方式(管道)     -1 在items.py中创建模型类     -2 在爬虫中chouti.py,引入,把解析的数据放到item对象中(要用中括号)     -3 yield item对象     -4 配置文件配置管道         ITEM_PIPELINES = {          # 数字表示优先级(数字越小,优先级越大)         'crawl_chouti.pipelines.CrawlChoutiPipeline': 300,         'crawl_chouti.pipelines.CrawlChoutiRedisPipeline': 301,      	}    -5 pipline.py中写持久化的类          spider_open  # 方法,一开始就打开文件          process_item # 方法,写入文件          spider_close # 方法,关闭文件        

保存到文件

# choutiaa.py 爬虫文件  import scrapy  from chouti.items import ChoutiItem  # 导入模型类  class ChoutiaaSpider(scrapy.Spider):      name = 'choutiaa'      # allowed_domains = ['https://dig.chouti.com/']   # 允许爬取的域      start_urls = ['https://dig.chouti.com//']   # 起始爬取位置      # 解析,请求回来,自动执行parse,在这个方法中解析      def parse(self, response):          print('----------------',response)          from bs4 import BeautifulSoup          soup = BeautifulSoup(response.text,'lxml')          div_list = soup.select('.link-con .link-item')            for div in div_list:              content = div.select('.link-title')[0].text              href = div.select('.link-title')[0].attrs['href']              item = ChoutiItem()  # 生成模型对象              item['content'] = content  # 添加值              item['href'] = href              yield item  # 必须用yield    # items.py 模型类文件  import scrapy  class ChoutiItem(scrapy.Item):      content = scrapy.Field()      href = scrapy.Field()    # pipelines.py 数据持久化文件  class ChoutiPipeline(object):      def open_spider(self, spider):          # 一开始就打开文件          self.f = open('a.txt', 'w', encoding='utf-8')        def process_item(self, item, spider):          # print(item)          # 写入文件的操作          self.f.write(item['content'])          self.f.write(item['href'])          self.f.write('n')          return item        def close_spider(self, spider):          # 写入完毕,最后关闭文件          self.f.close()    # setting.py  ITEM_PIPELINES = {      # 数字表示优先级,越小优先级越高     'chouti.pipelines.ChoutiPipeline': 300,     'chouti.pipelines.ChoutiRedisPipeline': 301,  }  

保存到redis

# settings.ps  ITEM_PIPELINES = {      # 数字表示优先级,越小优先级越高     'chouti.pipelines.ChoutiPipeline': 300,     'chouti.pipelines.ChoutiRedisPipeline': 301,  }    # pipelines.py  # 保存到redis  from redis import Redis  class ChoutiRedisPipeline(object):      def open_spider(self, spider):          # 不写参数就用默认配置          self.conn = Redis(password='123')  # 一开始就拿到redis对象        def process_item(self, item, spider):          print(item)          import json          s = json.dumps({'content': item['content'], 'href': item['href']})          self.conn.hset('choudi_article', item['id'], s)            return item        def close_spider(self, spoder):          pass          # self.conn.close()    # chouti.py  import scrapy  from chouti.items import ChoutiItem  # 导入模型类  class ChoutiaaSpider(scrapy.Spider):      name = 'choutiaa'      # allowed_domains = ['https://dig.chouti.com/']   # 允许爬取的域      start_urls = ['https://dig.chouti.com//']   # 起始爬取位置      # 解析,请求回来,自动执行parse,在这个方法中解析      def parse(self, response):          print('----------------',response)          from bs4 import BeautifulSoup          soup = BeautifulSoup(response.text,'lxml')          div_list = soup.select('.link-con .link-item')            for div in div_list:              content = div.select('.link-title')[0].text              href = div.select('.link-title')[0].attrs['href']              id = div.attrs['data-id']              item = ChoutiItem()  # 生成模型对象              item['content'] = content  # 添加值              item['href'] = href              item['id'] = id              yield item  # 必须用yield  

动作链,控制滑动的验证码

from selenium import webdriver  from selenium.webdriver import ActionChains  import time  bro=webdriver.Chrome(executable_path='./chromedriver')  bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')  bro.implicitly_wait(10)    #切换frame(很少)  bro.switch_to.frame('iframeResult')  div=bro.find_element_by_xpath('//*[@id="draggable"]')    # 1 生成一个动作练对象  action=ActionChains(bro)  # 2 点击并夯住某个控件  action.click_and_hold(div)  # 3 移动(三种方式)  # action.move_by_offset() # 通过坐标(x,y)  # action.move_to_element() # 到另一个标签  # action.move_to_element_with_offset() # 到另一个标签,再偏移一部分      for i in range(5):      action.move_by_offset(10,10)    # 4 真正的移动  action.perform()    # 5 释放控件(松开鼠标)  action.release()