热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Python分布式爬虫详解(三)

数据科学俱乐部中国数据科学家社区上一章中,利用scrapy-redis做了一个简单的分布式爬虫,虽然很一般(只有30个请求)
640?wx_fmt=jpeg

数据科学俱乐部

中国数据科学家社区

640?wx_fmt=jpeg

上一章中,利用scrapy-redis做了一个简单的分布式爬虫,虽然很一般(只有30个请求)但是基本能说清楚原理,本章中,将对该项目进行升级,使其成为一个完整的分布式爬虫项目。

Python分布式爬虫详解(一)

Python分布式爬虫详解(二)

本章知识点:

a.代理ip的使用

b.Master端代码编写

c.数据转存到mysql

一、使用代理ip

在 中,介绍了ip代理池的获取方式,那么获取到这些ip代理后如何使用呢?

首先,在setting.py文件中创建USER_AGENTS和PROXIES两个列表:

USER_AGENTS = [
    'Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10',
    'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
    'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36',
    'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER',
    'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 UBrowser/4.0.3214.0 Safari/537.36',
    'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 UBrowser/6.2.4094.1 Safari/537.36'

]

PROXIES = [
    {'ip_port''118.190.95.43:9001'"user_passwd"None},
    {'ip_port''61.135.217.7:80'"user_passwd"None},
    {'ip_port''118.190.95.35:9001'"user_passwd"None},
]

我们知道,下载中间件是介于Scrapy的request/response处理的钩子,每个请求都需要经过中间件。所以在middlewares.py中新建两个类,用于随机选择用户代理和ip代理:

# 随机的User-Agent
class RandomUserAgent(object):
    def process_request(self, request, spider):
        useragent = random.choice(USER_AGENTS)
        #print useragent
        request.headers.setdefault("User-Agent", useragent)

# 随机的代理ip
class RandomProxy(object):
    def process_request(self, request, spider):
        proxy = random.choice(PROXIES)
        # 没有代理账户验证的代理使用方式
        request.meta['proxy'] = "http://" + proxy['ip_port']

在setting.py中开启下载中间件:

DOWNLOADER_MIDDLEWARES = {
    'dytt_redis_slaver.middlewares.RandomUserAgent': 543,
    'dytt_redis_slaver.middlewares.RandomProxy': 553,
}

二、Master端代码

Scrapy-Redis分布式策略中,Master端(核心服务器),不负责爬取数据,只负责url指纹判重、Request的分配,以及数据的存储,但是一开始要在Master端中lpush开始位置的url,这个操作可以在控制台中进行,打开控制台输入:

redis-cli
127.0.0.1:6379> lpush dytt:start_urls https://www.dy2018.com/0/

也可以写一个爬虫对url进行爬取,然后动态的lpush到redis数据库中,这种方法对于url数量多且有规律的时候很有用(不需要在控制台中一条一条去lpush,当然最省事的方法是在slaver端代码中增加rule规则去实现url的获取)。比如要想获取所有电影的分类。

640?wx_fmt=jpeg

链接就是一个或者两个数字,所以rule规则为:

    rules = (
        Rule(LinkExtractor(allow=r'/\d{1,2}/$'), callback='parse_item'),
    )

在parse_item中返回这个请求链接:

 def parse_item(self, response):
        # print(response.url)
        items = DyttRedisMasterItem()
        items['url'] = response.url
        yield items

piplines.py中,将获得的url全部lpush到redis数据库:

import redis

class DyttRedisMasterPipeline(object):
    def __init__(self):
        # 初始化连接数据的变量
        self.REDIS_HOST = '127.0.0.1'
        self.REDIS_PORT = 6379
        # 链接redis
        self.r = redis.Redis(host=self.REDIS_HOST, port=self.REDIS_PORT)

    def process_item(self, item, spider):
        # 向redis中插入需要爬取的链接地址
        self.r.lpush('dytt:start_urls', item['url'])
        return item

运行slaver端时,程序会等待请求的到来,当starts_urls有值的时候,爬虫将开始爬取,但是一开始并没有数据,因为会过滤掉重复的链接:

640?wx_fmt=jpeg

毕竟有些电影的类型不止一种:

640?wx_fmt=jpeg

scrapy默认16个线程(当然可以修改为20个啊),而分类有20个,所以start_urls会随机剩下4个,等待任务分配:

640?wx_fmt=jpeg

当链接过滤完毕后,就有数据了:

640?wx_fmt=jpeg

因为在setting.py中设置了:

SCHEDULER_PERSIST = True

所以重新启动爬虫的时候,会接着之前未完成的任务进行爬取。在slaver端中新增rule规则可以实现翻页功能:

page_links = LinkExtractor(allow=r'/index_\d*.html')
rules = (
        # 翻页规则
        Rule(page_links),
        # 进入电影详情页
        Rule(movie_links, callback='parse_item'),
    )

三、数据转存到Mysql

因为,redis只支持String,hashmap,set,sortedset等基本数据类型,但是不支持联合查询,所以它适合做缓存。将数据转存到mysql数据库中,方便以后查询:

640?wx_fmt=jpeg

创建数据表:

代码如下:

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

import json
import redis
import pymysql

def main():
    # 指定redis数据库信息
    rediscli = redis.StrictRedis(host='127.0.0.1', port=6379, db=0)
    # 指定mysql数据库
    mysqlcli = pymysql.connect(host='127.0.0.1', user='root', passwd='zhiqi', db='Scrapy', port=3306, use_unicode=True)

    while True:
        # FIFO模式为 blpop,LIFO模式为 brpop,获取键值
        source, data = rediscli.blpop(["dytt_slaver:items"])
        item = json.loads(data)

        try:
            # 使用cursor()方法获取操作游标
            cur = mysqlcli.cursor()
            # 使用execute方法执行SQL INSERT语句
            cur.execute("INSERT INTO dytt (name, year, language, "
                        "movie_type, release_date, score, file_size, "
                        "film_time, introduction, posters, download_link) VALUES "
                        "(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )",
                        [item['name'], item['year'], item['language'],
                         item['movie_type'], item['release_date'], item['score'],
                         item['file_size'], item['film_time'], item['introduction'],
                         item['posters'], item['download_link']])
            # 提交sql事务
            mysqlcli.commit()
            #关闭本次操作
            cur.close()
            print ("inserted %s" % item['name'])
        except pymysql.Error as e:
            print ("Mysql Error %d: %s" % (e.args[0], e.args[1]))


if __name__ == '__main__':
    main()

最终结果:

640?wx_fmt=jpeg

项目地址:

https://github.com/ZhiqiKou/Scrapy_notes


640?wx_fmt=jpeg


本文作者

Zhiqi Kou,一个向往成为真正程序员的码奴。

地址:zhihu.com/people/zhiqi-kou

投稿邮箱:pythonpost@163.com

欢迎点击申请Python中文社区新专栏作者计划

640?wx_fmt=gif

Python中文社区作为一个去中心化的全球技术社区,以成为全球20万Python中文开发者的精神部落为愿景,目前覆盖各大主流媒体和协作平台,与阿里、腾讯、百度、微软、亚马逊、开源中国、CSDN等业界知名公司和技术社区建立了广泛的联系,拥有来自十多个国家和地区数万名登记会员,会员来自以公安部、工信部、清华大学、北京大学、北京邮电大学、中国人民银行、中科院、中金、华为、BAT、谷歌、微软等为代表的政府机关、科研单位、金融机构以及海内外知名公司,全平台近20万开发者关注。

640?wx_fmt=jpeg

▼ 点击下方阅读原文,免费成为社区会员


推荐阅读
  • 本文详细介绍了Python编程语言的学习路径,涵盖基础语法、常用组件、开发工具、数据库管理、Web服务开发、大数据分析、人工智能、爬虫开发及办公自动化等多个方向。通过系统化的学习计划,帮助初学者快速掌握Python的核心技能。 ... [详细]
  • 自己用过的一些比较有用的css3新属性【HTML】
    web前端|html教程自己用过的一些比较用的css3新属性web前端-html教程css3刚推出不久,虽然大多数的css3属性在很多流行的浏览器中不支持,但我个人觉得还是要尽量开 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • 本文详细介绍了如何使用Python编写爬虫程序,从豆瓣电影Top250页面抓取电影信息。文章涵盖了从基础的网页请求到处理反爬虫机制,再到多页数据抓取的全过程,并提供了完整的代码示例。 ... [详细]
  • Windows 7 64位系统下Redis的安装与PHP Redis扩展配置
    本文详细介绍了在Windows 7 64位操作系统中安装Redis以及配置PHP Redis扩展的方法,包括下载、安装和基本使用步骤。适合对Redis和PHP集成感兴趣的开发人员参考。 ... [详细]
  • 优化Flask应用的并发处理:解决Mysql连接过多问题
    本文探讨了在Flask应用中通过优化后端架构来应对高并发请求,特别是针对Mysql 'too many connections' 错误的解决方案。我们将介绍如何利用Redis缓存、Gunicorn多进程和Celery异步任务队列来提升系统的性能和稳定性。 ... [详细]
  • 在学习网页爬虫时,使用Selenium进行自动化操作。初次安装selenium模块后,第二天运行代码时遇到了ImportError:无法从'selenium'导入名称'webdriver'。本文将详细解释该问题的原因及解决方案。 ... [详细]
  • 本文详细解析了如何使用Python的urllib模块发起POST请求,并通过实例展示如何爬取百度翻译的翻译结果。 ... [详细]
  • Redis Hash 数据结构详解
    本文详细介绍了 Redis 中的 Hash 数据类型及其常用命令。Hash 类型用于存储键值对集合,支持多种操作如插入、查询、更新和删除字段值。此外,文章还探讨了 Hash 类型在实际业务场景中的应用,并提供了优化建议。 ... [详细]
  • 本文详细探讨了如何在Docker环境中实现单机部署Redis集群的方法,提供了详细的步骤和配置示例,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 随着Redis功能的不断增强和稳定性提升,其应用范围日益广泛,成为软件开发人员不可或缺的技能之一。本文将深入探讨Redis集群的部署与优化,包括主从备份机制、哨兵模式以及集群功能,帮助读者全面理解并掌握Redis集群的应用。 ... [详细]
  • JavaScript 中创建对象的多种方法
    本文详细介绍了 JavaScript 中创建对象的几种常见方式,包括对象字面量、构造函数和 Object.create 方法,并提供了示例代码和属性描述符的解释。 ... [详细]
  • 并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性
    Java并发编程实践目录并发编程01——ThreadLocal并发编程02——ConcurrentHashMap并发编程03——阻塞队列和生产者-消费者模式并发编程04——闭锁Co ... [详细]
  • 使用JS、HTML5和C3创建自定义弹出窗口
    本文介绍如何结合JavaScript、HTML5和C3.js来实现一个功能丰富的自定义弹出窗口。通过具体的代码示例,详细讲解了实现过程中的关键步骤和技术要点。 ... [详细]
author-avatar
罗帅飞1
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有