异步爬取+多线程+redis构建一个运转丝滑且免费http-ip代理池 (三)

这篇具有很好参考价值的文章主要介绍了异步爬取+多线程+redis构建一个运转丝滑且免费http-ip代理池 (三)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

内容提要:

 如果说,爬取网页数据的时候,我们使用了异步,那么将数据放入redis里面,其实也需要进行异步;当然,如果使用多线程或者redis线程池技术也是可以的,但那会造成冗余;
 因此,在测试完多线程redis搭配异步爬虫的时候,我发现效率直接在redis这里被无限拉低下来!

因此:

 最终的redis库,我选择aioredis(redis的异步库);

效果:

(对上万个ip进行了检测,最终只得到这么几个....)

完成aioredis的时候,肯定会遇到一个bug,我先写在这里;当你们遇到的时候,再回头来看,没遇到前先跳过:

aioredis报错: duplicate base class TimeoutError

异步爬取+多线程+redis构建一个运转丝滑且免费http-ip代理池 (三),15天玩转高级python,redis,数据库,缓存

解决办法:

1.异步爬取+多线程+redis构建一个运转丝滑且免费http-ip代理池 (三),15天玩转高级python,redis,数据库,缓存
异步爬取+多线程+redis构建一个运转丝滑且免费http-ip代理池 (三),15天玩转高级python,redis,数据库,缓存

回归正题:

当我们已经完成了网页的爬取和解析并验证完ip之后,会得到了X个有效ip;那么就需要将他存储起来:

aioredis相关代码如下:
import aioredis

# 配置连接 Redis 服务的地址和端口以及数据库索引
redis_host = "localhost"
redis_port = 6379
redis_db = 0
redis_url = f"redis://{redis_host}:{redis_port}/{redis_db}"

# 创建 Redis 连接的异步函数
async def create_redis():
    # aioredis 2.0+ 直接连接
    return aioredis.from_url(redis_url, encoding="utf-8", decode_responses=True)

# 异步将多个有效代理添加到 Redis 的集合中
async def add_valid_proxies_to_redis(proxies):
    redis = await create_redis()
    async with redis.client() as conn:
        new_proxies_added=0   #sadd对于已经存在的ip是不会放进去的!所以如果需要验证,就需要计数
        for proxy in proxies:
            proxy_str = f"{proxy['ip']}:{proxy['port']}"
            added_count = await conn.sadd("valid_proxies", proxy_str) # 如果被放进去,就会进行修正添加
            new_proxies_added += added_count
            # 打印实际添加到集合中的新代理数量
        print(f"{new_proxies_added}个新的IP实际加入到代理池中了.")

# 异步地从 Redis 随机获取一个有效的代理
async def get_random_proxy():
    redis = await create_redis()
    async with redis.client() as conn:
        random_proxy = await conn.srandmember("valid_proxies")
    return random_proxy

业务代码如下:文章来源地址https://www.toymoban.com/news/detail-755799.html

import logging
import asyncio
import aiohttp
import time
from bs4 import BeautifulSoup
from db.aioredis_ip import add_valid_proxies_to_redis #引入ip代理添加函数

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)


#请求头
headers = {

    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
}

# 异步获取URL的函数
async def fetch_url(session, url):
    try:
        async with session.get(url, timeout=3,headers=headers) as response:

            return await response.text()
    except aiohttp.ClientError as e:
        logger.exception("爬取相关url出错: %s", url)


async def parse_html(html):
    '''
    解析代码,如果标题包含'89免费代理',
    如果包含"快代理"--->就是快代理
    :param html:
    :return:
    '''

    soup = BeautifulSoup(html, 'html.parser')
    title = soup.find('title')
    if '89免费代理' in title.getText():

        table = soup.find('table', class_='layui-table')
        tbody = table.find('tbody')

        proxy_list = []

        rows = tbody.find_all('tr')
        for row in rows:
            proxy = {}
            cells = row.find_all('td')
            proxy["ip"] = cells[0].text.strip()
            proxy["port"] = cells[1].text.strip()
            proxy_list.append(proxy)



        return proxy_list


async def check_ip(session, proxy):
    """...略去其它注释,同时函数的docstring也要更新..."""
    proxy_url = f"http://{proxy['ip']}:{proxy['port']}"
    try:
        async with session.get(url="http://httpbin.org/ip", timeout=1.5, proxy=proxy_url) as response:
            # 返回原始代理信息和有效性状态
            return proxy, response.status == 200
    except (aiohttp.ClientError, asyncio.TimeoutError):
        # 返回原始代理信息和无效状态
        return proxy, False



async def check_ip_main(proxies):
    """
    检查IP有效性并返回所有有效的IP列表
    """
    async with aiohttp.ClientSession() as session:
        tasks = [asyncio.create_task(check_ip(session, proxy)) for proxy in proxies]

        valid_proxies = []  # 初始化一个空列表来存放有效的代理
        for task in asyncio.as_completed(tasks):
            proxy, is_valid = await task  # 这里我们期望 check_ip 也返回对应的 proxy 信息
            if is_valid:
                #当返回来是True的时候,他就会加入到valid_proxies里面;
                valid_proxies.append(proxy)

        return valid_proxies


# 异步操作,在其中打开会话和调用fetch任务
async def main(urls):
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        for future in asyncio.as_completed(tasks):
            html = await future
            if html:
                #对html进行解析,获取iplist=[]
                proxys = await parse_html(html)

                # 检查ip的有效性,这时候继续协程
                valid_proxies= await check_ip_main(proxys)

                if len(valid_proxies)>0:
                    #如果在redis里面进行比较>0,会突然增加redis通信的次数造成不必要操作;
                    # python内存处理比较,会更快一些;
                    #放入到redis库中
                    print(valid_proxies)

                    await add_valid_proxies_to_redis(valid_proxies)  # 将有效代理存储到Redis





# URL 列表
urls = ["https://www.89ip.cn/index_{}.html".format(i+1) for i in range(25)]

# 运行协程
stat =time.time()
asyncio.run(main(urls))
end =time.time()

print(f'一共用了{(end-stat)}秒')

总结:

好,那咱们给这篇文章画个句号。咱们今天聊了挺多,把Scrapy的异步机制和aiohttp做了个对比,然后又聊了聊怎么整合aiohttp进Scrapy。虽然听起来有点绕,但核心其实很简单:要是你喜欢给自己找点技术上的挑战,那你大可一试,折腾一下也是乐趣。但站在团队和项目效率的角度上,Scrapy已经把一系列复杂的问题处理得很优雅了。特别是当你的项目需要稳定和易于维护的解决方案时,Scrapy无疑是更明智的选择。用好了Scrapy,整合了Redis,你的爬虫可以飞起来,真的。
至于最后这段代码,就是展示了如何把异步验证代理IP和存储到Redis的复杂逻辑整合进Scrapy的`Item Pipeline`。这把原本可能天马行空的代码逻辑,放在了Scrapy这个可靠框架里头,让它变得简单又强大。
所以说啊,无论是新手还是老手,投入时间去弄懂Scrapy的内部机制和好处,总是值得的。Scrapy这玩意,稳、快、省心,再配上Scrapy-Redis,简直了。而且就算真的遇到难题,那强大的社区支持和丰富的文档也能帮你快速解决。
总之,享受Scrapy带来的便利,把时间节省下来,投入到更有价值的工作上吧。这文章就到这里,希望我这些话能给你点启发,也希望你的项目能顺风顺水,爬虫能顺顺利利拿到你想要的数据!

到了这里,关于异步爬取+多线程+redis构建一个运转丝滑且免费http-ip代理池 (三)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何获得一个丝滑的麦轮底盘(原理+代码详解)

            本文将用最直白的方式讲述麦轮底盘的控制原理,并且将附上全套stm32代码。 目录 一、准备工作 1. 麦轮简介 2. 安装底盘 二、原理分析 1. 先从一个轮子开始 2. 再到整个底盘 三、运动学逆解 1. 继续从整体分析 2. 最后回到一个轮子 四、离散化和PID 1. 数据离散化 2. 增

    2024年02月05日
    浏览(44)
  • 异步请求库的实际应用案例:爬取豆瓣经典电影

    在日常爬虫过程中,你有没有遇到过需要爬取大量数据的情况,但是传统的同步请求方式让您等得焦头烂额? 这个问题的根源在于传统的同步请求方式。当我们使用同步请求时,程序会一直等待服务器的响应,直到数据返回后才能继续执行下一步操作。这种方式效率低下,尤

    2024年02月09日
    浏览(29)
  • 如何自己实现一个丝滑的流程图绘制工具(一)vue如何使用

    背景 项目需求突然叫我实现一个类似processOn一样的在线流程图绘制工具。 这可难倒我了,立马去做调研,在github上找了很多个开源的流程图绘制工具, 对比下来我还是选择了 bpmn-js 原因: 1、他的流程图是涉及到业务的,比如开始事件、结束事件等 2、扩展性很强(这个扩展

    2024年02月11日
    浏览(43)
  • 如何自己实现一个丝滑的流程图绘制工具(五)bpmn的xml和json互转

    背景 因为服务端给的数据并不是xml,而且服务端要拿的数据是json,所以我们只能xml和json互转,来完成和服务端的对接 xml转json jsonxml.js json 转为xml

    2024年02月11日
    浏览(38)
  • 线程池创建线程异步获取Future超时

    其中,future.get是从开始进行get方法时进行计算的时间,非future生成开始计算的,即什么时候get什么时候开始计时。 线程池从生成线程,如果核心线程不为0,则有任务时一直生成核心线程,直至到核心线程,之后开始方队列中,最后任务多就开始开辟新线程到最大线程数。

    2024年02月02日
    浏览(30)
  • 如何自己实现一个丝滑的流程图绘制工具(七)bpmn-js 批量删除、复制节点

    背景 希望实现批量删除和复制节点,因为bpmn-js是canvas画的,所以不能像平时页面上的复制一样直接选择范围,会变成移动画布。 思路是: 绘制一个选择的效果框,这样才可以看出来选的节点有哪些。 上面的选中范围框效果也是用canvas画出来的 因为bpmn-js对鼠标直接选取范围

    2024年02月10日
    浏览(41)
  • 如何使用Ruby 多线程爬取数据

    现在比较主流的爬虫应该是用python,之前也写了很多关于python的文章。今天在这里我们主要说说ruby。我觉得ruby也是ok的,我试试看写了一个爬虫的小程序,并作出相应的解析。 Ruby中实现网页抓取,一般用的是mechanize,使用非常简单。 首先安装sudo gem install mechanize 然后抓取网

    2024年02月05日
    浏览(32)
  • @Async异步线程:Spring 自带的异步解决方案

            在项目应用中,使用MQ异步调用来实现系统性能优化,完成服务间数据同步是常用的技术手段。如果是在同一台服务器内部,不涉及到分布式系统,单纯的想实现部分业务的异步执行,这里介绍一个更简单的异步方法调用。         对于异步方法调用,从Spring3 开

    2023年04月24日
    浏览(56)
  • ET介绍——单线程异步

    前面几个例子都是多线程实现的异步,但是异步显然不仅仅是多线程的。我们在之前的例子中使用了Sleep来实现时间的等待,每一个计时器都需要使用一个线程,会导致线程切换频繁,这个实现效率很低,平常是不会这样做的。一般游戏逻辑中会设计一个单线程的计时器,我

    2024年02月05日
    浏览(34)
  • 手把手教你做一个 ChatGPT !丝滑小白版,只需一张单卡 GPU,轻松开启个性化训练!...

    大家好,我是贺同学。 一直以来密切关注 ChatGPT 的趋势,最近相关的话题可谓是热度不减,虽然从事互联网行业,但一直对 LLM 相关领域关注较少。 最近的 ChatGPT 的火热,让我对 LLM 相关开源社区也关注了起来,相关的开源社区,也涌现了很多优秀的工作,吸引了很多人的关

    2024年02月08日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包