scrapy的使用

作用概括:

  1. Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛。
  2. 框架的力量,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片,非常之方便。
  3. Scrapy 使用了 Twisted['twɪstɪd] 异步网络框架来处理网络通讯,可以加快我们的下载速度,不用自己去实现异步框架,并且包含了各种中间件接口,可以灵活的完成各种需求。
    补充知识:

各个模块的主要作用:

scrapy Engine引擎:

负责spider, itemPipeline,Downloader,Scheduler中间的通讯信号数据传递等

scheduler调度器:

负责接受引擎发送来的request请求 存放url

Downloader(下载器):

负责接受引擎从调度器中拿到的url并下载html获得请求体

spiser(爬虫):

  1. 负责接受引擎从下载器中拿到的请求体来提取我们需要的数据
  2. 提取新的url给调度器

ltem Pipeline(管道):

负责接受引擎从spider中提取到的数据来对其进行过滤存储等

下载中间件:自定义下载组件(请求任务和响应结果都会经过下载中间件)
代理中间件,cookies中间件,User—Agent中间件,selenium中间件

爬虫中间件:可以自定义request请求和过滤Response响应

1. 创建一个爬虫项目:scrapy startproject 项目名称

创建爬虫文件:cd spider 文件夹下
scrapy genspider 爬虫名称 域
框架内部的py文件以及作用:
spiders 文件夹下存放的爬虫文件
items.py :根据目标文件,提取有用的字段
pipeline.py:做数据的过滤和持久化
middkeware.py :下载中间件和爬虫中间件
settings.py:设置文件,可以在里面做User—Agen,headers,激活管道
scrapy.cfg:配置文件,一般情况下,做爬虫部署会用到

Scrapy Spider文件介绍

Spider类定义了如何爬取某个(或某些)网站。包括了爬取的动作(例如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item)。 换句话说,Spider就是您定义爬取的动作及分析某个网页(或者是有些网页)的地方。
class scrapy.Spider是最基本的类,所有编写的爬虫必须继承这个类。
主要用到的函数及调用顺序为:
init() : 初始化爬虫名字和start_urls列表
start_requests() 调用make_requests_from url():生成Requests对象交给Scrapy下载并返回response
parse():
解析response,并返回Item或Requests(需指定回调函数)。
Item传给Item pipline持久化 , 而Requests交由Scrapy下载,并由指定的回调函数处理(默认parse()),一直进行循环,直到处理完所有的数据为止。

Scrapy Item pipeline(管道文件)使用

Item Pipeline 当Item在Spider中被收集之后,它将会被传递到Item Pipeline,这些Item Pipeline组件按定义的顺序处理Item。

每个Item Pipeline都是实现了简单方法的Python类,比如决定此Item是丢弃而存储。以下是item pipeline的一些典型应用:
验证爬取的数据(检查item包含某些字段,比如说name字段)
查重(并丢弃)
将爬取结果保存到文件或者数据库中
编写item pipeline
编写item pipeline很简单,item pipiline组件是一个独立的Python类,其中process_item()方法必须实现:
启用一个Item Pipeline组件 为了启用Item Pipeline组件,必须将它的类添加到 settings.py文件ITEM_PIPELINES 配置,就像下面这个例子:
Configure item pipelines

ITEM_PIPELINES = {
#'mySpider.pipelines.SomePipeline': 300,
"mySpider.pipelines.JobboleprojectPipeline":300}

分配给每个类的整型值,确定了他们运行的顺序,item按数字从低到高的顺序,通过pipeline,通常将这些数字定义在0-1000范围内(0-1000随意设置,数值越低,组件的优先级越高)

去重 一个用于去重的过滤器,丢弃那些已经被处理过的item。让我们假设我们的item有一个唯一的id,但是我们spider返回的多个item中包含有相同的id:

将数据保存在数据库(Mongodb,Mysql)

items.py文件:自定义字段,确定要爬取的目标网站数据

import scrapy
class DoubanItem(scrapy.Item):
    # 标题
    title = scrapy.Field()
    # 是否可播放的状态
    playable = scrapy.Field()
    # 简介
    content = scrapy.Field()
    # 评分
    star = scrapy.Field()
    # 评论数量
    commentnum = scrapy.Field()
    # 主题
    inq = scrapy.Field()

spiders/douban.py 文件: 爬虫文件,在这里编写爬虫代码,解析数据,导入items.py 文件定义的类(class),并创建这个类的对象,
然后将解析的数据,赋值给类对象的属性,
然后yield 返回这个类对象,

这样这个数据就可以在pipeline.py 文件中数据持久化了(注意使用,pipeline时侯需要激活setting.py文件的pipline设置,)

scrapy 图片下载

第一种方式:正常发起请求 获取二进制文件,保存
第二种方式:自定义图片管道 继承自ImagePipeline
重写两个方法
def get_media_request(self,item,spider,...)
获取图片地址,发起请求
def item_completed(self,results,sipders...):
获取在results响应结果中根据图片下载状态 获取图片本地存储的路径,
将获取到的路径赋值给item,然后将item返给其他管道

数据持久化(一定要激活管道)

1.可以自定义数据管道


def __init__(self,xxx,xxx...):
    可以设置一些参数(比如,创建数据库链接,打开文件等等)

@classmethod
def from_crawler(cls,clawler):
    #clawler:包含了爬虫的一些核心组件
    # 可以获取settings中的一些设置参数
    return cls

def open_spider(self,item,spider):
    # 可选方法,在爬虫开启时会调用
def close_spider(self,item,spider):
    # 可选方法 在爬虫结束后会调用

# item肯定会经过
def process_item(self,item,spider):
    # 所有的item都会经过这个方法
    # 在这里做数据持久化(pymongo,pymysql)

(多个表时) 如果items.py 文件有多个class 类时,我们在做数据持久化时可以

方法一:(判断item的对象)
if isinstance(item,类名
elif isinstance(item,类名)

方法二: 
        1. 一般在item对应的类中,我们定义一个方法,返回sql语句和要插入的数据
        2. 在管道中使用item调用这个方法,得到sql语句和要插入的数据
        3. 执行插入操作

    #如果要将item传给下一个管道一定要return item

scrapy通用爬虫:

通用爬虫的py爬虫文件定义的类是继承(CrawlSpider)
普通爬虫继承(scrapy.Spider)
通过下面的命令可以快速创建 CrawlSpider模板 的代码:
scrapy genspider -t crawl 爬虫文件 域名

上一个案例中,我们通过xpath,制作了新的url作为Request请求参数,现在我们可以换个花样...
class scrapy.spiders.CrawlSpider
它是Spider的派生类,Spider类的设计原则是只爬取start_url列表中的网页,而CrawlSpider类定义了一些规则Rule来提供跟进链接的方便的机制,从爬取的网页结果中获取链接并继续爬取的工作

CrawlSpider继承于Spider类,除了继承过来的属性外(name、allow_domains),还提供了新的属性和方法:

rules
CrawlSpider使用rules属性来决定爬虫的爬取规则,并将匹配后的url请求提交给引擎,完成后续的爬取工作。
在rules中包含一个或多个Rule对象,每个Rule对爬取网站的动作定义了某种特定操作,比如提取当前相应内容里的特定链接,是否对提取的链接跟进爬取,对提交的请求设置回调函数等。
注意:
如果多个rule匹配了相同的链接,则根据规则在本集合中被定义的顺序,第一个会被使用。
class scrapy.spiders.Rule(
link_extractor,
callback = None,
cb_kwargs = None,
follow = None,
process_links = None,
process_request = None
)

● link_extractor:是一个Link Extractor对象,用于定义需要提取的链接。
● callback: 从link_extractor中每获取到链接得到Responses时,会调用参数所指定的值作为回调函数,该回调函数接收一个response作为其一个参数。
● follow:是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。如果callback为None,follow 默认设置为True ,否则默认为False。
● process_links:指定spider中哪个的函数将会被调用,从link_extractor中获取到链接列表时将会调用该函数。该方法主要用来过滤。
● process_request:指定处理函数,根据该Rule提取到的每个Request时,该函数将会被调用,可以对Request进行处理,该函数必须返回Request或者None
注意:当编写爬虫规则时,避免使用parse作为回调函数。由于CrawlSpider使用parse方法来实现其逻辑,如果覆盖了 parse方法,crawl spider将会运行失败

千万记住 callback 千万不能写 parse,再次强调:由于CrawlSpider使用parse方法来实现其逻辑,如果覆盖了 parse方法,crawl spider将会运行失败。

LinkExtractors
class scrapy.linkextractors.LinkExtractor

Link Extractors 的目的很简单: 提取链接。
每个LinkExtractor有唯一的公共方法是 extract_links(),它接收一个 Response 对象,并返回一个 scrapy.link.Link 对象。
Link Extractors要实例化一次,并且 extract_links 方法会根据不同的 response 调用多次提取链接。

主要参数:

● allow:满足括号中“正则表达式”的URL会被提取,如果为空,则全部匹配。
● deny:满足括号中“正则表达式”的URL一定不提取(优先级高于allow)。
● allow_domains:会提取的链接的domains。
● deny_domains:一定不会被提取链接的domains。
● restrict_xpaths:使用xpath表达式,和allow共同作用过滤链接。

Scrapy Shell

Scrapy终端是一个交互终端,我们可以在未启动spider的情况下尝试及调试代码,也可以用来测试XPath或CSS表达式,查看他们的工作方式,方便我们爬取的网页中提取的数据。
如果安装了 IPython ,Scrapy终端将使用 IPython (替代标准Python终端)。 IPython 终端与其他相比更为强大,提供智能的自动补全,高亮输出,及其他特性。(推荐安装IPython)
启动Scrapy Shell
scrapy shell "http://hr.tencent.com/position.php?&start=0#a"

scrapy shell -s USER_AGENT="Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36" "http://hr.tencent.com/position.php?&start=0#a"
Scrapy Shell根据下载的页面会自动创建一些方便使用的对象,例如 Response 对象,以及 Selector 对象 (对HTML及XML内容)。

当shell载入后,将得到一个包含response数据的本地 response 变量,输入 response.body将输出response的包体,输出 response.headers 可以看到response的包头。

输入 response.selector 时, 将获取到一个response 初始化的类 Selector 的对象,此时可以通过使用 response.selector.xpath()或response.selector.css() 来对 response 进行查询。

Scrapy也提供了一些快捷方式, 例如 response.xpath()或response.css()同样可以生效(如之前的案例)。

Selectors选择器 Scrapy Selectors 内置 XPath 和 CSS Selector 表达式机制 Selector有四个基本的方法,最常用的还是xpath:

xpath(): 传入xpath表达式,返回该表达式所对应的所有节点的selector list列表
extract(): 序列化该节点为字符串并返回list
css(): 传入CSS表达式,返回该表达式所对应的所有节点的selector list列表,语法同 BeautifulSoup4
re(): 根据传入的正则表达式对数据进行提取,返回字符串list列表
尝试使用Selector

我们用腾讯社招的网站http://hr.tencent.com/position.php?&start=0#a举例:

返回 xpath选择器对象列表

使用 extract()方法返回 Unicode字符串列表

打印列表第一个元素,终端编码格式显示

返回 xpath选择器对象列表

返回列表第一个元素的Unicode字符串

职位名称:

职位名称详情页:

职位类别:

以后做数据提取的时候,可以把现在Scrapy Shell中测试,测试通过后再应用到代码中。

当然Scrapy 的commend不仅仅如此,但是不属于我们课程重点,不做详细介绍。 例如:

scrapy -h 查看所有可用的命令:
scrapy view -h 查看view命令的详细内容:
scrapy list列出当前项目中所有可用的spider
scrapy runspider xxxx.py在未创建项目的情况下,运行一个编写在Python文件中的spider。
scrapy version输出Scrapy版本

Request

Request 部分源码

# 部分代码
class Request(object_ref):

    def __init__(self, url, callback=None, method='GET', headers=None, body=None, 
                 cookies=None, meta=None, encoding='utf-8', priority=0,
                 dont_filter=False, errback=None):

        self._encoding = encoding  # this one has to be set first
        self.method = str(method).upper()
        self._set_url(url)
        self._set_body(body)
        assert isinstance(priority, int), "Request priority not an integer: %r" % priority
        self.priority = priority

        assert callback or not errback, "Cannot use errback without a callback"
        self.callback = callback
        self.errback = errback

        self.cookies = cookies or {}
        self.headers = Headers(headers or {}, encoding=encoding)
        self.dont_filter = dont_filter

        self._meta = dict(meta) if meta else None

    @property
    def meta(self):
        if self._meta is None:
            self._meta = {}
        return self._meta

其中,比较常用的参数:

  • url: 就是需要请求,并进行下一步处理的url

  • callback: 指定该请求返回的Response,由那个函数来处理。

  • method: 请求一般不需要指定,默认GET方法,可设置为"GET", "POST", "PUT"等,且保证字符串大写

  • headers: 请求时,包含的头文件。一般不需要。内容一般如下: # 自己写过爬虫的肯定知道 Host: media.readthedocs.org User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0 Accept: text/css,/;q=0.1 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Referer: http://scrapy-chs.readthedocs.org/zh_CN/0.24/ Cookie: _ga=GA1.2.1612165614.1415584110; Connection: keep-alive If-Modified-Since: Mon, 25 Aug 2014 21:59:35 GMT Cache-Control: max-age=0

  • meta: 比较常用,在不同的请求之间传递数据使用的。字典dict型

      request_with_cookies = Request(
          url="http://www.example.com",
          cookies={'currency': 'USD', 'country': 'UY'},
          meta={'dont_merge_cookies': True}
      )
    
    
  • encoding: 使用默认的 'utf-8' 就行。

  • dont_filter: 表明该请求不由调度器过滤。这是当你想使用多次执行相同的
    请求,忽略重复的过滤器。默认为False。

  • errback: 指定错误处理函数

Response

# 部分代码
class Response(object_ref):
    def __init__(self, url, status=200, headers=None, body='', flags=None, request=None):
        self.headers = Headers(headers or {})
        self.status = int(status)
        self._set_body(body)
        self._set_url(url)
        self.request = request
        self.flags = [] if flags is None else list(flags)

    @property
    def meta(self):
        try:
            return self.request.meta
        except AttributeError:
            raise AttributeError("Response.meta not available, this response " \
                "is not tied to any request")

大部分参数和上面的差不多:

  • status: 响应码
  • _set_body(body): 响应体
  • _set_url(url):响应url
  • self.request = request (request对象)
  • self.headers = Headers (响应头)

发送POST请求

可以使用

yield scrapy.FormRequest(url, formdata, callback)

方法发送POST请求。

如果希望程序执行一开始就发送POST请求,可以重写Spider类的start_requests(self) 方法,并且不再调用start_urls里的url。

class mySpider(scrapy.Spider):
    # start_urls = ["http://www.example.com/"]

    def start_requests(self):
        url = 'http://www.renren.com/PLogin.do'

        # FormRequest 是Scrapy发送POST请求的方法
        yield scrapy.FormRequest(
            url = url,
            formdata = {"email" : "mr_mao_hacker@163.com", "password" : "axxxxxxxe"},
            callback = self.parse_page
        )

    def parse_page(self, response):
        # do something
        pass

Scrapy 的暂停和恢复:

有些情况下,例如爬取大的站点,我们希望能暂停爬取,之后再恢复运行。
Scrapy通过如下工具支持这个功能:
一个把调度请求保存在磁盘的调度器
一个把访问请求保存在磁盘的副本过滤器[duplicates filter]
一个能持续保持爬虫状态(键/值对)的扩展

Job 路径:
要启用持久化支持,你只需要通过 JOBDIR 设置 job directory 选项。
这个路径将会存储 所有的请求数据来保持一个单独任务的状态(例如:一次
spider爬取(a spider run))。必须要注意的是,这个目录不允许被不同的
spider 共享,甚至是同一个spider的不同jobs/runs也不行。也就是说,
这个目录就是存储一个 单独 job的状态信息。

怎么使用??? 要启用一个爬虫的持久化,运行以下命令:

反反爬虫相关机制

(有些些网站使用特定的不同程度的复杂性规则防止爬虫访问,绕过这些规则是困难和复杂的,有时可能需要特殊的设置)

Scrapy官方文档描述:http://doc.scrapy.org/en/master/topics/practices.html#avoiding-getting-banned

通常防止爬虫被反主要有以下几个策略:

  • 动态设置User-Agent(随机切换User-Agent,模拟不同用户的浏览器信息)

  • 禁用Cookies(也就是不启用cookies middleware,不向Server发送cookies,有些网站通过cookie的使用发现爬虫行为)

    • 使用cookies池,自定义中间件

    • 除非特殊需要,否则禁用cookies,防止某些网站根据Cookie来封锁爬虫。

COOKIES_ENABLED = False

  • 设置延迟下载(防止访问过于频繁,设置为 2秒 或更高)
DOWNLOAD_DELAY = 2

  • 使用IP代理地址池:VPN和代理IP,现在大部分网站都是根据IP来ban的。

使用 Crawlera(专用于爬虫的代理组件),正确配置和设置下载中间件后,项目所有的request都是通过crawlera发出。 官方网站:https://scrapinghub.com/crawlera 参考网站:https://www.aliyun.com/jiaocheng/481939.html

  DOWNLOADER_MIDDLEWARES = {
      'scrapy_crawlera.CrawleraMiddleware': 600
  }

  CRAWLERA_ENABLED = True
  CRAWLERA_USER = '注册/购买的UserKey'
  CRAWLERA_PASS = '注册/购买的Password'

自定义下载中间件(Downloader Middlewares)

下载中间件是处于引擎(crawler.engine)和下载器(crawler.engine.download())之间的一层组件,可以用来修改Request和Response。

  1. 当引擎传递请求(Request)给下载器的过程中,下载中间件可以对请求进行处理 (例如增加http header信息(User-Agent),增加proxy代理等);

  2. 在下载器完成http请求,传递响应给引擎的过程中, 下载中间件可以对响应进行处理

要激活下载器中间件组件,将其加入到 DOWNLOADER_MIDDLEWARES 设置中。 该设置是一个字典(dict),键为中间件类的路径,值为其中间件的优先级,值越低,代表优先级越高。

这里是一个例子:

DOWNLOADER_MIDDLEWARES = {
    'mySpider.middlewares.RandomUserAgentDownloaderMiddleware': 543,
}

每个中间件组件是一个定义了以下一个或多个方法

process_request(self, request, spider)

  • 当每个request对象通过下载中间件时该方法被调用。

  • process_request() 必须返回以下其中之一:NoneResponse 对象Request 对象IgnoreRequest异常:

    • 如果返回 None: Scrapy将继续处理该request,执行其他的中间件的相应方法。

    • 如果返回 Response 对象: Scrapy将不会调用任何其他的中间件的 process_request()或相应地下载函数; 直接返回该response对象。 已激活的中间件的 process_response() 方法则会在每个response返回时被调用。

    • 如果返回 Request 对象,Scrapy则停止调用其他中间件的process_request方法,并重新将返回的request对象放置到调度器等待下载。

    • 如果返回raise IgnoreRequest 异常:下载中间件的 process_exception() 方法会被调用。如果没有捕获该异常, 则request设置的errback(Request.errback)方法会被调用。如果也没有设置异常回调,则该异常被忽略且不记录到日志文件。

process_request()有两个参数:

  • request (Request 对象) – 处理的request
  • spider (Spider 对象) – 该request对应的spider

process_response(self, request, response, spider)

当下载器完成http请求,传递Response给引擎的时调用

  • process_response() 必须返回以下其中之一:

      **Response 对象**
      **Request 对象**
      **IgnoreRequest异常**。
    
    
    • 如果返回 Request: 更低优先级的下载中间件的process_response方法不会继续调用,该Request会被重新放到调度器任务队列中等待调度,相当于一个新的Request。

    • 如果返回 Response 对象,更低优先级的下载中间件的process_response方法会继续调用对Response对象进行处理

    • 如果抛出 IgnoreRequest 异常,则调用request设置的errback(Request.errback)函数。 如果异常没有被处理,则该异常被忽略且不记录。

process_response()有三个参数:

  • request (Request 对象) – response所对应的request
  • response (Response 对象) – 被处理的response
  • spider (Spider 对象) – response所对应的spider

自定义中间件代码实例

1.设置代理中间件。

Scrapy代理IP、Uesr-Agent的切换都是通过DOWNLOADER_MIDDLEWARES进行控制,我们在settings.py同级目录下创建middlewares.py文件,包装所有请求。

  • 添加代理IP设置PROXIES:

免费代理IP可以网上搜索,或者付费购买一批可用的私密代理IP:

PROXIES = [
    {'ip_port': '111.8.60.9:8123', 'user_pwd': 'user1:pass1'},
    {'ip_port': '101.71.27.120:80', 'user_pwd': 'user2:pass2'},
    {'ip_port': '122.96.59.104:80', 'user_pwd': None},
    {'ip_port': '122.224.249.122:8088', 'user_pwd': None},
]

# middlewares.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import random
import base64

#定义一个代理的中间件
import base64
class RandomProxyMiddleware(object):
    def __init__(self,proxies):

        self.proxies = proxies

    @classmethod
    def from_crawler(cls, crawler):

        proxies = crawler.settings['PROXIES']
        return cls(proxies)

    def process_request(self, request, spider):

        proxy = random.choice(self.proxies)
        if proxy['user_pwd'] is None:
            # 没有代理账户验证的代理使用方式
            request.meta['proxy'] = proxy['ip_port']
        else:
            #对账户密码进行base64编码
            user_pwd = base64.b64encode(proxy['user_pwd'].encode('utf-8')).decode('utf-8')
            #对应到代理服务器的信令格式里
            request.headers['Proxy-Authorization'] = 'Basic ' + user_pwd
            request.meta['proxy'] = proxy['ip_port']

为什么HTTP代理要使用base64编码:

HTTP代理的原理很简单,就是通过HTTP协议与代理服务器建立连接,协议信令中包含要连接到的远程主机的IP和端口号,如果有需要身份验证的话还需要加上授权信息,服务器收到信令后首先进行身份验证,通过后便与远程主机建立连接,连接成功之后会返回给客户端200,表示验证通过,就这么简单,下面是具体的信令格式:

CONNECT 59.64.128.198:21 HTTP/1.1
Host: 59.64.128.198:21
Proxy-Authorization: Basic bGV2I1TU5OTIz
User-Agent: OpenFetion

其中Proxy-Authorization是身份验证信息,Basic后面的字符串是用户名和密码组合后进行base64编码的结果,也就是对username:password进行base64编码。

HTTP/1.0 200 Connection established

客户端收到收面的信令后表示成功建立连接,接下来要发送给远程主机的数据就可以发送给代理服务器了,代理服务器建立连接后会在根据IP地址和端口号对应的连接放入缓存,收到信令后再根据IP地址和端口号从缓存中找到对应的连接,将数据通过该连接转发出去。

2. 设置User-Agent中间件。

  • 在settings.py文件中添加USER_AGENTS:
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",
]

方式1

# 自定义UserAgent中间件
class RandomUserAgent(object):
    def __init__(self, useragents):
        self.useragents = useragents

    @classmethod
    def from_crawler(cls, crawler):
        #crawler.settings['USERAGENTS'] 获取settings.py中的UA池
        USERAGENTS = crawler.settings['USERAGENTS']
        return cls(USERAGENTS)

    def process_request(self, request, spider):
        user_agent = random.choice(self.useragents)
        print('执行下载中间件'+user_agent)
        if user_agent:
            request.headers.setdefault(b'User-Agent', user_agent)
            # request.headers['User-Agent'] = user_agent

方式2

# 使用第三方插件来随机获取User-Agent
from fake_useragent import UserAgent
class RandomUserAgentMiddlewareTwo(object):

    def process_request(self, request, spider):
        user_agent = UserAgent().random
        print('执行下载中间件'+user_agent)
        if user_agent:
            request.headers.setdefault(b'User-Agent', user_agent)
            # request.headers['User-Agent'] = user_agent

最后设置setting.py里的DOWNLOADER_MIDDLEWARES,激活自己编写的下载中间件类

DOWNLOADER_MIDDLEWARES = {
    #'mySpider.middlewares.MyCustomDownloaderMiddleware': 543,
    'mySpider.middlewares.RandomUserAgent': 99,
    'mySpider.middlewares.ProxyMiddleware': 100
}

3. 设置cookies中间件。

import random
class RandomCookiesMiddleware(object):
    def __init__(self,cookies):
        self.cookies = cookies

    @classmethod
    def from_crawler(cls, crawler):
        #从设置文件中获取cookies列表
        #也可以从数据库中取
        cookies = crawler.settings['COOKIES']
        return cls(cookies)

    def process_request(self,request,spider):
        #随机获取一个cookies
        cookie = random.choice(self.cookies)
        if cookie:
            request.cookies = cookie

4. 设置selenium中间件。

from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from scrapy.http import HtmlResponse

class SeleniumMiddleware(object):
    def __init__(self):

        self.drive = webdriver.Chrome(executable_path='')
        self.drive.set_page_load_timeout(10)

    def process_request(self,request,spider):
        try:
            url = request.url
            self.drive.get(url)
            if self.drive.page_source:
                return HtmlResponse(url=url,body=self.drive.page_source,status=200,encoding='utf-8',request=request)
        except TimeoutException:
            print('请求超时')
            return HtmlResponse(url=url,body=None,status=500)

设置setting

BOT_NAME = 'midwareproject'

# 爬虫的文件目录
SPIDER_MODULES = ['midwareproject.spiders']
NEWSPIDER_MODULE = 'midwareproject.spiders'


# Crawl responsibly by identifying yourself (and your website) on the user-agent

# 用户代理,模拟浏览器发起请求
#USER_AGENT = 'midwareproject (+http://www.yourdomain.com)'

# Obey robots.txt rules
# 是否遵守robot协议,默认为True遵守协议
# 我们通常设置为False 不遵守协议

ROBOTSTXT_OBEY = False

# Configure maximum concurrent requests performed by Scrapy (default: 16)

# 下载器允许发起请求的最大并发数 默认是 16
# CONCURRENT_REQUESTS = 32

# Configure a delay for requests for the same website (default: 0)
# See https://doc.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs

# 下载延时 ,上一个请求与下一个请求的时间间隔
DOWNLOAD_DELAY = 2
# The download delay setting will honor only one of:

# 在某个域下允许最大并发请求数量 ,默认八个
#CONCURRENT_REQUESTS_PER_DOMAIN = 16

# 对单个ip下允许最大并发请求数量,默认为零(不限制)
# 1。如果非0 ,#CONCURRENT_REQUESTS_PER_DOMAIN = 16 的设定不会生效 ,并发的限制针对 ip
#     2。如果非0 ,延时不再是针对网站,而是针对ip
#CONCURRENT_REQUESTS_PER_IP = 16

# Disable cookies (enabled by default)

# 针对cookies的设置,一般不携带cookies,也是反反爬的手段,默认是True
COOKIES_ENABLED = False

# Disable Telnet Console (enabled by default)

# 是一个telent终端的扩展插件。它能够打印日志信息,监听爬虫的状态信息
# TELNETCONSOLE_ENABLED = False

# Override the default request headers:

# 默认的请求头,(是全局的)
#DEFAULT_REQUEST_HEADERS = {
#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#   'Accept-Language': 'en',
#}

# Enable or disable spider middlewares
# See https://doc.scrapy.org/en/latest/topics/spider-middleware.html
# 爬虫中间件。我们可以自定义爬虫中间件
SPIDER_MIDDLEWARES = {
   # 'midwareproject.middlewares.MidwareprojectSpiderMiddleware': 543,
    'midwareproject.middlewares.RandomProxiesDownloadMiddlerware':222,
}

# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html

# 下载中间件,可以自定义下载中间件,后面的数字表是优先极,数字越小,优先级越高
DOWNLOADER_MIDDLEWARES = {
   # 'midwareproject.middlewares.MidwareprojectDownloaderMiddleware': 543,
   #  "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"
}

# Enable or disable extensions
# See https://doc.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
#    'scrapy.extensions.telnet.TelnetConsole': None,
#}

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html

# 在这里激活管道,后面的数字表是优先极,数字越小,优先级越高
#ITEM_PIPELINES = {
#    'midwareproject.pipelines.MidwareprojectPipeline': 300,
#}
# 自动限速扩展

# Enable and configure the AutoThrottle extension (disabled by default)
# See https://doc.scrapy.org/en/latest/topics/autothrottle.html

# 是否启用延时扩展
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
# 下载延时初始值
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies

# 下载延时的最大值
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
# 下载请求的的并行数量
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:

# 调试模式,会打印出response响应信息
#AUTOTHROTTLE_DEBUG = False


# 缓存模块
# Enable and configure HTTP caching (disabled by default)
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
# 是否要启用http的缓存
#HTTPCACHE_ENABLED = True
# 设置缓存过期时间
#HTTPCACHE_EXPIRATION_SECS = 0
# 缓存到那个文件目录下,设置文件目录
#HTTPCACHE_DIR = 'httpcache'

# 忽略指定响应状态码的响应结果[](可以写多个)
#HTTPCACHE_IGNORE_HTTP_CODES = []

# 使用extensions.httpcache.FilesystemCacheStorage扩展组件进行缓存
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

##LOG_ENABLED
默认: True
是否启用logging
##LOG_ENCODING
默认: 'utf-8'
logging使用的编码。
##LOG_LEVEL
默认: 'DEBUG'
log的最低级别。可选的级别有: CRITICAL、 ERROR、WARNING、INFO、DEBUG 。

- CRITICAL - 严重错误(critical)
- ERROR - 一般错误(regular errors)
- WARNING - 警告信息(warning messages)
- INFO - 一般信息(informational messages)
- DEBUG - 调试信息(debugging messages)
##LOG_FILE
默认: None
logging输出的文件名。如果为None,则使用标准错误输出(standard error)。
##Logging使用
Scrapy提供了log功能,可以通过 logging 模块使用。
可以修改配置文件settings.py,任意位置添加下面两行,效果会清爽很多。

LOG_FILE = "TencentSpider.log"
LOG_LEVEL = "INFO"~~~



推荐阅读更多精彩内容

  • scrapy学习笔记(有示例版) 我的博客 scrapy学习笔记1.使用scrapy1.1创建工程1.2创建爬虫模...
    陈思煜阅读 6,010评论 5 41
  • 说起写爬虫,大多数第一时间想到的就是python了。python语法简洁明了,加上及其丰富好用的库,用它来写爬虫有...
    疯狂的哈丘阅读 3,471评论 1 13
  • 作用概括: Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛。...
    李舒静静阅读 264评论 0 2
  • 作用概括: Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛。...
    试翼腾渊阅读 430评论 0 0
  • 下午办公时间我们聆听三位优秀教师的师德报告,他们典型的故事,平凡中见伟大,简朴中见真情,体现了他们对教育、对...
    臧莉莉阅读 291评论 0 0