目录
架构介绍
安装创建和启动
配置文件目录介绍
爬取数据, 并解析
数据持久化
保存到文件
保存到 Redis
动作链, 控制滑动的验证码
架构介绍
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()
来源: https://www.cnblogs.com/guyouyin123/p/12676385.html