您现在的位置是:网站首页 > 博客日记 >

python之高性能爬虫|异步|多线程|多进程|反爬

作者:YXN-python 阅读量:96 发布日期:2023-03-25

首先看看单线程爬虫缺点:

  1. 低效率和速度慢:单线程爬虫一次只能处理一个请求和一个响应,不能同时处理多个任务,这大大限制了爬虫的执行效率。在同一时间内,单线程爬虫爬取的数据量远远少于多线程或异步爬虫。
  2. 资源利用率低:现代计算机一般都有多核处理器,可以同时执行多个线程。单线程爬虫无法充分利用多核处理器的优势,不能发挥现代硬件的最大性能。
  3. 处理IO阻塞的问题:在进行网络请求或等待响应时,单线程会出现阻塞,整个爬虫就在这段时间内停止工作,等待IO操作完成。
  4. 扩展性差:随着爬取任务的增加,单线程爬虫难以水平扩展。相对来说,多线程或异步模型更容易实现分布式爬虫,提高爬取能力。
  5. 复杂页面处理不足:某些网页可能需要执行复杂的数据处理或需要长时间等待响应,单线程爬虫会在这些页面上花费大量时间。
  6. 抗干扰能力弱:如果在爬取过程中遇到错误,整个爬虫可能会停止或崩溃。没有其他线程可以接管工作,这就需要爬虫开发者手动干预。

为什么需要高性能爬虫?

主要有以下几个原因:

  1. 大规模数据采集: 在许多应用中,需要从互联网上大量的网页中获取数据。高性能爬虫能够更快速地获取大量数据,适应对数据量的快速增长。
  2. 实时数据更新: 一些应用需要实时地获取最新的信息,如新闻、社交媒体的实时动态等。高性能爬虫可以更迅速地获取并更新数据,确保信息的及时性。
  3. 竞争优势: 在一些竞争激烈的行业中,对最新市场信息的快速响应可能是取得竞争优势的关键。高性能爬虫可以帮助企业更快速地收集并分析市场数据。
  4. 提高效率和降低成本: 高性能爬虫能够以更低的成本获取更多的数据,从而提高效率。这对于需要大规模数据支持的企业和项目来说是至关重要的。
  5. 反爬虫策略对抗: 许多网站采用反爬虫技术,对于速度较慢的爬虫,可能更容易被检测和封禁。高性能爬虫可以更难以被检测到,并更具有应对反爬虫策略的能力。
  6. 实时分析和挖掘: 在一些需要实时分析和挖掘大量数据的场景中,高性能爬虫可以更快速地将数据传输到分析系统,以支持实时决策和洞察。
  7. 应对网站变化: 互联网上的网站和数据结构可能随时发生变化。高性能爬虫能够更灵活地适应这些变化,并在较短的时间内进行调整。
  8. 高性能爬虫是应对现代互联网环境中海量、动态和复杂数据的必要工具,可以为企业、研究机构和开发者带来更多的竞争力和创新力。

高性能爬虫关键要素

当讲到高性能爬虫时,有几个关键要素需要强调,这些要素涉及到爬虫的设计、优化和执行阶段。

  • 异步和并发: 使用异步和并发是高性能爬虫的基础。异步请求和并发处理可以使爬虫更有效地利用系统资源,同时提高爬取速度。采用异步库(如 asyncio)和多线程/多进程技术可以有效降低等待时间。
  • 优化请求和响应处理: 精确而高效的请求和响应处理是高性能爬虫的核心。合理设置请求头、使用连接池、压缩响应体、减少网络延迟等都是需要注意的优化点。
  • 分布式架构: 随着数据量的增加,单机爬虫可能无法满足需求。分布式爬虫架构能够通过多个爬虫节点协同工作,提高整体的爬取效率和容错性。
  • 合理的爬取策略: 制定合理的爬取策略是高性能爬虫的关键。这包括爬取深度、频率、并发请求数量等参数的优化。合理的爬取策略可以降低被封禁的风险,同时提高数据采集的效率。
  • 反爬虫策略和防封禁机制: 处理反爬虫策略是高性能爬虫必须面对的问题。采用随机 User-Agent、使用代理、模拟人类操作等手段可以降低被封禁的概率。
  • 合理的存储和数据处理: 高性能爬虫需要高效的数据存储和处理方式。选择适当的数据库、数据结构和索引能够提高数据检索速度,而异步数据处理和队列系统可以减轻单一节点的负担。
  • 自动化和监控: 高性能爬虫需要具备自动化的能力,包括自动恢复、自动调度和自动报警。监控爬虫的运行状态、请求成功率和速度等指标是及时发现问题并作出调整的关键。
  • 机器学习和智能优化: 利用机器学习技术对爬虫行为进行优化,包括识别反爬虫策略、智能调整爬取策略等,可以提高爬虫的适应性和智能性。

这些关键要素的综合应用,可以使得爬虫在大规模、动态、复杂的互联网环境中更具有稳定性、效率和鲁棒性。同时,不同的项目可能有不同的优化重点,因此在实际应用中,需要根据具体情况进行灵活调整。

异步爬虫

aiohttp 是一个支持异步请求的Python库,它是基于 asyncio 功能构建的,允许你发出异步的HTTP请求。这意味着当你等待网络响应时,CPU可以去做其他事情,从而提高总体性能,特别是在处理大量并发请求时非常有用。

import asyncio
import aiohttp
import time

async def fetch(session, url, i):
    res = await session.get(url)
    print(f'第{i + 1}次请求,status_code = {res.status}')
    # await asyncio.sleep(1)
    return res

async def main():
    # 生明一个异步的上下文管理器,能帮助我们自己的分配和释放资源
    # aiohttp.ClientSession()   类似requests的session()
    async with aiohttp.ClientSession() as session:
        task_list = []
        for i in range(30):
            # 获取到协程对象
            res = fetch(session, 'https://www.baidu.com', i)
            # 创建task对象
            task = asyncio.create_task(res)
            task_list.append(task)
            # 直接执行异步对象任务,会阻塞
            # await fetch(session, url, i)
            # 等待执行的异步 将task对象交有event_loop来控制
        done, pending = await asyncio.wait(task_list)
        print(done, pending)
        # for item in done:
        #     print(item.result())

if __name__ == '__main__':
    start = time.time()
    # 开启事件循环对象
    loop = asyncio.get_event_loop()
    # 用事件循环对象开启协程异步对象
    loop.run_until_complete(main())
    end = time.time()
    print(f'同步发送30次请求,耗时:{end - start}')

异步爬取网站

import asyncio
import aiohttp
import aiofiles

async def get_html(session, url):
    try:
        await asyncio.sleep(random.randint(1, 3))  # 异步等待
        async with session.get(url=url, timeout=8) as resp:
            if not resp.status // 100 == 2:
                print(resp.status)
                print("爬取", url, "出现错误")
            else:
                resp.encoding = 'utf-8'
                text = await resp.text()
                return text
    except Exception as e:
        print("出现错误", e)
        await get_html(session, url)

使用异步请求之后,对应的文件保存也需要使用异步,即是一处异步,处处异步

async def download(title_list, content_list):
    async with aiofiles.open('{}.txt'.format(title_list[0]), 'a',
                             encoding='utf-8') as f:
        await f.write('{}'.format(str(content_list)))

aiohttp高级使用

对于更复杂的 HTTP 请求,aiohttp 提供了丰富的功能:

发送HTTP POST请求:

async with session.post('http://httpbin.org/post', data=b'data') as resp:
    print(resp.status)

上传文件:

files = {'file': open('report.xls', 'rb')}
async with session.post(url, data=files) as resp:

使用参数、头部(Headers)和Cookies:

params = {'key1': 'value1', 'key2': 'value2'}
headers = {'content-type': 'application/json'}
cookies = {'session_id': '1234567890'}
async with session.get(url, params=params, headers=headers, cookies=cookies) as resp:

处理JSON响应:

async with session.get(url) as resp:
    json_response = await resp.json()

处理异常

使用 aiohttp 时,可能会遇到各种异常,例如连接问题或超时。可以通过捕获这些异常来增强程序的健壮性:

try:
    async with session.get(url) as response:
        # 处理响应
        ...
except aiohttp.ClientError as e:
    print(f"A client error occurred: {e}")

并发请求

aiohttp 与 asyncio 搭配使用能很容易的执行并发请求。可以使用 asyncio.gather() 或者运行多个协程来实现:

import aiohttp
import asyncio

async def fetch(session, url):
    res = await session.get(url)
    return res


async def main():
    urls = ['https://www.baidu.com', 'https://news.sina.com.cn/', 'https://www.sohu.com/']
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        for result in results:
            print(result)

if __name__ == '__main__':
    import time
    start = time.time()
    # 开启事件循环对象
    loop = asyncio.get_event_loop()
    # 用事件循环对象开启协程异步对象
    loop.run_until_complete(main())
    end = time.time()
    print(f'耗时:{end - start}')

多线程和多进程

多线程

参考:python多线程应用—threading

多进程

参考:python进程multiprocessing

反封禁和反爬虫策略

反封禁和反爬虫技术是指实施一系列策略来避免爬虫被目标网站识别并封禁。目标网站可能会使用各种方式检测不正常的流量模式,从而采取措施限制或阻止爬虫的访问。以下是一些反爬虫和反封禁的策略:

  • 使用代理池和动态IP。
  • 调整请求频率:限制请求速度,随机化请求间隔。
  • 修改User-Agent
  • 参考robots.txt
  • 合理设置HTTP请求头
  • Cookie管理
  • 使用浏览器驱动:使用Selenium或Puppeteer等工具
  • 分布式爬虫
  • 使用API而非HTML解析
  • 验证码处理:手动解决,或者使用图像识别软件和第三方解码服务。
  • 会话和Cookies伪装
  • Referrer伪装

相关:反爬虫机制及解决方法

常见性能挑战和解决方案

  • 处理大规模数据
  • 高并发和异步请求
  • 防止封禁和反爬策略

在构建爬虫时,常常会面临一些性能挑战,这些挑战可能涉及到数据处理、网络通信、反爬虫机制等方面。

1. 大规模数据处理挑战:

挑战: 爬取大量数据可能导致内存消耗过多,影响系统性能。

解决方案:

采用分页爬取策略,将数据分批次处理。

利用数据库的索引和合适的数据结构,优化数据查询和检索效率。

使用合适的数据库,考虑分布式存储系统以提高数据处理速度。

2. 高并发和异步请求挑战:

挑战: 同时处理大量并发请求可能导致性能瓶颈。

解决方案:

使用异步请求库(如 asyncio、aiohttp)实现非阻塞的异步爬取。

利用线程池或进程池处理大量并发请求。

合理设置连接池大小,以减少连接建立和断开的开销。

3. 网站反爬虫策略挑战:

挑战: 网站采用反爬虫机制,封禁 IP 或检测爬虫行为。

解决方案:

使用随机 User-Agent,模拟真实用户行为。

配置代理池,定期更换 IP 地址。

降低请求频率,模拟人类操作的访问模式。

使用验证码识别技术或图像识别技术应对特殊反爬机制。

4. 网络延迟和响应时间挑战:

挑战: 高延迟和长等待时间会降低爬取速度。

解决方案:

选择高速、稳定的云服务提供商,减小网络延迟。

使用分布式爬虫,降低单节点的请求等待时间。

合理设置请求超时时间,避免长时间等待无响应。

5. 动态网页渲染挑战:

挑战: 需要爬取使用 JavaScript 动态渲染的网页内容。

解决方案:

使用无头浏览器(Headless Browser)模拟浏览器行为。

考虑使用专门的动态网页爬取工具,如 Puppeteer、Selenium。

分析网页加载过程,直接请求后端 API 获取数据。

6. 数据存储和数据库查询挑战:

挑战: 数据库操作过多可能导致性能下降。

解决方案:

使用数据库连接池,避免频繁的连接和断开操作。

优化数据库查询语句,合理使用索引。

考虑使用缓存技术,减轻数据库负担。

7. 防止被封禁挑战:

挑战: 网站可能通过 IP 封禁等方式应对爬虫。

解决方案:

使用代理池,定期更换 IP 地址。

控制爬取速度,模拟人类访问行为。

多样化 User-Agent,防止被单一特征检测。

 

YXN-python

2023-03-25