You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tweepy多线程爬取粉丝ID时如何保存游标实现断点续爬

问题答复

1. 整数类型游标、逐页变化属于完全正常的表现

  • Twitter V1.1 系列分页接口返回的分页标识本身就是整数类型,Tweepy没有对这个字段做额外封装,直接透传了API返回值,所以你拿到int类型的next_cursor是符合预期的,不是代码问题。
  • 游标值逐页变动是分页逻辑的正常行为:每一页请求返回的next_cursor对应当前分页的终点位置,下次请求带这个值就能从断点继续拉取,当next_cursor等于0时,代表已经拉取完所有粉丝数据,没有更多分页。

2. 传入历史游标实现ID拉取续爬的方法

因为游标本身就是可序列化的整数,不需要保存整个Cursor对象,只要每次拉完一页把当前next_cursor持久化到本地,下次启动时把这个值作为cursor参数传入Cursor初始化方法即可。
核心实现逻辑:
首先写通用的断点读写方法,把游标、已拉取的ID、已转换的用户名进度都存在本地,避免脚本退出后内存数据丢失:

import os
import json

# 按爬取账号定义独立的存储文件,避免多账号数据混淆
STATUS_FILE = f"{name}_crawl_state.json"
IDS_SAVE_FILE = f"{name}_follower_ids.txt"
NAMES_SAVE_FILE = f"{name}.txt"

def load_state():
    # 默认初始状态:游标为0代表从头爬取,已爬ID列表为空,已转换用户名数量为0
    default_state = {
        "last_cursor": 0,
        "finished_ids": 0,
        "finished_names": 0
    }
    # 读取上次保存的状态
    if os.path.exists(STATUS_FILE):
        with open(STATUS_FILE, "r", encoding="utf-8") as f:
            saved = json.load(f)
            default_state.update(saved)
    return default_state

def save_state(last_cursor=None, add_ids=None, add_names=None):
    current_state = load_state()
    # 更新游标
    if last_cursor is not None:
        current_state["last_cursor"] = last_cursor
    # 追加写入新拉取的ID
    if add_ids:
        with open(IDS_SAVE_FILE, "a", encoding="utf-8") as f:
            for uid in add_ids:
                f.write(f"{uid}\n")
        current_state["finished_ids"] += len(add_ids)
    # 追加写入新转换的用户名
    if add_names:
        with open(NAMES_SAVE_FILE, "a", encoding="utf-8") as f:
            for uname in add_names:
                f.write(f"{uname}\n")
        current_state["finished_names"] += len(add_names)
    # 保存状态到本地
    with open(STATUS_FILE, "w", encoding="utf-8") as f:
        json.dump(current_state, f, ensure_ascii=False)

修改原有的get_ids函数,启动时加载历史游标,每拉完一页就保存进度:

def get_ids():
    state = load_state()
    start_cursor = state["last_cursor"]
    # 如果游标为0且本地已有ID数据,说明ID已经全部拉完,直接标记完成
    if start_cursor == 0 and state["finished_ids"] > 0:
        finished.append("True")
        return
    # 初始化分页器时传入上次保存的游标,直接从断点位置开始拉
    paginator = tweepy.Cursor(
        api.get_follower_ids,
        screen_name=name,
        count=5000,
        cursor=start_cursor
    ).pages()
    while True:
        try:
            page = paginator.next()
            new_cursor = paginator.next_cursor
            # 保存本页拉到的ID和新游标
            save_state(last_cursor=new_cursor, add_ids=page)
            # 同步到线程共享列表
            user_followers.extend(page)
            # 游标为0代表所有ID拉取完成
            if new_cursor == 0:
                break
        except:
            # 触发限流、网络报错时直接退出,下次启动从上次保存的位置继续
            break
    finished.append("True")

3. 完整续爬的补充优化

你原有代码除了ID拉取没有断点,用户名转换部分也存在重复爬取的问题,需要同步修改才能实现真正的无重复续爬:

  • 启动时先把本地已经存好的ID加载到内存的user_followers列表,避免两个线程初始数据不一致;同时统计已经写入文件的用户名数量,get_names函数每次只取还没转换的ID批量查询,写入文件用追加模式,不要每次全量覆写,避免重复消耗lookup_users的限流额度。
  • 进度保存时机选在每拉完一页ID、每转换完一批用户名之后立刻写入磁盘,不要等全部任务完成再存,最多只会丢失最后一批未保存的进度,不会出现从头爬取的问题。
  • 不需要尝试序列化整个Tweepy的Cursor对象,只要存整数类型的游标值就足够恢复分页位置,序列化对象反而会因为版本、API属性变动出现兼容问题。

内容的提问来源于stack exchange,提问作者Mohammed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 22:33:25