爬虫--scrapy框架

  • 一、scrapy框架的简介和基础使用
  • 二、scrapy框架--parse方法解析示例
  • 三、scrapy框架--基于终端指令的持久化存储
  • 四、scrapy框架--基于管道的持久化存储
  • 五、scrapy框架--基于MySQL数据库的持久化存储
  • 六、scrapy框架--基于redis的持久化存储
  • 七、scrapy框架--将爬取到的数据分别存储在本地磁盘、redis、MySQL中
  • 八、scrapy框架--多个url数据爬取(请求手动发送)
  • 九、scrapy框架--核心组件
  • 十、scrapy框架--发送post请求
  • 十一、scrapy框架--session的使用
  • 十二、scrapy框架--代理的使用
  • 十三、scrapy框架--日志等级
  • 十四、scrapy框架--请求传参
  • 十五、scrapy框架--CrawlSpider的使用
  • 十六、scrapy框架--分布式爬虫RedisCrawlSpider
  • 十七、scrapy框架--分布式爬虫RedisSpider
  • 十八、scrapy框架--UA池、代理池
  • 十九、scrapy框架--综合应用(爬网易新闻)
  • 二十、scrapy框架--总结

===============================================

一、scrapy框架的简介和基础使用

a)概念:为了爬取网站数据而编写的一款应用框架,出名,强大。
    框架其实就是一个集成了相应功能且具有很强通用性的项目模板。
    (高性能的异步下载,解析,持久化...)
b) 安装:
  i. linux mac --pip install scrapy
  ii. windows
   -step1: pip install wheel
   -step2: 下载并安装twisted: https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
               pip install 下载好的t框架.whl
   -step3: pip install pywin32
      -step4: pip install scrapy
c) 测试安装是否成功:
  命令终端输入“scrapy”后出现scrapy版本号
d) 基础使用:使用流程
  i. 创建一个工程,在某目录的命令行:scrapy startproject 工程名称
  ii. 在工程目录下创建一个爬虫文件:

    -step1: cd 工程名称
    -step2: scrapy genspider 爬虫文件名称 起始url 
    例如:scrapy genspider first www.qiushibaike.com
  ------------------------------------------------------------------------
  附:目录结构说明
  spiders 爬虫目录,如:创建文件,编写爬虫解析规则
  settings.py 配置文件,如:递归的层数,并发数,延迟下载等
  pipelines 数据持久化处理
  items.py 设置数据存储模板,用于结构化数据,如Django的Model
  scrapy.cfg 项目的主配置信息(真正爬虫相关的配置信息在settings.py文件中)
  ------------------------------------------------------------------------
  iii.对应的文件中编写爬虫程序来完成爬虫的相关操作  

# -*- coding: utf-8 -*-
import scrapy


class FirstSpider(scrapy.Spider):
    # 爬虫文件的名称:用来定位到某一个爬虫文件,因为一个项目可以有多个爬虫文件
    name = 'first'
    # 允许的域名:存放一系列域名,只能爬取该列表内的域名
    # 一般可以注释掉,如果想爬取图片等就可能不在这个域名
    allowed_domains = ['www.qiushibaike.com']
    # 起始url:就是当前工程想爬取的url页面,必须在允许的域名内
    start_urls = ['http://www.qiushibaike.com/']
    # 解析方法:对获取的页面数据进行指定内容的解析,建议使用xpath进行解析
    # 根据起始url发起请求,response是请求成功后拿到的响应对象
    # parse方法返回值必须为迭代器或者None
    def parse(self, response):
        print(response.text)
parse方法

  iv. 配置文件settings.py的编写

     第22行:ROBOTSTXT_OBEY = False # 不遵守robots协议
     第19行:USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
  v. 执行:scrapy crawl 爬虫文件的名称 --nolog
    如果后面加--nolog不输出日志信息
    当前的工程从爬虫文件开始执行,scrapy框架会向起始url列表中的url发起请求,请求成功后获取响应对象,传给parse方法,然后调用parse方法进行解析

二、scrapy框架--parse方法解析示例

--需求:解析糗百中段子的内容和作者
1.创建一个工程:scrapy startproject qiubaiPro
2.创建一个爬虫文件:
cd qiubaiPro/
scrapy genspider qiubai www.qiushibaike.com/text
3.编写代码(爬虫文件):

# -*- coding: utf-8 -*-
import scrapy
class QiubaiSpider(scrapy.Spider):
  name = 'qiubai'
  # allowed_domains = ['www.qiushibaike.com']
  start_urls = ['https://www.qiushibaike.com/text/']
  def parse(self, response):
    div_list = response.xpath('//div[@id="content-left"]/div')
    for div in div_list:
      # xpath解析到的内容被存储到了Selector对象中
      # 可以用extract()方法获取Selector里的数据
      # extract_first()等价于extract()[0]
      author = div.xpath('./div/a[2]/h2/text()').extract_first()
      content = div.xpath('.//div[@class="content"]/span/text()').extract_first()

4.配置文件的编写:改19、22行
5.执行工程

三、scrapy框架--基于终端指令的持久化存储

i. 保证parse方法返回一个可迭代对象(存储解析到的页面内容)

# -*- coding: utf-8 -*-
import scrapy
class QiubaiSpider(scrapy.Spider):
  name = 'qiubai'
  start_urls = ['https://www.qiushibaike.com/text/']
  def parse(self, response):
    div_list = response.xpath('//div[@id="content-left"]/div')
    data_list = []
    for div in div_list:
      author = div.xpath('./div/a[2]/h2/text()').extract_first()
      content = div.xpath('.//div[@class="content"]/span/text()').extract_first()
      dict = {"author":author, "content":content}
      data_list.append(dict)
    return data_list

ii. 使用终端指令完成数据存储到指定磁盘文件

   scrapy crawl 爬虫文件名称 -o 磁盘文件.csv --nolog

四、scrapy框架--基于管道的持久化存储

i. items.py:存储解析到的页面数据
ii. pipelines:处理持久化存储的相关操作
iii. 代码实现流程:
  0. 给items对象加属性
  1. 将解析到的页面数据存储到items对象
  2. 使用yield关键字将items提交给管道文件进行处理
  3. 在管道文件中编写代码完成数据存储的操作
  4. 在配置文件中开启管道操作
  5. 执行


0:items加属性,取的时候只能item["author"] 不能item.author
import scrapy
class QiubaiproItem(scrapy.Item):
  author = scrapy.Field()
  content = scrapy.Field()

1. 将解析到的数据(author和content)存储到items对象
# -*- coding: utf-8 -*-
import scrapy
from qiubaiPro.items import QiubaiproItem
class QiubaiSpider(scrapy.Spider):
  name = 'qiubai'
  start_urls = ['https://www.qiushibaike.com/text/']
  def parse(self, response):
    div_list = response.xpath('//div[@id="content-left"]/div')
    for div in div_list:
      author = div.xpath('./div/a[2]/h2/text()').extract_first()
      content = div.xpath('.//div[@class="content"]/span/text()').extract_first()

      # 1,将解析到的数据(author和content)存储到items对象
      item = QiubaiproItem()
      item["author"] = author
      item["content"] = content
      # 2,将item对象提交给管道
      yield item


3. 在管道文件中编写代码完成数据存储的操作
class QiubaiproPipeline(object):
  fp = None
  # 整个爬虫过程中,open方法只会在开始爬虫的时候被调用一次
  def open_spider(self, spider):
    print('开始爬虫')
    self.fp = open('./qiubai_pipe.text','w',encoding='utf8')
  # 该方法可以接受爬虫文件中提交过来的item对象,并且对item对象中存储的页面数据进行持久化
  # 参数item:接收到的item对象
  # 每当爬虫文件yield一次item,该方法就会被执行一次
  # 文件'qiubai_pipe.text'在整个过程只打开一次,关闭一次,文件中就不会只有一条记录
  def process_item(self, item, spider):
    # 取出item对象中的数据
    author = item["author"]
    content = item["content"]
    # 持久化存储
    self.fp.write(author+":"+content+"\n\n")
    return item
  # close方法只会在爬虫结束的时候被调用一次
  def close_spider(self,spider):
    print('爬虫结束')
    self.fp.close()

4. 在配置文件中开启管道操作
第68行:取消注释(数字表示优先级,这里只有一个所以没有谁先谁后)
ITEM_PIPELINES = {
'qiubaiPro.piplines.QiubaiproPipeline':300,
}

5.执行:scrapy crawl qiubai --nolog

五、scrapy框架--基于MySQL数据库的持久化存储

编码流程跟管道一样,只是第3步不同。

0. 给items对象加属性
1. 将解析到的页面数据存储到items对象
2. 使用yield关键字将items提交给管道文件进行处理
3. 在管道文件中编写代码完成数据存储的操作(连接数据库-》执行SQL语句-》提交事务)
4. 在配置文件中开启管道操作
5. 执行


import pymysql
class QiubaiproPipeline(object):
  cursor = None
  conn = None
  def open_spider(self, spider):
    print('开始爬虫')
    # 1.连接数据库
    self.conn = pymysql.Connect(
                host='127.0.0.1',
                port=3306,
                user='root',
                password='mysql8',
                db='qiubai')
  def process_item(self, item, spider):
    # 2.执行SQL语句
    sql = 'insert into qiubai values("%s","%s")'%(item["author"], item["content"])
    self.cursor = self.conn.cursor()
    try:
      self.cursor.excute(sql)
      self.conn.commit()
    except Exception as e:
      print(e)
      self.conn.rollback()
    return item
  def close_spider(self,spider):
    print('爬虫结束')
    # 3.关闭连接
    self.cursor.close()
    self.conn.close()

六、scrapy框架--基于redis的持久化存储

编码流程跟管道一样。只是第3步不同

0. 给items对象加属性
1. 将解析到的页面数据存储到items对象
2. 使用yield关键字将items提交给管道文件进行处理
3. 在管道文件中编写代码完成数据存储的操作(连接redis-》存储数据)
4. 在配置文件中开启管道操作
5. 执行


import redis
class QiubaiproPipeline(object):
  conn = None
  def open_spider(self, spider):
    print('开始爬虫')
    # 1.连接redis
    self.conn = redis.Redis(host='127.0.0.1', port=6379)
  def process_item(self, item, spider):
    # 2.存储数据
    dict = {'author' : item["author"],  'content' : item["content"] }
    self.conn.lpush('data', dict)
    return item
  

七、scrapy框架--将爬取到的数据分别存储在本地磁盘、redis、MySQL中

1. 在管道文件中编写对应平台的管道类
在管道文件中定义三个类。如果下面自定义的两个类也被调用,那也可以写入
2. 在配置文件中对自定义的管道类进行生效操作
ITEM_PIPELINES = {
'qiubaiPro.pipelines.QiubaiproPipeline': 300,
'qiubaiPro.pipelines.QiubaiByFiles': 400,
'qiubaiPro.pipelines.QiubaiByMysql': 500,
}

import redis
#实现将数据存储到redis中
class QiubaiproPipeline(object):
  conn = None
  def open_spider(self, spider):
    print('开始爬虫')
    # 1.连接redis
    self.conn = redis.Redis(host='127.0.0.1', port=6379)
  def process_item(self, item, spider):
    # 2.存储数据
    dict = {'author' : item["author"], 'content' : item["content"] }
    self.conn.lpush('data', dict)
    return item

#实现将数据存储到本地磁盘
class QiubaiByFiles(object):
def process_item(self, item, spider):
    print('写入到本地磁盘')
    return item

#实现将数据存储到mysql数据库
class QiubaiByMysql(object):
def process_item(self, item, spider):
    print('写入到mysql')
    return item

八、scrapy框架--多个url数据爬取(请求手动发送)

需求:爬取糗百所有页码的作者和文章
跟单个url爬取一样,只是标红部分不同

=========items.py============

import scrapy
class QiubaibypagesItem(scrapy.Item):
author = scrapy.Field()
content = scrapy.Field()

=========qiubai.py============

import scrapy
from qiubaiByPages.items import QiubaibypagesItem
class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
start_urls = ['https://www.qiushibaike.com/text/']
# 设计一个通用的url模板
page_num = 1
url = 'https://www.qiushibaike.com/text/page/%d/'

def parse(self, response):
div_list = response.xpath('//div[@id="content-left"]/div')
for div in div_list:
author = div.xpath('./div[@class="author clearfix"]/a[2]/h2//text()').extract_first()
content = div.xpath('.//div[@class="content"]/span//text()').extract_first()
# 创建一个item对象,将解析到的数据存储到item对象
item = QiubaibypagesItem()
item["author"] = author
item["content"] = content
# 将item对象提交给管道
yield item
# 请求的手动发送,13表示最后一页页码
if self.page_num <= 13:
print("爬取到了第%d页."%self.page_num)
self.page_num += 1
new_url = format(self.url % self.page_num)
yield scrapy.Request(url=new_url, callback=self.parse)

=========pipelines.py============

class QiubaibypagesPipeline(object):
fp = None
def open_spider(self, spider):
print('开始爬虫')
self.fp = open('./qiubai.txt','w',encoding='utf-8')
def process_item(self, item, spider):
self.fp.write(item["author"]+":"+item["content"]+"\n\n")
return item
def close_spider(self,spider):
self.fp.close()
print('爬虫结束')

=========settings.py============

USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
ROBOTSTXT_OBEY = False
ITEM_PIPELINES = {
'qiubaiByPages.pipelines.QiubaibypagesPipeline': 300,
}

九、scrapy框架--核心组件

引擎接收到start_url列表中的请求对象,转交给调度器
请求被调度器装到队列里,由调度器调度请求给下载器
下载器从互联网下载后将结果转交给spiders
spiders接收到响应对象后,调度器调用parse方法对页面进行解析
spiders将解析结果封装到items,提交给管道
管道收到items后进行持久化

引擎(Scrapy)
用来处理整个系统的数据流处理, 触发事务(框架核心)

调度器(Scheduler)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址

下载器(Downloader)
用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)

爬虫(Spiders)
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面

项目管道(Pipeline)
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。

十、scrapy框架--发送post请求

发送post请求一定要对start_requests方法进行重写。
  方式1.Request()方法中给method属性赋值
  方式2.FormRequest()进行post请求的发送(推荐)

需求:给百度翻译发送post请求带参数
===========spiders=============

import scrapy
class PostdemoSpider(scrapy.Spider):
name = 'postDemo'
start_urls = ['https://fanyi.baidu.com/sug']
'''
之前一直是发的get请求:
覆盖父类的start_requests方法,对start_url列表中的元素进行get请求的发送
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(url=url, callback=self.parse)
'''
'''
如何发起post请求?
1.将Request方法中的method参数赋值成post
2.FormRequest()可以发起post请求(推荐)
'''
def start_requests(self):
data = {
'kw' : 'dog',
}
for url in self.start_urls:
#formdata请求参数对应的字典
yield scrapy.FormRequest(url=url,formdata=data,callback=self.parse)
def parse(self, response):
print(response.text)

十一、scrapy框架--session的使用

第二次请求的时候,会自动携带第一次请求的cookie,在使用scrapy进行cookie操作的时候,不需要刻意管cookie

需求:爬取豆瓣的个人主页。先登录再查看主页

# -*- coding: utf-8 -*-
import scrapy
class DoubanSpider(scrapy.Spider):
name = 'douban'
allowed_domains = ['www.douban.com']
start_urls = ['https://www.douban.com/accounts/login']
# 登录发送post请求
def start_requests(self):
data = {
'source': 'index_nav',
'form_email': '18212345678',
'form_password': '123456'
}
for url in self.start_urls:
yield scrapy.FormRequest(url=url, formdata=data, callback=self.parse)
#针对个人主页数据进行解析
def parse_second_page(self, response):
fp = open('second.html', 'w', encoding='utf8')
fp.write(response.text)
fp.close()
def parse(self, response):
#登录成功后的页面数据进行存储
fp = open('main.html', 'w', encoding='utf8')
fp.write(response.text)
#获取当前用户的个人主页
url = 'https://www.douban.com/people/18212345678/'
yield scrapy.Request(url=url,callback=self.parse_second_page)
fp.close()

十二、scrapy框架--代理的使用

原理:

#代理需要下载器的中间件,位于调度器和下载器之间

#拦截调度器发给下载器的请求,进行IP的更换

代码实现流程:

1.下载中间件类的自定义,重写process_request方法,拦截到请求后会调用该方法:

# 自定义一个下载中间件的类,在类中实现process_request方法
# 处理中间件拦截到的请求
class MyProxy(object):
def process_request(self, request, spider):
#进行请求IP的更换,IP为代理IP
request.meta['proxy'] = "http://120.76.77.152:9999"

2.配置文件中进行代理的开启

DOWNLOADER_MIDDLEWARES = {
'proxyPro.middlewares.MyProxy': 543,
}

十三、scrapy框架--日志等级

日志:运行爬虫时,会在控制台打印出一系列的信息

日志等级:
ERROR: 错误
WARNING: 警告
INFO: 一般信息
DEBUG: 调试信息(默认)
默认是输出调试信息。
如果只想让终端打印某个类型的信息,就在settings.py中添加LOG_LEVEL,
如果想将信息输出到文件就添加LOG_FILE:
LOG_LEVEL = 'ERROR'
LOG_FILE = 'log.txt'

十四、scrapy框架--请求传参

请求传参:解决的问题是爬取的数据不在同一个页面中
需求:将id97电影网站中电影详情数据进行爬取(名称,类型,导演,语言,片长)

=============爬虫文件=============

# -*- coding: utf-8 -*-
import scrapy
from moviePro.items import MovieproItem
class MovieSpider(scrapy.Spider):
name = 'movie'
start_urls = ['http://www.id97.com/movie/']
#用于解析二级子页面中的数据
def parse_by_second_page(self,response):
actor = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[1]/td[2]/a/text()').extract_first()
language = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[6]/td[2]/a/text()').extract_first()
long_time = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[8]/td[2]/a/text()').extract_first()

#取出Request方法的meta参数传递过来的字典(response.meta)
item = response.meta['item']
item['actor'] = actor
item['language'] = language
item['long_time'] = long_time
#将item提交给管道
yield item
def parse(self, response):
#名称,类型,导演,语言,片长
div_list = response.xpath('/html/body/div[1]/div[1]/div[2]/div')
for div in div_list:
name = div.xpath('.//div[@class="meta"]/h1/a/text()').extract_first()
kind = div.xpath('.//div[@class="otherinfo"]//text()').extract_first()
url = div.xpath('.//div[@class="meta"]/h1/a/@href').extract_first()

item = MovieproItem()
item['name'] = name
item['kind'] = kind
#要将其他数据存到item,就要进行请求传参(meta)

#需要对url发起请求,获取页面数据,进行指定数据解析,meta把item传给回调函数
#meta参数只能赋值给一个字典(将item对象先封装到字典)
yield scrapy.Request(url=url, callback=self.parse_by_second_page,meta={'item': item})

=============items文件============

import scrapy
class MovieproItem(scrapy.Item):
name = scrapy.Field()
kind = scrapy.Field()
actor = scrapy.Field()
language = scrapy.Field()
long_time = scrapy.Field()

十五、scrapy框架--CrawlSpider的使用

CrawlSpider的作用:
CrawlSpider会根据某一个提取规则提取全站所有链接,并提取链接的链接...一直到全部提取完。
问题:想对某个网站全站的数据进行爬取,例如爬取所有页码的数据
解决方案:
1.手动请求的发送, 递归
2.CrawlSpider(推荐)

CrawlSpider是Spider的一个子类。比Spider功能更强大(链接提取器,规则解析器)
1.创建一个基于CrawlSpider的爬虫文件:scrapy genspider -t crawl chouti dig.chouti.com/
2.使用方法:

==============爬虫文件=============

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class ChoutiSpider(CrawlSpider):
name = 'chouti'
start_urls = ['https://dig.chouti.com//']
# 实例化一个链接提取器对象
# 链接提取器用来提取指定的链接
# allow参数:赋值一个正则表达式
# 链接提取器可以根据正则表达式在页面中提取指定的链接
# 会提取满足正则表达式的所有,一般用来提取链接,提取到的链接会全部交给规则解析器
link = LinkExtractor(allow=r'/all/hot/recent/\d+')
rules = (
#实例化一个规则解析器对象
#规则解析器接收了链接提取器发送的链接后,就会对这些链接发起请求,获取链接对应的页面内容,然后根据指定的规则所有的页面数据进行解析
#callback:指定一个解析规则(方法/函数)
#fallow:表示是否将链接提取器继续作用到链接提取器提取出的页面数据中
#如果follow=True会产生大量重复链接,不影响,调度器会进行去重操作
Rule(link, callback='parse_item', follow=False),
)

def parse_item(self, response):
#链接提取器提取了多少个链接,parse_item方法就执行多少次
print(response)

十六、scrapy框架--分布式爬虫RedisCrawlSpider

分布式爬虫:
1.概念:多台机器上可以执行同一个爬虫程序,实现网站数据的分布式爬取
    A机器爬取一部分内容,B机器爬取一部分内容,最后整合
2.原生的scrapy不能实现分布式爬虫,因为--
    调度器和管道无法在A、B...多台机器共享,原生scrapy是五大组件配合进行网络数据的爬取
    A、B两个机器都有单独的五大组件,这样执行结束后,都下载了单独的一份数据,都存储了单独的一份数据

3.scrapy-redis组件:专门为scrapy开发的一套组件,该组件可以让scrapy实现分布式。
4.分布式爬取流程
  4.1*安装: pip install scrapy-redis
  4.2*配置redis.conf: 两个位置
      bind 127.0.0.1 #注释掉
      protected-mode no #yes改为no,关闭保护模式
  4.3*启动redis服务: ./redis-server ./redis-conf
  4.4*启动redis客户端: ./redis-cli
  4.5*创建工程: scrapy startproject redisPro
  4.6*创建CrawlSpider: scrapy genspider -t crawl qiubai www.qiushibaike.com/pic
  4.7*导入RedisCrawlSpider让爬虫类继承该RedisCrawlSpider
  4.8*将start_url修改成redis_key='xxx' #表示调度器队列名称
  4.9*编写parse_item
  4.10*将管道和调度器配置成基于scrapy-redis组件可以共享的:

USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
ROBOTSTXT_OBEY = False

ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400
}
# 使用scrapy-redis组件的去重队列
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 是否允许暂停,如果某台机器宕机,从宕机地方继续爬取
SCHEDULER_PERSIST = True
#如果redis服务器不在本机,需要进行如下配置
REDIS_HOST = '192.168.0.100' #本机的IP
REDIS_PORT = 6379
REDIS_PARAMS = {'password':'123456'}

  4.11*执行爬虫文件,进入爬虫文件所在目录:scrapy runspider qiubai.py
  4.12*启动redis客户端,将起始url扔进队列里:
          lpush redis-key 起始url
     lpush qiubai_spider https://www.qiushibaike.com/pic/
  4.13*在其他电脑上同样执行:
     scrapy runspider qiubai.py
     lpush qiubai_spider https://www.qiushibaike.com/pic/

 

 

需求:爬取糗百全站的图片链接保存在redis,https://www.qiushibaike.com/pic/

====爬虫文件=========

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import Rule
from scrapy_redis.spiders import RedisCrawlSpider
from redisPro.items import RedisproItem

class QiubaiSpider(RedisCrawlSpider):
name = 'qiubai'

# 表示跟start_url含义一样,表示是调度器队列的名称
redis_key = 'qiubai_spider'
link = LinkExtractor(allow=r'/pic/page/\d+')
rules = (
Rule(link, callback='parse_item', follow=True),
)

def parse_item(self, response):
div_list = response.xpath('//div[@id="content-left"]/div')
for div in div_list:
img_url = "https://"+div.xpath('.//div[@class="thumb"]/a/img/@src').extract_first()
item = RedisproItem()
item['img_url'] = img_url

# 使用分布式的管道
yield item

====items===========

import scrapy
class RedisproItem(scrapy.Item):
img_url = scrapy.Field()

====settings==========

# -*- coding: utf-8 -*-
BOT_NAME = 'redisPro'
SPIDER_MODULES = ['redisPro.spiders']
NEWSPIDER_MODULE = 'redisPro.spiders'
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
ROBOTSTXT_OBEY = False

ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400
}
# 使用scrapy-redis组件的去重队列
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 是否允许暂停,如果某台机器宕机,从宕机地方继续爬取
SCHEDULER_PERSIST = True
#如果redis服务器不在本机,需要进行如下配置
REDIS_HOST = '192.168.0.100' #本机的IP
REDIS_PORT = 6379

=====pipelines========默认

class RedisproPipeline(object):
def process_item(self, item, spider):
return item

 

十七、scrapy框架--分布式爬虫RedisSpider

先写Spider的代码,再修改为RedisSpider
基于RedisSpider实现的分布式爬虫(网易新闻)
a)代码修改(爬虫类)
  1.导包:from scrapy_redis.spiders import RedisSpider
  2.将爬虫类的父类修改为RedisSpider
  3.注释掉start_url,添加一个redis_key属性(调度器队列名称)
b)修改redis数据库配置文件redis.conf
  1.#bind 127.0.0.1
  2.protected-mode no
c)项目中settings.py进行配置
  ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 400
  }
  # 使用scrapy-redis组件的去重队列
  DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
  # 使用scrapy-redis组件自己的调度器
  SCHEDULER = "scrapy_redis.scheduler.Scheduler"
  # 是否允许暂停,如果某台机器宕机,从宕机地方继续爬取
  SCHEDULER_PERSIST = True
  #如果redis服务器不在本机,需要进行如下配置
  REDIS_HOST = '192.168.0.100' #本机的IP
  REDIS_PORT = 6379
  REDIS_PARAMS = {'password':'123456'}
d)启动redis服务端:
  命令:redis-server 配置文件
e)执行爬虫文件:
  进入到爬虫文件所在目录执行命令:scrapy runspider wangyi.py
f)向调度器的管道中扔一个起始url:
  1.开启redis客户端
  2.lpush wangyi https://news.163.com

十七、scrapy框架--selenium如何应用

selenium在scrapy中使用的原理分析:

  • 用selenium在中间件中拦截下载器提交给引擎的response对象
  • 对其进行篡改,用selenium发请求,将具有动态数据的响应对象替换原响应对象。

selenium在scrapy中的使用流程:

  • 重写爬虫文件的构造方法,在该方法中使用selenium实例化一个浏览器对象(因为浏览器对象只需要被实例化一次)
  • 重写爬虫文件的closed(self,spider)方法,在其内部关闭浏览器对象。该方法是在爬虫结束时被调用
  • 重写下载中间件的process_response方法,让该方法对响应对象进行拦截,并篡改response中存储的页面数据
  • 在配置文件中开启下载中间件

-爬虫文件:

# -*- coding: utf-8 -*-
import scrapy
from selenium import webdriver
class WangyiSpider(scrapy.Spider):
name = 'wangyi'
start_urls = ['https://news.163.com/']
def __init__(self):
#实例化浏览器对象(实例化一次)
self.bro = webdriver.Chrome(executable_path=r'C:\Users\Administrator\Downloads\chromedriver.exe')
def closed(self, spider):
     #必须在整个爬虫结束后,关闭浏览器
self.bro.quit()

-中间件文件:

from scrapy.http import HtmlResponse    
#参数介绍:
#拦截到响应对象(下载器传递给Spider的响应对象)
#request:响应对象对应的请求对象
#response:拦截到的响应对象
#spider:爬虫文件中对应的爬虫类的实例
def process_response(self, request, response, spider):
#响应对象中存储页面数据的篡改
if request.url in['http://news.163.com/domestic/','http://news.163.com/world/','http://news.163.com/air/','http://war.163.com/']:
spider.bro.get(url=request.url)
js = 'window.scrollTo(0,document.body.scrollHeight)'
spider.bro.execute_script(js)
time.sleep(2) #一定要给与浏览器一定的缓冲加载数据的时间
#页面数据就是包含了动态加载出来的新闻数据对应的页面数据
page_text = spider.bro.page_source
#篡改响应对象
return HtmlResponse(url=spider.bro.current_url,body=page_text,encoding='utf-8',request=request)
else:
return response

-配置文件:

DOWNLOADER_MIDDLEWARES = {
'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,

}

十八、scrapy框架--UA池、代理池

原理:

  • 使用下载中间件处理请求,对请求设置随机的User-Agent 或者设置随机的代理。
  • 目的在于防止爬取网站的反爬虫策略。

操作流程:

  1. 在下载中间件中拦截请求
  2. 将拦截到的请求的请求头信息中的UA进行篡改伪装  或者  将拦截到的请求的IP修改成某一代理IP
  3. 在配置文件中开启下载中间件

代码展示UA池:

#导包
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
import random
#UA池代码的编写(单独给UA池封装一个下载中间件的一个类)
class RandomUserAgent(UserAgentMiddleware):
def process_request(self, request, spider):
#从列表中随机抽选出一个ua值
ua = random.choice(user_agent_list)
#ua值进行当前拦截到请求的ua的写入操作
request.headers.setdefault('User-Agent',ua)

user_agent_list = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "
"(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 "
"(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 "
"(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 "
"(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 "
"(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 "
"(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 "
"(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
]

代码展示代理池:

#批量对拦截到的请求进行ip更换
#单独封装下载中间件类
class Proxy(object):
def process_request(self, request, spider):
#对拦截到请求的url进行判断(协议头到底是http还是https)
#request.url返回值:http://www.xxx.com
h = request.url.split(':')[0] #请求的协议头
if h == 'https':
ip = random.choice(PROXY_https)
request.meta['proxy'] = 'https://'+ip
else:
ip = random.choice(PROXY_http)
request.meta['proxy'] = 'http://' + ip

#可被选用的代理IP
PROXY_http = [
'153.180.102.104:80',
'195.208.131.189:56055',
]
PROXY_https = [
'120.83.49.90:9000',
'95.189.112.214:35508',
]

十九、scrapy框架--综合应用(爬网易新闻)

需求:

  • 爬取网易新闻https://news.163.com/基于文字的新闻数据(“国内”、”国际“、”军事“、”航空“四个板块)

用到的知识点:

  1. 请求的手动发送
  2. 请求传参
  3. xpath解析
  4. UA池,代理池
  5. selenium在scrapy中的应用

代码展示

--爬虫类:

# -*- coding: utf-8 -*-
import scrapy
from selenium import webdriver
from wangyiPro.items import WangyiproItem
class WangyiSpider(scrapy.Spider):
name = 'wangyi'
start_urls = ['https://news.163.com/']

def __init__(self):
#实例化浏览器对象
self.bro = webdriver.Chrome(executable_path=r'C:\Users\Administrator\Downloads\chromedriver.exe')
def closed(self, spider):
self.bro.quit()
def parse(self, response):
#获取四个板块的a标签href属性
lis = response.xpath('//div[@class="ns_area list"]/ul/li')
indexs = [3,4,6,7]
li_list = [] #存储的四个板块的li标签对象
for index in indexs:
li_list.append(lis[index])
#获取四个板块中的超链接和文字标题
for li in li_list:
url = li.xpath('./a/@href').extract_first()
title = li.xpath('./a/text()').extract_first()
#对每一个板块的url发请求,获取页面数据(标题,缩略图,关键字,发布时间,url)
yield scrapy.Request(url=url, callback=self.parse_second, meta={'title': title})
def parse_second(self, response):
#这个div_list长度为0,xpath表达式没有问题,是动态加载的数据发送的请求拿不到。
#所以要用selenium去拿数据
div_list = response.xpath('//div[@class="data_row news_article clearfix "]')
for div in div_list:
#文章标题
head = div.xpath('.//div[@class="news_title"]/h3/a/text()').extract_first()
#文章超链接
url = div.xpath('.//div[@class="news_title"]/h3/a/@href').extract_first()
#缩略图
img_url = div.xpath('./a/img/@src').extract_first()
#关键字
tag_list = div.xpath('.//div[@class="news_tag"]//text()').extract()
tags = "".join([tag.strip() for tag in tag_list])

item = WangyiproItem()
title = response.meta['title']
item['head'] = head
item['url'] = url
item['img_url'] = img_url
item['tag'] = tags
item['title'] = title
# 对url发请求,获取页面中存储的新闻内容
yield scrapy.Request(url=url, callback=self.get_content, meta={'item': item})

def get_content(self, response):
#获取请求传参的item
item = response.meta['item']
#解析出新闻内容
content_list = response.xpath('//div[@class="post_text"]/p/text()').extract()
content = "".join(content_list)
item['content'] = content

yield item

--items:

import scrapy
class WangyiproItem(scrapy.Item):
head = scrapy.Field()
url = scrapy.Field()
img_url = scrapy.Field()
tag = scrapy.Field()
title = scrapy.Field()
content = scrapy.Field()

--管道:

class WangyiproPipeline(object):
def process_item(self, item, spider):
print("title:"+item['title']+"|content:"+item['content'])
return item

--中间件:

# -*- coding: utf-8 -*-

from scrapy import signals
import time
from scrapy.http import HtmlResponse
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
import random
#单独给UA池封装一个下载中间件的类
class RandomUserAgent(UserAgentMiddleware):
def process_request(self, request, spider):
ua = random.choice(user_agent_list)
request.headers.setdefault('User-Agent',ua)
#单独给代理池封装一个下载中间件的类
class Proxy(object):
def process_request(self, request, spider):
#判断协议头是http还是https的
h = request.url.split(':')[0]
if h=='https':
ip = random.choice(proxies_https)
request.meta['proxy'] = "https://"+ip
else:
ip = random.choice(proxies_http)
request.meta['proxy'] = "http://" + ip
class WangyiproDownloaderMiddleware(object):
def process_request(self, request, spider):
return None

#拦截响应:拦截下载器传给spider的响应对象
#request:响应对象所对应的请求对象
#response:拦截到的响应对象
#spider:爬虫文件中对应的爬虫类实例
def process_response(self, request, response, spider):
#需要篡改response,使其包括动态加载的新闻内容
if request.url in ['http://news.163.com/domestic/','http://news.163.com/world/',
'http://war.163.com/','http://news.163.com/air/']:
spider.bro.get(url=request.url)
js = 'window.scrollTo(0, document.body.scrollHeight)'
spider.bro.execute_script(js)
time.sleep(1)
page_text = spider.bro.page_source
return HtmlResponse(url=spider.bro.current_url, body=page_text, encoding='utf-8', request=request)
else:
return response

def process_exception(self, request, exception, spider):
pass

def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)

user_agent_list = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "
"(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 "
"(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 "
"(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 "
"(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 "
"(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 "
"(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 "
"(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
]
proxies_http=[
'173.82.219.113:3128',
'92.243.6.37:80',
]
proxies_https=[
'117.102.96.59:8080',
'213.234.28.94:8080',
'101.51.123.88:8080',
'158.58.131.214:41258',
]

--settings:

# -*- coding: utf-8 -*-
BOT_NAME = 'wangyiPro'
SPIDER_MODULES = ['wangyiPro.spiders']
NEWSPIDER_MODULE = 'wangyiPro.spiders'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

DOWNLOADER_MIDDLEWARES = {
'wangyiPro.middlewares.Proxy': 542, #代理池
'wangyiPro.middlewares.RandomUserAgent': 542, #UA池
'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543, #selenium拦截


}

二十、scrapy框架--总结

1.接触过几种爬虫模块

  • urllib
  • requests

2.robots协议是什么

  • requests模块没有使用硬性的语法对该协议进行生效

3.如何处理验证码

  • 云打码平台
  • 打码兔

4.掌握几种数据解析的方式

  • 正则
  • xpath
  • bs4

5.如何爬取动态加载的页面数据

  • selenium
  • ajax请求

6.接触过哪些反爬机制?如何处理?

  • robots协议
  • UA
  • 封IP
  • 验证码
  • 动态数据爬取
  • 数据加密(挨着试解密方法)
  • token(前台页面中查找)

7.在scrapy中接触过几种爬虫的类

  • Spider
  • CrawlSpider
  • RedisCrawlSpider
  • RedisSpider

8.如何实现分布式流程

  1. pip3 install scrapy-redis
  2. RedisCrawlSpider
  3. RedisSpider
posted on 2019-04-25 15:03 要一直走下去 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/staff/p/10768497.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值