Python中如何编写一个简单的分布式定时爬虫

  • 待抓取和已抓取(存放 url 的 hash )的 url 队列存储在 redis
    + 抓取结果放在 mongo
    + spider 和 master 之间用 rpc 框架通信( thrift 的 python 版本 thriftpy )

    目前实现了是抓取点评 poi 详情页面的名称 地址 电话 3 个字段,后续再继续其他定下抓取的功能


    项目地址: https://github.com/linzhi/minerva

    爬虫的稳定性方面还有待提升,后续慢慢优化
    Python中如何编写一个简单的分布式定时爬虫

8 回复

有个问题请教下大家,仿照 https://github.com/xchaoinfo/fuck-login 写的知乎模拟登录,一直报验证码无效的 msg ,即使用个错误的密码也是报这个,代码在 https://github.com/linzhi/minerva/blob/master/minerva/zhihu.py ,不知道为啥呢


要写一个简单的分布式定时爬虫,可以用Celery + Redis + Beat的组合。这里给你一个完整可运行的示例:

首先安装依赖:

pip install celery redis requests beautifulsoup4

然后创建爬虫任务文件 crawler_tasks.py

from celery import Celery
import requests
from bs4 import BeautifulSoup
import time
from datetime import timedelta

# 创建Celery应用,使用Redis作为消息代理
app = Celery('crawler_tasks', 
             broker='redis://localhost:6379/0',
             backend='redis://localhost:6379/0')

# 爬虫任务
@app.task
def crawl_website(url):
    """爬取指定网页的任务"""
    try:
        print(f"开始爬取: {url}")
        response = requests.get(url, timeout=10)
        response.raise_for_status()
        
        soup = BeautifulSoup(response.text, 'html.parser')
        
        # 这里简单提取标题作为示例
        title = soup.title.string if soup.title else "无标题"
        print(f"爬取完成: {url} - 标题: {title}")
        
        return {
            'url': url,
            'title': title,
            'status': 'success',
            'timestamp': time.time()
        }
    except Exception as e:
        print(f"爬取失败 {url}: {str(e)}")
        return {
            'url': url,
            'error': str(e),
            'status': 'failed',
            'timestamp': time.time()
        }

# 批量爬取任务
@app.task
def batch_crawl(urls):
    """批量爬取多个网页"""
    results = []
    for url in urls:
        result = crawl_website.delay(url)  # 异步执行
        results.append(result)
    return [result.get() for result in results]

创建定时任务配置 celery_config.py

from datetime import timedelta

# Celery配置
broker_url = 'redis://localhost:6379/0'
result_backend = 'redis://localhost:6379/0'

# 定时任务配置
beat_schedule = {
    'crawl-every-30-minutes': {
        'task': 'crawler_tasks.crawl_website',
        'schedule': timedelta(minutes=30),  # 每30分钟执行一次
        'args': ('https://example.com',),   # 要爬取的URL
    },
    'daily-crawl': {
        'task': 'crawler_tasks.batch_crawl',
        'schedule': timedelta(days=1),      # 每天执行一次
        'args': ([
            'https://news.example.com',
            'https://blog.example.com',
            'https://api.example.com/data'
        ],),
    },
}

启动步骤:

  1. 启动Redis(如果还没启动):
redis-server
  1. 启动Celery worker(在不同机器或终端启动多个worker实现分布式):
# Worker 1
celery -A crawler_tasks worker --loglevel=info --hostname=worker1@%h

# Worker 2(在另一台机器或终端)
celery -A crawler_tasks worker --loglevel=info --hostname=worker2@%h
  1. 启动Celery beat(定时调度器)
celery -A crawler_tasks beat --loglevel=info

这个方案的核心优势:

  • 分布式:可以启动多个worker在不同机器上并行处理任务
  • 定时调度:通过Celery Beat实现灵活的定时任务
  • 任务队列:Redis作为消息队列,任务自动分配给空闲worker
  • 容错性:失败的任务可以重试,worker崩溃不影响整体系统

如果要扩展,可以:

  • 添加任务结果存储到数据库
  • 实现任务优先级
  • 添加任务去重机制
  • 配置任务重试策略

用Celery+Redis是Python分布式爬虫的经典方案。

知道原因了。。。获取验证码的 url https://www.zhihu.com/captcha.gif?r=1492661961962&type=login ,即使中间的参数 r (时间戳)是一样的,获取到的验证码也不一样

再次更新。。抓取知乎的问题&答案 不需要模拟登陆。。。。之前方向错了

我想问问为什么我写的爬虫运行一段时间就会报 requests.exceptions.ConnectionError: (‘Connection aborted.’, BadStatusLine("’’",)),用的 python 和 requests 库,我没有使用多线程,在请求之前都添加了 time.sleep(0.5) ,按说不至于请求太频繁啊,请问这个问题该怎么解决啊!谢谢了,找了好久答案也没辙

我看了那个解决方案,应该不是那个问题,要贴异常代码吗?还是程序代码?
Traceback (most recent call last):
File “zhihuSprider.py”, line 306, in <module>
sprider.bfs_search()
File “zhihuSprider.py”, line 286, in bfs_search
self.analyze_user(user_url, followee_url, follower_url)
File “zhihuSprider.py”, line 155, in analyze_user
result = json.loads(self.get_user_data(user_url))
File “zhihuSprider.py”, line 144, in get_user_data
response = self.session.get(url, headers=self.headers)
File “D:\Python27\lib\site-packages\requests<a target=”_blank" href=“http://sessions.py” rel=“nofollow noopener”>sessions.py", line 501, in get
return self.request(‘GET’, url, **kwargs)
File “D:\Python27\lib\site-packages\requests<a target=”_blank" href=“http://sessions.py” rel=“nofollow noopener”>sessions.py", line 488, in request
resp = self.send(prep, **send_kwargs)
File “D:\Python27\lib\site-packages\requests<a target=”_blank" href=“http://sessions.py” rel=“nofollow noopener”>sessions.py", line 609, in send
r = adapter.send(request, **kwargs)
File “D:\Python27\lib\site-packages\requests<a target=”_blank" href=“http://adapters.py” rel=“nofollow noopener”>adapters.py", line 473, in send
raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: (‘Connection aborted.’, BadStatusLine("’’",))

有代码链接么

回到顶部