如何用Python让爬虫一天抓取100万张网页

大规模数据抓取好文分享一篇,文章对设计一个单机定向日抓取百万网页的爬虫,进行了较为细致的阐述。 对日抓取百万量级,合计一亿张网页的爬虫, 在内存策略,硬盘存储策略,海量数据去重,网络性能优化方面给予了实操性很强的方案。 https://www.yuanrenxue.com/crawler/high-performance-crawler.html


如何用Python让爬虫一天抓取100万张网页

18 回复

大佬大佬


要一天抓取100万张网页,关键在于并发处理和资源管理。核心思路是用异步IO配合连接池,同时控制好请求频率。

这里给个基于aiohttp和asyncio的示例:

import asyncio
import aiohttp
from aiohttp import ClientSession, TCPConnector
import time
import logging

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

class MassCrawler:
    def __init__(self, urls_file, max_concurrent=500):
        self.urls_file = urls_file
        self.max_concurrent = max_concurrent
        self.semaphore = asyncio.Semaphore(max_concurrent)
        self.processed = 0
        self.start_time = None
        
    async def fetch_page(self, session: ClientSession, url: str):
        async with self.semaphore:  # 控制并发数
            try:
                async with session.get(url, timeout=30) as response:
                    html = await response.text()
                    # 这里处理页面内容,比如保存到文件或数据库
                    self.processed += 1
                    
                    if self.processed % 1000 == 0:
                        elapsed = time.time() - self.start_time
                        rate = self.processed / elapsed
                        logger.info(f"已处理 {self.processed} 个页面,速率: {rate:.2f} 页/秒")
                        
                    return html
            except Exception as e:
                logger.error(f"抓取失败 {url}: {e}")
                return None
    
    async def worker(self, session: ClientSession, url_queue: asyncio.Queue):
        while True:
            try:
                url = await url_queue.get()
                await self.fetch_page(session, url)
                url_queue.task_done()
            except asyncio.CancelledError:
                break
    
    async def run(self):
        self.start_time = time.time()
        
        # 读取URL列表
        with open(self.urls_file, 'r') as f:
            urls = [line.strip() for line in f if line.strip()]
        
        # 创建连接池,限制总连接数
        connector = TCPConnector(limit=1000, limit_per_host=50)
        
        async with ClientSession(connector=connector) as session:
            # 创建任务队列
            url_queue = asyncio.Queue(maxsize=10000)
            
            # 预先填充队列
            for url in urls[:1000000]:  # 限制前100万
                await url_queue.put(url)
            
            # 启动工作协程
            workers = [
                asyncio.create_task(self.worker(session, url_queue))
                for _ in range(self.max_concurrent)
            ]
            
            # 等待队列处理完成
            await url_queue.join()
            
            # 取消工作协程
            for w in workers:
                w.cancel()
            
            # 等待所有worker结束
            await asyncio.gather(*workers, return_exceptions=True)
        
        elapsed = time.time() - self.start_time
        logger.info(f"完成! 处理了 {self.processed} 个页面,总耗时: {elapsed:.2f} 秒")

# 使用示例
async def main():
    crawler = MassCrawler('urls.txt', max_concurrent=800)
    await crawler.run()

if __name__ == '__main__':
    asyncio.run(main())

几个关键点:

  1. 异步IO:用asyncio+aiohttp避免线程开销,一个事件循环就能处理大量并发连接
  2. 连接池管理:TCPConnector控制总连接数和单主机连接数,避免把目标服务器搞垮
  3. 信号量限流:用Semaphore控制最大并发数,防止内存爆掉
  4. 队列缓冲:用asyncio.Queue做生产消费者模式,平衡读写速度

要真正达到100万/天(约11.6页/秒),需要:

  • 足够带宽和IP资源
  • 目标服务器能承受这个压力
  • 考虑用代理池分散请求
  • 做好异常处理和重试机制

代码里留了扩展点,比如可以在fetch_page里加代理切换、内容解析、数据存储等。

总结:用异步IO配合连接池控制是关键。

> 不过奇怪,bloom 里没有公有方法来判断 URL 是否重复,我用的__contains__()方法,也可能是我没用对,不过判重效果是一样的。


大佬大佬。

瑞幸 哈哈

给大佬捏腿

围观大佬,学习 ing

Ping 没必要发送四次,可以自主设置。
Win: -n NUMBER
Linux: -c NUMBER

递咖啡

说两点用 go 来替代,你那去除不需要的页面头部功能效率提高 n 倍,除非能不用 chrome 的驱动自己实现一套才有可能比别人开发的效率高。

是的,这个方法可以

直接提取内容比直接拿 body 更好吧,body 里面有很多不需的 html 标签

嗯,也可以这样。根据项目的选择来。 有的需要原始数据。另外直接提取有可能某些 html tag 确实 或者匹配规则没考虑周全,可能会大面积出现提取错误

如果单 IP 你爬不了 100+就被 ban 了,效率直线下降。

谢谢大佬分享 学习了 最近也正卡在这里

反爬不错的网站,单 ip 爬不到这么多次。 降低单 IP 的单位时间抓取次数,有可能爬这么多。 但是这样单 IP 的抓取效率就太低了

靠 ADSL 拨号来切 IP,可能有一部分站在完美的理想状况下确实按你的方法能达到日抓百万。

但是大多数能够产生有利用价值数据的网站,这种方法都不太能实现单机日抓百万,

IP 切换开销 6S 一次,太久了。。

学习了

最近就在爬一个网站,大概有 1 亿个网页
感觉主要限制还是在于网站的反爬,而且它的反爬是一封就封 2-3 个月的那种
考虑稳定可靠,买了动态 IP 地址池,一个月 1000 块的样子
代理限制了最大 40 的 QPS,一天能爬差不多 300 多 W 的页面下来

get,tks

回到顶部