• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Scrapy 框架爬取豆瓣电影的信息(包括图片)和电影评论-2

武飞扬头像
Cappuccino_Luo
帮助1

六、模拟浏览器行为

创建 useragent.py 用于选择头部请求代理

# 导入random模块
import random
# 导入 useragent 用户代理模块中的 UserAgentMiddleware 类
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware


# RotateUserAgentMiddleware 类,继承 UserAgentMiddleware 父类
# 作用:创建动态代理列表,随机选取列表中的用户代理头部信息,伪装请求。
# 绑定爬虫程序的每一次请求,一并发送到访问网址。

# 发爬虫技术:由于很多网站设置反爬虫技术,禁止爬虫程序直接访问网页,因此需要创建动态代理,将爬虫程序模拟伪装成浏览器进行网页访问。
class RotateUserAgentMiddleware(UserAgentMiddleware):
    def process_request(self, request, spider):
        # 随机轮换 user-agent
        ua = random.choice(self.user_agent_list)
        if ua:
            request.headers.setdefault('User-Agent', ua)

    # the default user_agent_list composes chrome,IE,firefox,Mozilla,opera,netscape
    # for more user agent strings,you can find it in http://www.useragentstring.com/pages/useragentstring.php
    # 编写头部请求代理列表
    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",
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0"
    ]
学新通

在 settings.py 中开启

注: 使用下面的 DOWNLOADER_MIDDLEWARES 将 settings.py 中的 DOWNLOADER_MIDDLEWARES 进行覆盖

# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
# response 403(服务器拒绝请求) 说明人家不欢迎爬虫,这时候就需要设置 header 头部信息,用 user_agent_list 进行头部请求伪装
# useragent.py 配置到框架中去,此时框架在发送请求时,会自动添加随机的头部 user-agent 列表其中一个代理信息
# ip 存在被封的风险,所以使用代理 ip ,代理被封还有代理,代理在中间件中设置
# 数字543, 400表示中间件先被调用的次序。数字越小,越先被调用
DOWNLOADER_MIDDLEWARES = {
    'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
    'douban.useragent.RotateUserAgentMiddleware': 400,
}

七、设置代理 ip (中间件的方式)

1、获取的代理 ip 存入 redis 数据库

免费代理 ip

在 items.py 中新增 ProxyItem 类
class ProxyItem(scrapy.Item):
    schema_pattern = re.compile(r'http|https$', re.I)
    ip_pattern = re.compile(r'^([0-9]{1,3}.){3}[0-9]{1,3}$', re.I)
    port_pattern = re.compile(r'^[0-9]{2,5}$', re.I)

    '''
        {
            "schema": "http", # 代理的类型
            "ip": "127.0.0.1", # 代理的IP地址
            "port": "8050", # 代理的端口号
            "original": "西刺代理", # 来源
            "used_total": 11, # 代理的使用次数
            "success_times": 5, # 代理请求成功的次数
            "continuous_failed": 3, # 使用代理发送请求,连续失败的次数
            "created_time": "2018-05-02" # 代理的爬取时间
        }
    '''
    schema = scrapy.Field()
    ip = scrapy.Field()
    port = scrapy.Field()
    original = scrapy.Field()
    created_time = scrapy.Field()

    # 检查 IP 代理的格式是否正确
    def _check_format(self):
        if self['schema'] is not None and self['ip'] is not None and self['port'] is not None:
            if self.schema_pattern.match(self['schema']) and self.ip_pattern.match(self['ip']) and self.port_pattern.match(
                    self['port']):
                return True
        return False

    # 获取IP代理的url
    def _get_url(self):
        # 代理地址的格式化字符串
        PROXY_URL_FORMATTER = '%(schema)s://%(ip)s:%(port)s'
        return PROXY_URL_FORMATTER % {'schema': self['schema'], 'ip': self['ip'], 'port': self['port']}
学新通
快代理(kuaiProxy.py)
import datetime
import time

import scrapy
from scrapy.cmdline import execute

from douban.items import ProxyItem


class KuaiproxySpider(scrapy.Spider):
    name = 'kuaiProxy'
    allowed_domains = ['www.kuaidaili.com']
    start_urls = ['https://www.kuaidaili.com/free/inha/']

    def parse(self, response):
        tr_list = response.css("div#list>table>tbody tr")
        for tr in tr_list:
            ip = tr.css("td[data-title='IP']::text").get()
            port = tr.css("td[data-title='PORT']::text").get()
            schema = tr.css("td[data-title='类型']::text").get()
            if schema.lower() == "http" or schema.lower() == "https":
                item = ProxyItem()
                item['schema'] = schema.strip().lower()
                item['ip'] = ip.strip()
                item['port'] = port.strip()
                item['original'] = '快代理'
                item['created_time'] = time.strftime('%Y-%m-%d', time.localtime(time.time()))
                if item._check_format():
                    yield item
        next_page = response.xpath("//a[@class='active']/../following-sibling::li/a/@href").get()
        # 前几页都是最近更新的,我选取第1页
        if next_page != "/free/inha/2/":
            next_url = 'https://www.kuaidaili.com'   next_page
            yield scrapy.Request(next_url, dont_filter=True)

    if __name__ == '__main__':
        execute(['scrapy', 'crawl', 'kuaiProxy'])
        # execute('scrapy crawl kuaiProxy_spider -s JOBDIR=../../crawls/kuaiProxy'.split())

学新通
齐云代理(qiYunProxy.py)
import datetime
import time

import scrapy
from scrapy.cmdline import execute

from douban.items import ProxyItem


class QiyunproxySpider(scrapy.Spider):
    name = 'qiYunProxy'
    allowed_domains = ['proxy.ip3366.net']
    start_urls = ['https://proxy.ip3366.net/free/']

    def parse(self, response):
        tr_list = response.css("div.container>table>tbody tr")
        for tr in tr_list:
            ip = tr.css("td[data-title='IP']::text").get()
            port = tr.css("td[data-title='PORT']::text").get()
            schema = tr.css("td[data-title='类型']::text").get()
            if schema.lower() == "http" or schema.lower() == "https":
                item = ProxyItem()
                item['schema'] = schema.strip().lower()
                item['ip'] = ip.strip()
                item['port'] = port.strip()
                item['original'] = '齐云代理'
                item['created_time'] = time.strftime('%Y-%m-%d', time.localtime(time.time()))
                if item._check_format():
                    yield item
        next_page = response.xpath('//div[@class="container"]/nav/ul/li/a[@aria-label="Next"]/@href').get()
        if next_page != "?action=china&page=2":
            next_url = 'https://proxy.ip3366.net/free/'   next_page
            yield scrapy.Request(next_url, dont_filter=True)

    if __name__ == '__main__':
        execute(['scrapy', 'crawl', 'qiYunProxy'])
        # execute('scrapy crawl qiYunProxy_spider -s JOBDIR=../../crawls/qiYunProxy'.split())
学新通
云代理(yunProxy.py)
import datetime
import time

import scrapy
from scrapy.cmdline import execute

from douban.items import ProxyItem


class YunproxySpider(scrapy.Spider):
    name = 'yunProxy'
    allowed_domains = ['www.ip3366.net']
    start_urls = ['http://www.ip3366.net/']
    next_page = 1

    def parse(self, response):
        tr_list = response.css("div#list>table>tbody tr")
        for tr in tr_list:
            ip = tr.css("td:nth-child(1)::text").get()
            port = tr.css("td:nth-child(2)::text").get()
            schema = tr.css("td:nth-child(4)::text").get()
            if schema.lower() == "http" or schema.lower() == "https":
                item = ProxyItem()
                item['schema'] = schema.strip().lower()
                item['ip'] = ip.strip()
                item['port'] = port.strip()
                item['original'] = '云代理'
                item['created_time'] = time.strftime('%Y-%m-%d', time.localtime(time.time()))
                if item._check_format():
                    yield item
        self.next_page  = 1
        # int 类型不能直接和字符串拼接
        next_page = "?stype=1&page="   str(self.next_page)
        if self.next_page != 2:
            next_url = 'http://www.ip3366.net/'   next_page
            yield scrapy.Request(next_url, dont_filter=True)

    if __name__ == '__main__':
        execute(['scrapy', 'crawl', 'yunProxy'])
        # execute('scrapy crawl yunProxy_spider -s JOBDIR=../../crawls/yunProxy'.split())
学新通
将获取到的免费代理 ip 存入 redis 数据库
import redis


class ProxyPoolPipeline(object):
    REDIS_HOST = '127.0.0.1'
    REDIS_PORT = 6379
    REDIS_PARAMS = ''
    server = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PARAMS)
    # 保存未检验代理的 Redis key
    PROXIES_UNCHECKED_LIST = 'proxies:unchecked:list'
    # 已经存在的未检验 HTTP 代理和 HTTPS 代理集合
    PROXIES_UNCHECKED_SET = 'proxies:unchecked:set'

# 将可用的IP代理添加到代理池队列
    def process_item(self, item, spider):
        if not self._is_existed(item):
            # 用于其它爬虫判断 ip 是否被封禁等,删除 redis 内的代理
            self.server.hincrby("proxies:unchecked:set_stats", item._get_url(), 1)
            # rpush 命令用于将一个或多个值插入到列表的尾部
            # self.server.rpush(self.PROXIES_UNCHECKED_LIST, json.dumps(dict(item), ensure_ascii=False))

    # 检查 IP 代理是否已经存在
    def _is_existed(self, item):
        # 以集合的方式插入, sadd 命令将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略
        added = self.server.sadd(self.PROXIES_UNCHECKED_SET, item._get_url())
        # added = self.server.rpush(self.PROXIES_UNCHECKED_SET, item._get_url())
        return added == 0
学新通

付费代理 ip (ipProxy.py)

# 获取花钱买的代理
import time

import redis
import requests

url = ""
while True:
    r = requests.get(url)
    # 亲测站大爷和代理云经过下面的处理后,存入 redis 的数据格式变为 58.243.143.247:57114
    text = r.text
    # 去除字符串首位的回车换行符,不然后面的 text 会多出一个空字符(由 replace 和 split 联合产生)
    text = text.strip()
    # 去除字符串中间的回车换行符
    text = [text.replace('\r\n', ',')]
    # 这里不能写成 text = [x.split(",") for x in text] ,不然会出现[['58.243.143.247:57114', '58.243.143.247',...]]
    for x in text:
        text = x.split(",")

    server = redis.StrictRedis(host='127.0.0.1', port=6379, password='')
    length = 0
    for i in range(len(text)):
        item = "http://"   text[i]
        added = server.sadd('proxies:unchecked:set', item)
        if added != 0:
            server.hincrby("proxies:unchecked:set_stats", item, 1)
            length  = 1
            pass
        pass
    # can only concatenate str (not "int") to str
    print("本次获得 "   str(length)   " 个 ip")
    # 睡眠 10 分钟,再次获取 ip
    time.sleep(600)
学新通

2、 从 redis 数据库中随机取出一个代理 ip 用作这次请求的代理

# 随机选择 IP 代理下载器中间件
class RandomProxyMiddleware(object):
    # 第 2 爬虫开启前就执行
    def __init__(self, settings):
        # 2.初始化配置及相关变量
        self.r = redis.Redis(host='127.0.0.1')
        self.proxy_key = "proxies:unchecked:set"
        self.proxy_stats_key = self.proxy_key   '_stats'
        # 最多失败 1 次
        self.max_failed = 2

    @property
    def proxies(self):
        # self.r.lrange(self.proxy_key, 0, -1) 对列表数据进行读取操作
        return [i.decode('utf-8') for i in self.r.smembers(self.proxy_key)]

    # 最先执行,爬虫开启前就执行
    # 这个函数是访问 setting 和 signals 的入口,比如读取 settings.py 的某些配置。重写这个方法需要返回实例对象;如果返回对象里有参数,可在初始化方法 __init__()
    @classmethod
    def from_crawler(cls, crawler):
        # 1. 创建中间件对象
        # 默认代理是启用的
        if not crawler.settings.getbool('HTTPPROXY_ENABLED'):
            raise NotConfigured
        return cls(crawler.settings)

    def process_request(self, request, spider):
        # 3. 为每个 request 对象分配随机的 ip 代理
        if self.proxies and not request.meta.get('proxy') and request.url not in spider.start_urls:
            request.meta['proxy'] = random.choices(self.proxies)
            # 因为 random.choices(self.proxies) 出来的是 list 数据类型:['http://124.93.201.59:42672']
            # 报错:TypeError: expected string or bytes-like object
            request.meta['proxy'] = request.meta['proxy'][0]
            # 花钱买的代理需要验证,无这个验证报错 407
            # 设置代理的认证信息(站大爷可以自动绑定 ip,可以不用设置账号密码验证)
            # auth = base64.b64encode(bytes("username:password", 'utf-8'))
            # request.headers['Proxy-Authorization'] = b'Basic '   auth

    def process_response(self, request, response, spider):
        # 4.0 请求成功
        cur_proxy = request.meta.get('proxy')
        # redis升级到3.0后仅接受用户数据为字节、字符串或数字(整数,长整数和浮点数)
        # 将键或值指定为任何其他类型将引发DataError异常
        # redis.exceptions.DataError: Invalid input of type: 'NoneType'. Convert to a bytes, string, int or float first.
        # cur_proxy 为 NoneType 类型,None
        # 判断 ip 是否被对方封禁
        if response.status in (401, 403, 400, 307, 302, 503):
            self.r.hincrby(self.proxy_stats_key, cur_proxy, 1)
            # 当某个 IP 的失败次数累积到一定的数量
            filed_times = self.r.hget(self.proxy_stats_key, cur_proxy) or 0
            if int(filed_times) >= self.max_failed:
                print('got wrong http code (%s) when use %s' % (response.status, cur_proxy))
                # 可以认为该 IP 被对方封禁。从代理池中将该 IP 删除
                self.remove_proxy(cur_proxy)
                del request.meta['proxy']
            # 返回 request 将该请求重新->调度器
            return request
        # # 访问没达到效果的状态码(302 豆瓣:有异常 ip 从你这里发出)
        # elif response.status in (302, 503):
        #     return request
        return response

    def process_exception(self, request, exception, spider):
        # 4.1 请求失败
        cur_proxy = request.meta.get('proxy')
        # 请求使用代理,并且网络请求报错,认为该 IP 出错,删除,并重新->调度器
        # 判断 cur_proxy 的类型是否在元组中,是返回 True ,不在元组中返回 false
        # isinstance(cur_proxy, (ConnectError, TimeoutError)) 本身就是错误,只会判断结果只会出现 false
        if cur_proxy and isinstance(exception, (ConnectError, TimeoutError)):
            print('error (%s) occur when use proxy %s' % (exception, cur_proxy))
            self.remove_proxy(cur_proxy)
            del request.meta['proxy']
            return request

    def remove_proxy(self, proxy):
        if proxy in self.proxies:
            # lrem 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素
            # count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT
            # count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值
            # count = 0 : 移除表中所有与 VALUE 相等的值
            # self.r.lrem(self.proxy_key, proxy) 列表操作
            self.r.srem(self.proxy_key, proxy)
            self.r.hdel(self.proxy_stats_key, proxy)
学新通

3、 在 settings.py 中开启代理 ip

注: 使用下面的 DOWNLOADER_MIDDLEWARES 再次将 settings.py 中的 DOWNLOADER_MIDDLEWARES 进行覆盖

DOWNLOADER_MIDDLEWARES = {
    'douban.middlewares.RandomProxyMiddleware': 543,
    'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
    'douban.useragent.RotateUserAgentMiddleware': 400,
}

在 settings.py 内追加 redis 的相关配置(缺陷:无论启动那一只蜘蛛都要在启动之前开启 redis 服务,解决:第八点中的 custom_settings)

# 调度器启用 Redis 存储 Requests 队列
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 确保所有的爬虫实例使用 Redis 进行重复过滤
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 将 Requests 队列持久化到 Redis ,可支持暂停或重启爬虫
SCHEDULER_PERSIST = True
# Requests 的调度策略,默认优先级队列
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'

4、 扩展

1、 获取来的代理 ip 存在一定的质量问题,不是所有的 ip 都可用(中间件上设置了某个 ip 访问失败达一定次数后从 redis 数据库内删除)
2、 确定某个 ip 是否能与主机连上的时间间隔过长(3分钟),在 settings.py 中追加以下代码:

# TCP connection time out (还有下载超时,默认180)
DOWNLOAD_TIMEOUT = 60

3、 代理 ip 获取的数据少于真实数据的个数,在 settings.py 中解开下面代码的注释:

AUTOTHROTTLE_ENABLED = True

4、 让蜘蛛访问网站更像人正在访问该网站可以为每次访问设置时间间隔,在 settings.py 中解开和追加代码:

# 单位时间内的点击次数,这个是很多网站都会有的反爬虫措施(延时1秒,不能动态改变,导致访问延时都差不多,也容易被发现)
DOWNLOAD_DELAY = 30
# 启用后,当从相同的网站获取数据时, Scrapy 将会等待一个随机的值,延迟时间为0.5到1.5之间的一个随机值乘以 DOWNLOAD_DELAY
RANDOMIZE_DOWNLOAD_DELAY = True

5、 动态的为 Scrapy 添加 start_urls (未使用到所以未检验):

经过测试 在 Scrapy 的主要抓取文件里面,添加  start_requests 方法,这是 Scrapy 提供的方法哦, 在内部直接执行 yield Request(newUrl)  就可以发起新的抓包请求。 同时需要注意的是,如果写了 复写 start_requests 方法,start_urls 的请求就失效了。来给你一个范例:
class AAA(Spider):
    name = "AAA"
    start_urls = [
        "http://www.xxx.com/handbook/201710/xxx.html",
    ]

    def __init__(self):
        super(AAA, self).__init__()

    def start_requests(self):
        params = {'pageCount': '100'}
        content = requests.get('http://AAA.cc/AAA/json', params)
        json = content.json()
        resultJson = json["result"]

        for result in resultJson:
            newUrl = result["AAAUrl"]
            print("当前抓取的 Url 是:"   newUrl)
            yield Request(newUrl)

    def parse(self, response):
        content = response.body.decode("utf-8")
        print(content)
      
作者:知乎用户
链接:https://www.zhihu.com/question/50462759/answer/246929608
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
学新通

八、多管道、多蜘蛛

1、 每一个 spider 需要的配置可能不一样

在 spider 内使用 custom_settings 对 settings.py 中的相应的文件内容进行覆盖,例如在快代理(kuaiProxy.py)中使用:
	name = 'kuaiProxy'
    allowed_domains = ['www.kuaidaili.com']
    start_urls = ['https://www.kuaidaili.com/free/inha/']
    custom_settings = {
        'DOWNLOAD_DELAY': 1,
        'ROBOTSTXT_OBEY': True,
        'ITEM_PIPELINES': {
            'douban.pipelines2redis.ProxyPoolPipeline': 308
        },
    }

2、 在 douban 文件夹下创建 crawls 文件夹并在该文件夹内创建 crawls.py 文件统一启动所有蜘蛛

# 执行所有爬虫
import multiprocessing
import os

from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings


# class StartSpider:
#     # 异步执行
#     def start_by_process(self):
#         process = CrawlerProcess(get_project_settings())
#         # 爬代理
#         process.crawl('kuaiProxy')
#         process.crawl('qiYunProxy')
#         process.crawl('yunProxy')
#         # 爬内容
#         process.crawl('movieInfo')
#         process.crawl('comment')
#
#         process.start()
#
#     # 顺序执行
#     def start_by_os(self):
#         # 深入文件夹,运行文件
#         os.system("cd comment && cd spiders && scrapy runspider kuaiProxy.py && scrapy runspider "
#                   "qiYunProxy.py && scrapy runspider yunProxy.py")
#         os.system("cd comment && cd spiders && scrapy runspider movieInfo.py && scrapy runspider "
#                   "comment.py")
#
#     # 先同步后异步:先爬取代理后,依靠代理爬数据
#     # 方法一:再建2个 py 文件,两个文件内异步进行,最外的同步进行(未检测是否可行)
#
#
# startSpider = StartSpider()
# startSpider.start_by_process()
# startSpider.start_by_os()


# 方法二:有上面的代码,不需要下面区域内的代码===================================================================================
def start_by_pool(item):
    if item != "ipProxy":
        os.system("cd .. && cd douban && cd spiders && scrapy runspider "   item   ".py")
    else:
        os.system("cd .. && cd douban && python "   item   ".py")


if __name__ == "__main__":
    # 这里设置允许同时运行的的进程数量要考虑机器 cpu 的数量,进程的数量最好别大于 cpu 的数量,
    # 因为即使大于 cpu 的数量,增加了任务调度的时间,效率反而不能有效提高
    # redis.exceptions.TimeoutError: Timeout reading from socket
    pool = multiprocessing.Pool(processes=2)
    item_list = ['qiYunProxy', 'yunProxy', 'kuaiProxy', 'movieInfo', 'comment']
    for item in item_list:
        # 维持执行的进程总数为 processes ,当一个进程执行完毕后会添加新的进程进去
        pool.apply_async(start_by_pool, (item,))

    pool.close()  # 关闭进程池(pool),使其不在接受新的任务
    # 主进程阻塞等待子进程的退出, join 方法要在 close 或 terminate 之后使用
    # 调用 join 之前,先调用 close 函数,否则会出错。执行完 close 后不会有新的进程加入到 pool , join 函数等待所有子进程结束
    pool.join()
# ======================================================================================================================

学新通

九、 Scrapy 每天定时爬取数据

1、生成日志文件(控制台不输出)

在 settings.py 中追加以下代码:

# CRITICAL - 严重错误; ERROR - 一般错误; WARNING - 警告信息; INFO - 一般信息; DEBUG - 调试信息
LOG_LEVEL = 'DEBUG'
# 日志使用的编码
LOG_ENCODING = 'utf-8'
# 日志命名
LOG_FILE = f"log"   datetime.datetime.now().strftime('%Y%m%d')   ".txt"

2、启动 spiders 的 startSpidersDaily.py 文件

# 文件timerStartDaily.py
from scrapy import cmdline
import datetime
import time
import shutil
import os

recoderDir = r"crawls"  # 这是为了爬虫能够续爬而创建的目录,存储续爬需要的数据
checkFile = "isRunning.txt"  # 爬虫是否在运行的标志

startTime = datetime.datetime.now()
print(f"startTime = {startTime}")

miniter = 0
while True:
    isRunning = os.path.isfile(checkFile)
    # 爬虫不在执行状态则开始启动爬虫
    if not isRunning:
        # 在爬虫启动之前处理一些事情,清掉 JOBDIR = crawls
        isExsit = os.path.isdir(recoderDir)  # 检查 JOBDIR 目录 crawls 是否存在
        print(f"Spiders not running, ready to start. {recoderDir} isExsit:{isExsit}")
        if isExsit:
            shutil.rmtree(recoderDir)
            print(f"At time:{datetime.datetime.now()}, delete res:{recoderDir}")
        else:
            print(f"At time:{datetime.datetime.now()}, Dir:{recoderDir} is not exsit.")

        # # execute 只执行第一个
        # # 在爬虫运行过程中,会自动将状态信息存储在 crawls/comment_spider 目录下,支持续爬
        # # 若想支持续爬,在 ctrl c 终止爬虫时,只能按一次,爬虫在终止时需要进行善后工作,切勿连续多次按 ctrl c
        # cmdline.execute('scrapy crawl kuaiProxy_spider -s JOBDIR=crawl/kuaiProxy_spider'.split())
        # cmdline.execute('scrapy crawl qiYunProxy_spider -s JOBDIR=crawl/qiYunProxy_spider'.split())
        # cmdline.execute('scrapy crawl yunProxy_spider -s JOBDIR=crawl/yunProxy_spider'.split())
        # cmdline.execute('scrapy crawl movieInfo_spider -s JOBDIR=crawl/movieInfo_spider'.split())
        # cmdline.execute('scrapy crawl comment_spider -s JOBDIR=crawl/comment_spider'.split())

        # os.system("cd .. && cd comment && python ipProxy.py")
        os.system("python crawls.py")

        # 爬虫结束之后,退出脚本
        break
    else:
        print(f"At time:{datetime.datetime.now()}, spiders is running, sleep to wait.")
    # 每10分钟检查一次
    time.sleep(600)
    miniter  = 10
    # 等待满24小时,自动退出监控脚本
    if miniter >= 1440:
        break
学新通

3、 系统启动 startSpidersDaily.py 的 bat 文件

cd /d D:\workplace\python\douban\crawls
call python startSpidersDaily.py
pause

4、 设置每天的定时任务(我是 window10)

1、 打开计算机管理
学新通
2、 创建基本任务
学新通
3、触发器选择每天
学新通
4、一直下一步,直到启动程序
学新通
5、 创建完后需要修改
学新通
在右边找到自己创建时对应的名称后双击

十、我的资源包

Scrapy 框架爬取豆瓣电影的信息(包括图片)和电影评论-3

Scrapy 框架爬取豆瓣电影的信息(包括图片)和电影评论-1

十一、参考

python实现scrapy爬虫每天定时抓取数据
scrapy-redis ip代理池
mongo操作图片储存

scrapy框架中多个spider,tiems,pipelines的使用及运行方法

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgcfehk
系列文章
更多 icon
同类精品
更多 icon
继续加载