当前位置:首页>python>Python-单细胞RNA测序分析流程(含CellMarker自动注释-也可以手动人工注解)

Python-单细胞RNA测序分析流程(含CellMarker自动注释-也可以手动人工注解)

  • 2026-02-05 01:06:51
Python-单细胞RNA测序分析流程(含CellMarker自动注释-也可以手动人工注解)

python-单细胞RNA测序分析流程(含CellMarker自动注释--也可以手动人工注解

功能特点

  1. 自动下载CellMarker数据库 - 从官方网站自动下载Human cell markers数据

  2. 智能细胞类型注释 - 基于CellMarker数据库自动识别细胞类型

  3. 完整的QC流程 - 包括质控、双细胞去除

  4. scVI批次校正 - 使用scVI进行样本间整合

  5. 统计分析 - 细胞比例变化的统计检验

  6. 富集分析 - 支持KEGG/GO通路富集

  • 差异表达分析:比较control vs treat组各细胞类型的基因表达

  • 轨迹分析:使用PAGA或velocyto进行发育轨迹推断

  • 细胞通讯分析:使用CellPhoneDB分析细胞间相互作用

  • 拟时序分析:使用Monocle3或scVelo

安装依赖

pip install scanpy scvi-tools scrublet anndata pandas numpy scipy statsmodels gseapy requests openpyxl --break-system-packages

输入文件准备

1. Clinical信息表(必需)

创建一个CSV或TSV文件,至少包含两列:

示例:clinical.csv

sample,type
GSM6161374,control
GSM6161375,control
GSM6161376,treat
GSM6161377,treat
  • sample: 样本ID,需要与h5文件名匹配

  • type: 只能是 control 或 treat

2. 10x h5文件(必需)

将所有的 filtered_feature_bc_matrix.h5 文件放在同一个目录下。

文件命名示例:

GSM6161374_filtered_feature_bc_matrix.h5
GSM6161375_filtered_feature_bc_matrix.h5
GSM6161376_filtered_feature_bc_matrix.h5
GSM6161377_filtered_feature_bc_matrix.h5

使用方法

基本用法

python sc_analysis_with_cellmarker.py \
--clinical clinical.csv \
--h5_dir ./h5_files \
--outdir results

高级用法(指定组织类型)

如果你的样本来自特定组织(如肺、血液等),可以筛选CellMarker数据库:

python sc_analysis_with_cellmarker.py \
--clinical clinical.csv \
--h5_dir ./h5_files \
--outdir results_lung \
--tissue"Lung"

自定义QC参数

python sc_analysis_with_cellmarker.py \
--clinical clinical.csv \
--h5_dir ./h5_files \
--outdir results \
--min_genes500 \
--min_umis1000 \
--max_mt15.0 \
--doublet_rate0.08

使用本地CellMarker文件

python sc_analysis_with_cellmarker.py \
--clinical clinical.csv \
--h5_dir ./h5_files \
--outdir results \
--cellmarker_path ./Cell_marker_Human.xlsx

使用内置marker字典(无需下载)

python sc_analysis_with_cellmarker.py \
--clinical clinical.csv \
--h5_dir ./h5_files \
--outdir results \
--use_custom_markers

参数说明

必需参数

  • --clinical: Clinical信息文件路径

  • --h5_dir: 包含h5文件的目录

可选参数

输出

  • --outdir: 输出目录(默认:sc_out)

质控参数

  • --min_genes: 最小基因数(默认:300)

  • --min_umis: 最小UMI数(默认:500)

  • --max_mt: 最大线粒体比例(默认:20.0)

  • --doublet_rate: 双细胞预期比例(默认:0.06)

整合参数

  • --hvg: 高变基因数量(默认:3000)

  • --latent: scVI潜在维度(默认:30)

  • --epochs: 训练轮数(默认:200)

  • --resolution: Leiden聚类分辨率(默认:0.5)

CellMarker参数

  • --tissue: 组织类型筛选(如:Blood, Lung, Brain等)

  • --cellmarker_path: 本地CellMarker文件路径

  • --use_custom_markers: 使用内置marker字典

  • --min_markers: 每个细胞类型最小marker数(默认:2)

输出文件

运行完成后,在输出目录中会生成:

数据文件

  1. Cell_marker_Human.xlsx - 下载的CellMarker数据库

  2. marker_dictionary.csv - 使用的marker字典

  3. marker_matching_summary.csv - Marker匹配统计

  4. qc_summary.csv - QC统计摘要

  5. merged_after_qc.h5ad - QC后合并的数据

  6. cluster_markers.csv - 各cluster的marker基因

  7. celltype_proportions.csv - 细胞类型比例表

  8. prop_stats_mannwhitneyu.csv - Mann-Whitney U检验结果

  9. prop_stats_logit_ols.csv - Logit回归结果

  10. final_scvi_clustered_annotated.h5ad - 最终带注释的h5ad文件

可视化文件

  • umap_overview.png - UMAP总览图

  • umap_celltype_labeled.png - 细胞类型标注UMAP图

富集分析(可选)

  • enrichr_clusterX_kegg.csv - KEGG通路富集结果

分析摘要

  • analysis_summary.txt - 分析总结报告

工作流程

1. 下载CellMarker数据库
   ↓
2. 解析marker基因字典
   ↓
3. 读取clinical信息,匹配h5文件
   ↓
4. 逐样本QC(基因数、UMI数、线粒体比例)
   ↓
5. Scrublet双细胞检测
   ↓
6. 合并所有样本
   ↓
7. scVI批次校正
   ↓
8. 降维(UMAP)+ 聚类(Leiden)
   ↓
9. 基于CellMarker自动注释细胞类型
   ↓
10. 计算细胞比例 + 统计检验
   ↓
11. 通路富集分析
   ↓
12. 生成报告和可视化

注意事项

  1. 内存需求:大数据集可能需要较大内存(建议32GB+)

  2. 计算时间:scVI整合可能需要较长时间,取决于细胞数量

  3. CellMarker下载:首次运行会自动下载,如网络不稳定可提前下载

  4. 组织特异性:使用--tissue参数可以提高注释准确性

  5. GPU加速:scVI支持GPU,可显著加快速度

故障排除

问题1:下载CellMarker失败

解决:手动下载后使用 --cellmarker_path 参数,或使用 --use_custom_markers

问题2:内存不足

解决

  • 减少样本数量

  • 降低 --hvg 参数

  • 使用GPU版本的scVI

问题3:找不到匹配的h5文件

解决:确保h5文件名包含clinical表中的sample ID

问题4:细胞类型注释不准确

解决

  • 使用 --tissue 参数指定组织类型

  • 检查 marker_matching_summary.csv 看匹配的marker数量

  • 调整 --min_markers 参数

后续分析建议

  1. 差异表达分析:比较control vs treat组各细胞类型的基因表达

  2. 轨迹分析:使用PAGA或velocyto进行发育轨迹推断

  3. 细胞通讯分析:使用CellPhoneDB分析细胞间相互作用

  4. 拟时序分析:使用Monocle3或scVelo

#!/usr/bin/env python3# -*- coding: utf-8 -*-'''使用方法:python geo_sc_pipeline_cellmarker.py \  --clinical clinical.csv \  --h5_dir ./h5_files \  --outdir ./sc_out \  --tissue_filter Endometrium \  --tissue_mode tissue_type \  --seed 42'''import osimport argparseimport warningsfrom typing import DictListOptionalimport numpy as npimport pandas as pdimport scanpy as scimport anndata as adwarnings.filterwarnings("ignore")os.environ.pop("CUDA_VISIBLE_DEVICES"None)os.environ.pop("PL_TORCH_DISTRIBUTED_BACKEND"None)# 设置所有随机种子以确保可重复性import randomimport torch# 设置随机种子函数def set_all_seeds(seed=0):    """设置所有随机种子以确保可重复性"""    random.seed(seed)    np.random.seed(seed)    os.environ['PYTHONHASHSEED'] = str(seed)    # PyTorch 相关    torch.manual_seed(seed)    if torch.cuda.is_available():        torch.cuda.manual_seed(seed)        torch.cuda.manual_seed_all(seed)    # 确保可重复性    torch.backends.cudnn.deterministic = True    torch.backends.cudnn.benchmark = False# 在导入其他包之前设置种子set_all_seeds(0)import scviimport scrublet as scrimport statsmodels.api as smimport gseapy as gpimport requestsCELL_MARKER_HUMAN_URL = "http://www.bio-bigdata.center/CellMarker_download_files/file/Cell_marker_Human.xlsx"# -----------------------------# Clinical + h5 mapping# -----------------------------def read_clinical(clinical_path: str) -> pd.DataFrame:    if clinical_path.endswith(".csv"):        df = pd.read_csv(clinical_path)    else:        df = pd.read_table(clinical_path)    if "type" not in df.columns:        raise ValueError("clinical 文件必须包含列: type (control/treat)")    if "sample" not in df.columns:        raise ValueError("clinical 文件必须包含列: sample (用于匹配.h5文件名)")    df["type"] = df["type"].astype(str).str.lower()    if not set(df["type"].unique()).issubset({"control""treat"}):        raise ValueError(f"type列只能包含control/treat,你的type值为: {df['type'].unique()}")    return dfdef list_h5_files(h5_dir: str) -> List[str]:    files = [os.path.join(h5_dir, fn) for fn in os.listdir(h5_dir) if fn.endswith(".h5")]    if len(files) == 0:        raise FileNotFoundError(f"{h5_dir} 下找不到 .h5 文件")    return sorted(files)def match_sample_to_h5(clinical_df: pd.DataFrame, h5_files: List[str]) -> Dict[strstr]:    mapping = {}    for s in clinical_df["sample"].astype(str):        hits = [f for f in h5_files if s in os.path.basename(f)]        if len(hits) == 1:            mapping[s] = hits[0]        elif len(hits) == 0:            raise ValueError(f"sample={s} 在h5_dir里找不到匹配文件(文件名需包含sample)")        else:            raise ValueError(f"sample={s} 匹配到多个h5文件: {hits},请保证唯一")    return mapping# -----------------------------# QC + doublets# -----------------------------def add_qc_metrics(adata: ad.AnnData) -> None:    adata.var["mt"] = adata.var_names.str.startswith("MT-")    sc.pp.calculate_qc_metrics(adata, qc_vars=["mt"], inplace=True)def per_sample_qc_filter(adata: ad.AnnData, min_genes=300, min_umis=500, max_mt=20.0) -> ad.AnnData:    add_qc_metrics(adata)    keep = (        (adata.obs["n_genes_by_counts"] >= min_genes) &        (adata.obs["total_counts"] >= min_umis) &        (adata.obs["pct_counts_mt"] <= max_mt)    )    return adata[keep].copy()def run_scrublet(adata: ad.AnnData, expected_doublet_rate=0.06) -> ad.AnnData:    scrub = scr.Scrublet(adata.X, expected_doublet_rate=expected_doublet_rate)    scores, preds = scrub.scrub_doublets()    adata.obs["doublet_score"] = scores    adata.obs["predicted_doublet"] = preds.astype(bool)    return adatadef drop_doublets(adata: ad.AnnData) -> ad.AnnData:    if "predicted_doublet" not in adata.obs.columns:        return adata    return adata[~adata.obs["predicted_doublet"]].copy()def read_one_10x_h5(h5_path: str, sample_id: str, group: str) -> ad.AnnData:    a = sc.read_10x_h5(h5_path)    a.var_names_make_unique()    a.obs["sample"] = sample_id    a.obs["type"] = group    return a# -----------------------------# scVI integration + clustering# -----------------------------def integrate_scvi(adata: ad.AnnData, n_hvg=3000, latent_dim=30, max_epochs=200, seed=0) -> ad.AnnData:    sc.pp.filter_genes(adata, min_cells=3)    adata.layers["counts"] = adata.X.copy()    sc.pp.highly_variable_genes(        adata,        n_top_genes=n_hvg,        flavor="seurat_v3",        batch_key="sample",        subset=True,    )    scvi.model.SCVI.setup_anndata(adata, layer="counts", batch_key="sample")    model = scvi.model.SCVI(adata, n_latent=latent_dim)    # 新版本scvi-tools的训练参数    model.train(max_epochs=max_epochs,                 plan_kwargs={"lr"1e-3},                seed=seed,		accelerator="cpu",                devices=1)    adata.obsm["X_scVI"] = model.get_latent_representation()    return adatadef cluster_and_umap(adata: ad.AnnData, neighbors_k=15, resolution=0.5) -> ad.AnnData:    sc.pp.neighbors(adata, use_rep="X_scVI", n_neighbors=neighbors_k)    sc.tl.umap(adata)    sc.tl.leiden(adata, resolution=resolution, key_added="leiden")    return adatadef find_markers(adata: ad.AnnData, groupby="leiden", method="wilcoxon") -> pd.DataFrame:    sc.pp.normalize_total(adata, target_sum=1e4)    sc.pp.log1p(adata)    sc.tl.rank_genes_groups(adata, groupby=groupby, method=method)    return sc.get.rank_genes_groups_df(adata, group=None)# -----------------------------# CellMarker download + parse (YOUR FORMAT)# -----------------------------def download_file(url: str, out_path: str, force=False, timeout=60) -> str:    os.makedirs(os.path.dirname(out_path), exist_ok=True)    if os.path.exists(out_path) and (not force):        return out_path    r = requests.get(url, stream=True, timeout=timeout)    r.raise_for_status()    with open(out_path, "wb"as f:        for chunk in r.iter_content(chunk_size=1024 * 1024):            if chunk:                f.write(chunk)    return out_pathdef load_cellmarker_human_markers_from_xlsx(    xlsx_path: str,    tissue_filter: Optional[str] = None,    tissue_mode: str = "tissue_type",  # tissue_type or tissue_class    min_genes_per_celltype: int = 5,    max_genes_per_celltype: int = 80,) -> Dict[strList[str]]:    """    你的 Excel 列:species, tissue_class, tissue_type, cell_name, Symbol ...    我们用:      - cell_name 作为 celltype 名字      - Symbol 作为 gene symbol(大写)      - tissue_filter 可选过滤组织(包含匹配)    """    df = pd.read_excel(xlsx_path)  # default first sheet    needed = {"species""cell_name""Symbol""tissue_class""tissue_type"}    missing = needed - set(df.columns)    if missing:        raise ValueError(f"CellMarker表缺少列: {missing},现有列: {df.columns.tolist()}")    df = df[df["species"].astype(str).str.lower() == "human"].copy()    # tissue filter    if tissue_filter:        tf = tissue_filter.strip().lower()        if tissue_mode not in {"tissue_type""tissue_class"}:            raise ValueError("--tissue_mode 只能是 tissue_type 或 tissue_class")        df[tissue_mode] = df[tissue_mode].astype(str)        df = df[df[tissue_mode].str.lower().str.contains(tf, na=False)].copy()    df["cell_name"] = df["cell_name"].astype(str).str.strip()    df["Symbol"] = df["Symbol"].astype(str).str.strip().str.upper()    df = df[(df["cell_name"] != "") & (df["Symbol"] != "")]    marker_dict: Dict[strList[str]] = {}    for ct, sub in df.groupby("cell_name"):        genes = sub["Symbol"].dropna().unique().tolist()        genes = sorted(set(genes))        if len(genes) < min_genes_per_celltype:            continue        if len(genes) > max_genes_per_celltype:            genes = genes[:max_genes_per_celltype]        marker_dict[str(ct)] = genes    return marker_dict# -----------------------------# Auto annotation by CellMarker (cluster-level)# -----------------------------def score_celltypes_per_cell(adata: ad.AnnData, marker_dict: Dict[strList[str]], prefix="cm_") -> List[str]:    # ensure log1p exists    if "log1p" not in adata.uns_keys():        sc.pp.normalize_total(adata, target_sum=1e4)        sc.pp.log1p(adata)    score_cols = []    for ct, genes in marker_dict.items():        valid = [g for g in genes if g in adata.var_names]        if len(valid) < 3:            continue        col = f"{prefix}{ct}"        sc.tl.score_genes(adata, gene_list=valid, score_name=col, use_raw=False)        score_cols.append(col)    return score_colsdef assign_cluster_celltypes(    adata: ad.AnnData,    score_cols: List[str],    cluster_key="leiden",    prefix="cm_",    out_cluster_key="celltype_cluster",    out_cell_key="celltype_auto",) -> ad.AnnData:    if len(score_cols) == 0:        adata.obs[out_cluster_key] = "unknown"        adata.obs[out_cell_key] = "unknown"        return adata    df = adata.obs[[cluster_key] + score_cols].copy()    mean_scores = df.groupby(cluster_key)[score_cols].mean()    best_col = mean_scores.idxmax(axis=1)    cluster2ct = best_col.apply(lambda x: x[len(prefix):] if x.startswith(prefix) else x).to_dict()    adata.obs[out_cluster_key] = adata.obs[cluster_key].map(cluster2ct).astype("category")    adata.obs[out_cell_key] = adata.obs[cluster_key].map(cluster2ct).astype("category")    return adata# -----------------------------# Proportion shift# -----------------------------def compute_celltype_proportions(adata: ad.AnnData, celltype_key="celltype_auto") -> pd.DataFrame:    df = adata.obs[["sample""type", celltype_key]].copy()    tab = (        df.groupby(["sample""type", celltype_key])        .size()        .reset_index(name="n_cells")    )    totals = tab.groupby(["sample"])["n_cells"].sum().rename("n_total")    tab = tab.merge(totals, on="sample")    tab["prop"] = tab["n_cells"] / tab["n_total"]    return tabdef mannwhitneyu_by_celltype(prop_df: pd.DataFrame, celltype_col="celltype_auto") -> pd.DataFrame:    from scipy.stats import mannwhitneyu    out = []    for ct in prop_df[celltype_col].unique():        d = prop_df[prop_df[celltype_col] == ct]        c = d[d["type"] == "control"]["prop"].values        t = d[d["type"] == "treat"]["prop"].values        if len(c) >= 2 and len(t) >= 2:            _, p = mannwhitneyu(t, c, alternative="two-sided")            out.append([ct, len(c), len(t), np.mean(c), np.mean(t), p])    return pd.DataFrame(out, columns=["celltype""n_control""n_treat""mean_control""mean_treat""p_mannwhitneyu"])def beta_regression(prop_df: pd.DataFrame, celltype_col="celltype_auto") -> pd.DataFrame:    out = []    eps = 1e-4    for ct in prop_df[celltype_col].unique():        d = prop_df[prop_df[celltype_col] == ct].copy()        if d["type"].nunique() < 2:            continue        y = np.clip(d["prop"].values, eps, 1 - eps)        y_logit = np.log(y / (1 - y))        X = (d["type"].values == "treat").astype(int)        X = sm.add_constant(X)        model = sm.OLS(y_logit, X).fit()        out.append([ct, model.params[1], model.pvalues[1], model.rsquared])    return pd.DataFrame(out, columns=["celltype""coef_treat""p_value""r2"])# -----------------------------# Enrichment# -----------------------------def run_enrichr(gene_list: List[str], library="KEGG_2021_Human", outdir="enrichr_out") -> pd.DataFrame:    os.makedirs(outdir, exist_ok=True)    enr = gp.enrichr(gene_list=gene_list, gene_sets=library, organism="Human", outdir=outdir, no_plot=True)    return enr.results# -----------------------------# Main# -----------------------------def main():    parser = argparse.ArgumentParser("GEO scRNA pipeline: QC + scrublet + scVI + CellMarker auto annotation")    parser.add_argument("--clinical", required=True)    parser.add_argument("--h5_dir", required=True)    parser.add_argument("--outdir", default="sc_out")    parser.add_argument("--min_genes"type=int, default=300)    parser.add_argument("--min_umis"type=int, default=500)    parser.add_argument("--max_mt"type=float, default=20.0)    parser.add_argument("--doublet_rate"type=float, default=0.06)    parser.add_argument("--hvg"type=int, default=3000)    parser.add_argument("--latent"type=int, default=30)    parser.add_argument("--epochs"type=int, default=200)    parser.add_argument("--resolution"type=float, default=0.5)    # CellMarker options    parser.add_argument("--cellmarker_cache_dir", default="cellmarker_db")    parser.add_argument("--tissue_filter", default=Nonehelp='e.g. "Endometrium" or "Uterus" (recommended)')    parser.add_argument("--tissue_mode", default="tissue_type", choices=["tissue_type""tissue_class"])    parser.add_argument("--cm_min_genes"type=int, default=5)    parser.add_argument("--cm_max_genes"type=int, default=80)    parser.add_argument("--seed"type=int, default=0help="随机种子")    args = parser.parse_args()    os.makedirs(args.outdir, exist_ok=True)    # 设置随机种子    set_all_seeds(args.seed)    # clinical + h5 mapping    clinical = read_clinical(args.clinical)    h5_files = list_h5_files(args.h5_dir)    mapping = match_sample_to_h5(clinical, h5_files)    # per-sample QC + scrublet    adatas = []    qc_rows = []    for _, row in clinical.iterrows():        sample = str(row["sample"])        group = str(row["type"]).lower()        h5 = mapping[sample]        print(f"[Load] {sample} ({group}) <- {os.path.basename(h5)}")        a = read_one_10x_h5(h5, sample, group)        n0 = a.n_obs        a = per_sample_qc_filter(a, args.min_genes, args.min_umis, args.max_mt)        n1 = a.n_obs        a = run_scrublet(a, args.doublet_rate)        d0 = int(a.obs["predicted_doublet"].sum())        a = drop_doublets(a)        n2 = a.n_obs        qc_rows.append([sample, group, n0, n1, d0, n2])        adatas.append(a)    qc_df = pd.DataFrame(qc_rows, columns=["sample""type""cells_raw""cells_after_qc""doublets_removed""cells_final"])    qc_df.to_csv(os.path.join(args.outdir, "qc_summary.csv"), index=False)    # merge    adata = ad.concat(adatas, join="outer", label="sample", keys=[a.obs["sample"][0for a in adatas], fill_value=0)    adata.obs["type"] = adata.obs["type"].astype(str).str.lower()    adata.write_h5ad(os.path.join(args.outdir, "merged_after_qc.h5ad"))    # scVI - 移除原有的 scvi.settings.seed = 0    print("[scVI] Running integration...")    adata = integrate_scvi(adata, n_hvg=args.hvg, latent_dim=args.latent, max_epochs=args.epochs, seed=args.seed)    # cluster    print("[Clustering] Running clustering and UMAP...")    adata = cluster_and_umap(adata, neighbors_k=15, resolution=args.resolution)    # markers    print("[Markers] Finding cluster markers...")    markers = find_markers(adata, groupby="leiden")    markers.to_csv(os.path.join(args.outdir, "cluster_markers.csv"), index=False)    # download + parse CellMarker    cm_cache = os.path.join(args.outdir, args.cellmarker_cache_dir)    os.makedirs(cm_cache, exist_ok=True)    cm_xlsx = os.path.join(cm_cache, "Cell_marker_Human.xlsx")    print(f"[CellMarker] downloading/caching: {cm_xlsx}")    download_file(CELL_MARKER_HUMAN_URL, cm_xlsx, force=False)    marker_dict = load_cellmarker_human_markers_from_xlsx(        cm_xlsx,        tissue_filter=args.tissue_filter,        tissue_mode=args.tissue_mode,        min_genes_per_celltype=args.cm_min_genes,        max_genes_per_celltype=args.cm_max_genes,    )    print(f"[CellMarker] usable celltypes: {len(marker_dict)}")    # score + assign cluster celltypes    print("[CellMarker] Scoring cell types...")    score_cols = score_celltypes_per_cell(adata, marker_dict, prefix="cm_")    adata = assign_cluster_celltypes(adata, score_cols, cluster_key="leiden", prefix="cm_",                                     out_cluster_key="celltype_cluster", out_cell_key="celltype_auto")    # export mapping    ct_map = adata.obs[["leiden""celltype_cluster"]].drop_duplicates().sort_values("leiden")    ct_map.to_csv(os.path.join(args.outdir, "cluster_to_celltype.csv"), index=False)    # proportions + stats    print("[Statistics] Computing cell type proportions...")    prop = compute_celltype_proportions(adata, celltype_key="celltype_auto")    prop.to_csv(os.path.join(args.outdir, "celltype_proportions.csv"), index=False)    prop_u = mannwhitneyu_by_celltype(prop, celltype_col="celltype_auto")    prop_u.to_csv(os.path.join(args.outdir, "prop_stats_mannwhitneyu.csv"), index=False)    prop_b = beta_regression(prop, celltype_col="celltype_auto")    prop_b.to_csv(os.path.join(args.outdir, "prop_stats_logit_ols.csv"), index=False)    # save    adata.write_h5ad(os.path.join(args.outdir, "final_scvi_cellmarker_annotated.h5ad"))    # plots    sc.settings.figdir = args.outdir    print("[Plotting] Creating UMAP plots...")    sc.pl.umap(adata, color=["type""sample""leiden""celltype_auto"],                wspace=0.4, show=False, save="_overview.png")    print("=" * 50)    print("DONE. Results saved in:", args.outdir)    print("=" * 50)if __name__ == "__main__":    main()

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-07 18:32:25 HTTP/2.0 GET : https://f.mffb.com.cn/a/472725.html
  2. 运行时间 : 0.202951s [ 吞吐率:4.93req/s ] 内存消耗:4,568.38kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=e8b7e362e9bdcee01f1936a1516e7a0d
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001157s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001469s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000694s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000665s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001236s ]
  6. SELECT * FROM `set` [ RunTime:0.000499s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001482s ]
  8. SELECT * FROM `article` WHERE `id` = 472725 LIMIT 1 [ RunTime:0.002653s ]
  9. UPDATE `article` SET `lasttime` = 1770460345 WHERE `id` = 472725 [ RunTime:0.022843s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.001535s ]
  11. SELECT * FROM `article` WHERE `id` < 472725 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001136s ]
  12. SELECT * FROM `article` WHERE `id` > 472725 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001162s ]
  13. SELECT * FROM `article` WHERE `id` < 472725 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.002925s ]
  14. SELECT * FROM `article` WHERE `id` < 472725 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.002668s ]
  15. SELECT * FROM `article` WHERE `id` < 472725 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.002722s ]
0.206674s