资讯动态

Python大众点评数据可视化:从爬虫到pyecharts的完整实战

发布时间:2026/9/12 12:53:42 来源:尧图企业网站定制
简介针对大众点评数据可视化分析需求这套完整的Python实战源码包面向数据分析初学者与爬虫爱好者以真实商家及评论数据为对象演示从环境准备、数据采集到可视化呈现的完整项目流程。压缩包共99个文件整体大小约14.89MB内部包含8个Python脚本、29个Excel数据表、12个CSV数据文件、15个HTML可视化页面以及中文词云字体、SQL文件、说明文档等结构清晰便于按流程分步查阅与复用。目前已有269人学习下载。内容覆盖爬虫请求与解析、数据清洗与存储、商家星级和评论数量统计、门店位置分布、菜品分类、Top10排行及评论词云等关键环节主要使用requests、BeautifulSoup、pandas、matplotlib、wordcloud等库实现并配有完整运行结果。通过这套源码学习者既能获得可直接运行的爬虫和分析代码也能掌握数据可视化模板与商业数据采集的排错思路适合作为课程设计、毕业设计或商业分析项目的实用参考。1. 用Python做大众点评数据可视化分析难点不在画图想用Python把大众点评上的店铺数据变成一张能说明问题的图大部分人的第一反应是先找个公开数据集然后开始画。真正跑起来你会发现数据获取是第一个坎点评对非登录状态和异常请求的拦截非常灵敏隔几分钟就可能弹验证码数据清洗是第二个坎评论文本里的“刚刚”“昨天”以及PingFang、emoji混排会把时间字段和统计口径一起搅乱最后才是可视化而pyecharts、matplotlib在坐标轴、地图geojson、词云字体上的细节每一个都能消耗半天。这篇文章按数据采集、清洗、可视化、关联分析、落地运行五个环节把链路完整走一遍。适合想学Python爬虫与数据可视化配合使用的人也适合需要做店铺经营分析的运营和技术同学。示例只做学习研究用途抓取频率和范围请严格限制。2. 大众点评数据采集requests绕过基础反爬并解析店铺信息2.1 大众点评的反爬机制与前置准备大众点评的反爬并不是单点防御而是“UA检测 Cookie校验 访问频率 行为分析”的组合。最直观的表现是无登录状态下搜索列表页大概率直接跳转验证码短时间连续翻页会被限流即使带上Cookie超过一定频率后也会出现“访问过于频繁”的提示页。第一次做的人容易把全部精力放在UA伪装上实际效果有限因为服务端真正看重的是会话是否来自真实浏览器操作链。常见做法是先在浏览器里登录大众点评打开开发者工具从Network面板复制请求里的Cookie和User-Agent再通过Python的requests库构造一个带完整头的请求。动手前先把Python环境装好建议3.10以上版本日常调试推荐VSCode断点时能直接观察resp.text里的验证码标记比print大法高效得多。反爬维度触发条件应对参数UA无UA或Python默认UA随机UA 完整浏览器的Accept-LanguageCookie未登录、过期、异地登录登录后Cookie注入Session定期更新频率单IP单位时间请求过多random.uniform随机延时1.53.5秒行为翻页间隔完全一致延时随机化偶尔模拟滚动停歇2.2 用requests拉取页面Cookie注入与随机延时带上Cookie以后请求才能拿到完整的店铺列表。直接用requests.get也能跑但每次都要手动带Cookie和Header代码会散乱。我更习惯创建一个requests.Session把Cookie和Header统一挂到session上后续所有请求自动携带。import requests import time import random from fake_useragent import UserAgent def create_session(cookie_str: str) - requests.Session: session requests.Session() ua UserAgent() headers { User-Agent: ua.random, Referer: https://www.dianping.com/, Accept-Language: zh-CN,zh;q0.9,en;q0.8, } # 把浏览器里复制的Cookie转成字典再注入Session cookie_dict dict(item.split(, 1) for item in cookie_str.split(; ) if in item) session.headers.update(headers) session.cookies.update(cookie_dict) return session def fetch_page(session: requests.Session, url: str): # 随机延时不是越久越好1.5-3.5秒的抖动更接近人工浏览节奏 time.sleep(random.uniform(1.5, 3.5)) resp session.get(url, timeout10) if resp.status_code 403: print(IP被临时限流等待60秒) time.sleep(60) return None if 验证码 in resp.text: print(触发验证码需要更换Cookie或降低频率) return None return resp.text代码里有两个关键参数值得说明random.uniform(1.5, 3.5)是请求间隔的抖动区间不要用time.sleep(2)这种固定值固定间隔反而更容易被行为分析识别timeout10是连接超时大众点评页面平均响应在几百毫秒10秒足够但如果代理或网络抖动严重可以放宽到15。fetch_page返回None后调用方要停止翻页并退出因为继续请求只会加重封禁。2.3 用BeautifulSoup解析店铺列表选择器容错与字段提取拿到HTML后下一步是解析店铺卡片。大众点评的列表页结构是div#shop-all-list下面每个li代表一家店铺字段集中在h4、div.star-view、span.review-num、span.price这些节点里。这里要处理的两个坑一是部分店铺缺少评分或评论数二是改版后class名会带动态后缀CSS选择器要写得宽容一些。from bs4 import BeautifulSoup def parse_shop_list(html: str) - list[dict]: soup BeautifulSoup(html, lxml) shops [] items soup.select(div#shop-all-list li) for item in items: name_tag item.select_one(h4) if not name_tag: continue # 结构可能变更跳过不影响整体 name name_tag.get_text(stripTrue) rating_tag item.select_one(div.star-view span) # 点评的评分以 title 属性形式存在如 title4.5 rating rating_tag.get(title) if rating_tag else None review_cnt_tag item.select_one(span.review-num) review_cnt review_cnt_tag.get_text(stripTrue) if review_cnt_tag else None price_tag item.select_one(span.price) price price_tag.get_text(stripTrue) if price_tag else None shops.append({ name: name, rating: rating, review_count: review_cnt, price: price, }) return shops这里选择器的设计思路是“先取父节点再用select_one精准定位取不到就置空”。rating用title属性而不是文本是因为星标的视觉效果由CSS生成页面文本里不一定有数字review_count取出后通常是“1234条点评”这样的字符串需要到清洗阶段再处理。如果点评后来改了DOM结构select匹配不到会返回空列表而不是直接抛异常这也是用lxml做解析时比较稳的一点。2.4 落盘CSV编码与增量基础解析结果不能只在内存里第一步落盘建议用CSV因为它可以直接用Excel打开做人工抽查。写文件时编码选utf-8-sig这样Windows上的Excel打开不会乱码。每次写入前先判断文件是否存在存在则用追加模式这为后面的增量爬取留好了接口。import os import pandas as pd def save_shop_data(shops: list[dict], filepath: str dianping_shops.csv): df pd.DataFrame(shops) if not os.path.exists(filepath): df.to_csv(filepath, indexFalse, encodingutf-8-sig) else: old_df pd.read_csv(filepath, encodingutf-8-sig) merged pd.concat([old_df, df], ignore_indexTrue) # 以店铺名称地址做初步去重避免重复爬取时累加脏数据 merged.drop_duplicates(subset[name], keeplast, inplaceTrue) merged.to_csv(filepath, indexFalse, encodingutf-8-sig)save_shop_data的追加逻辑并不复杂真正的用途是应对中断爬取过程中一旦触发验证码或断网已抓到的数据已经落盘下次重启不用从头开始。drop_duplicates(subset[name])只做初步去重因为不同分店可能同名所以到清洗阶段还要加入地址字段做二次去重。此时采集链路已经打通下一步就是把这些原始字符串变成可计算的结构化数据。3. 大众点评数据清洗与特征工程pandas把脏数据变成分析输入3.1 去重、类型强制转换与正则清理从HTML里拿到的字段几乎全是字符串评分是4.5评论数是1234条点评价格是¥150。这些直接画图会报错或产生错误坐标轴必须统一做类型转换。另外列表页翻页较多时同一家店可能被抓两次name加district或地址一起做subset去重才是可靠的。import pandas as pd def clean_shop_df(df: pd.DataFrame) - pd.DataFrame: df df.copy() # 评分转数值无法解析的置空 df[rating] pd.to_numeric(df[rating], errorscoerce) # 评论数去掉“条点评”等非数字字符 df[review_count] df[review_count].astype(str).str.replace(r[^0-9], , regexTrue) df[review_count] pd.to_numeric(df[review_count], errorscoerce) # 价格只保留数字部分单位是元 df[price] df[price].astype(str).str.replace(r[^0-9], , regexTrue) df[price] pd.to_numeric(df[price], errorscoerce) # 店铺地址非空才保留 df df.dropna(subset[name, rating]) df df.drop_duplicates(subset[name, address], keeplast) return df清洗的优先级有讲究先转类型再去重。如果先按name去重两个不同地址的“海底捞(人民广场店)”和“海底捞(五角场店)”会被错误合并先转类型再以nameaddress去重就能把同品牌不同实体店区分开。errorscoerce的作用是遇到暂无评分这类文本时直接返回NaN而不是报错中断整个清洗流程。脏数据形态清洗方法说明4.5分str.replace(r[^0-9.], , regexTrue)保留小数点1234条点评str.replace(r[^0-9], , regexTrue)去掉非数字¥150/人str.replace(r[^0-9], , regexTrue)只取金额空白字符串df.replace(, pd.NA)转为缺失值统一处理3.2 解析评论文本里的相对时间与时段特征点评的评论时间不稳定有2025-03-14 12:30这种标准格式也有刚刚、5分钟前、昨天这种相对表达。pd.to_datetime只认绝对时间因此要先做一个映射函数把相对时间换算成绝对时间再批量解析。这一步不做后续“周末消费高峰”“夜宵时段”这类分析就无从谈起。import re import pandas as pd def parse_dp_time(s, nowNone): if now is None: now pd.Timestamp.now() s str(s).strip() if 刚刚 in s: return now match re.search(r(\d)分钟前, s) if match: return now - pd.Timedelta(minutesint(match.group(1))) match re.search(r(\d)小时前, s) if match: return now - pd.Timedelta(hoursint(match.group(1))) if 昨天 in s: return now - pd.Timedelta(days1) return pd.to_datetime(s, errorscoerce) df[publish_time] df[comment_time].apply(parse_dp_time) df[hour] df[publish_time].dt.hour df[weekday] df[publish_time].dt.day_name()parse_dp_time里用了两次re.search分别匹配分钟和小时注意要把数字部分用(\d)括号捕获再转成int参与Timedelta计算。now参数默认取当前时间但测试时建议手传一个固定时间否则结果会随时间漂移。解析后的hour可以直接用于后续按“早市/午市/晚市/夜宵”分桶weekday则可以跟评分做汇总看出不同工作日的口碑差异。3.3 三维评分合成与权重设定大众点评有“口味、环境、服务”三个子评分但列表页不一定全部暴露详情页里则通常齐全。三个维度直接画雷达图是可以的但如果你想用一个指标给店铺排序就需要合成综合分。没有公开的官方权重常见的做法是口味0.6、环境0.25、服务0.15这个权重偏向“好吃优先”适合餐饮类分析。如果分析对象是美容美发权重就要改成环境和服务为主。def compose_score(row): # 三个维度任一缺失时退回整体评分 if pd.isna(row[taste_score]) or pd.isna(row[environment_score]) or pd.isna(row[service_score]): return row[rating] return round(0.6 * row[taste_score] 0.25 * row[environment_score] 0.15 * row[service_score], 2) df[composite_score] df.apply(compose_score, axis1)compose_score是逐行调用的函数axis1表示按行遍历。这里有个容易被忽略的细节如果三个子评分都缺失0.6*NaN0.25*NaN0.15*NaN的结果还是NaN一组店全变成空值所以必须用pd.isna判断并回退到rating。权重本身是经验值不做敏感性分析的话可以直接写死在函数里但建议把三个权重定义为模块级常量方便后续做0.5/0.3/0.2之类的对比实验。3.4 用关键词规则给评论打消费场景标签评论里“闺蜜”“带孩子”“公司聚餐”这些词直接指向消费场景把它们抽出来做成标签就能回答“这家店到底吸引哪类人群”的问题。最简单有效的是关键词规则准备一组场景词表用str.contains逐条匹配命中则标记。不用一上来就上BERT规则方法在小样本下解释性更强调试也直观。scene_rules { 情侣约会: [约会, 情人节, 纪念日], 朋友聚餐: [朋友, 聚餐, 聚会], 家庭聚会: [全家, 带孩子, 老人, 家人], 商务宴请: [商务, 客户, 谈事, 宴请], } def tag_scene(text: str) - str: if not isinstance(text, str): return 未分类 for scene, keywords in scene_rules.items(): if any(kw in text for kw in keywords): return scene return 未分类 df[scene] df[comment_excerpt].apply(tag_scene)关键词匹配的精度依赖词表覆盖率实际使用中可以看到“带孩子”和“家庭聚会”之间经常互为干扰这属于正常现象。any(kw in text for kw in keywords)是逐词做子串匹配一旦命中就返回场景并终止循环。这里的顺序有讲究越具体的场景放越前面比如“商务宴请”里的“谈事”必须优先于“朋友聚餐”里的“聚会”否则商务评论会被归到朋友聚餐。规则表的维护是个持续过程第一次跑完统计未分类的占比迭代补充词表即可。4. 大众点评数据可视化pyecharts与matplotlib的搭配方案4.1 评分与价格分布的直方图和饼图中文显示与matplotlib基础可视化的第一步不是炫图而是把数据分布看清楚。评分集中在3.5到5.0价格带从几十到上千直方图和饼图分别承担数值分布和占比展示。matplotlib画这类图最顺手但中文乱码是绕不过去的问题需要在代码开头指定中文字体否则SimHei或PingFang SC会出现豆腐块。import matplotlib import matplotlib.pyplot as plt matplotlib.rcParams[font.sans-serif] [PingFang SC, SimHei, Microsoft YaHei] matplotlib.rcParams[axes.unicode_minus] False plt.figure(figsize(10, 6)) # bins 按0.1分档能看出评分集中在4.5左右的长尾结构 plt.hist(df[rating], bins[3.5 i * 0.1 for i in range(11)], edgecolorblack, alpha0.75) plt.title(大众点评店铺评分分布) plt.xlabel(评分) plt.ylabel(店铺数量) plt.tight_layout() plt.savefig(rating_dist.png, dpi150)rcParams[font.sans-serif]传入的是一个字体列表matplotlib会按顺序查找第一个可用的字体这种方式在Windows和macOS上都能适配。bins手动指定为3.5到5.0每隔0.1一档是因为大众点评评分下限基本是3.5默认分箱会产生大量空区间。dpi150是按出图质量设定的屏幕看150足够打印或嵌入报告建议提到200。饼图更适合看价格带占比比如把价格切成0-50、50-100、100-200、200-500、500五档。分组用pd.cut再value_counts()取占比具体画法不赘述但要注意饼图里占比不足3%的组要合并为“其他”否则图例过密影响可读性。4.2 pyecharts输出可交互HTML柱状图与坐标轴间隔matplotlib出的是静态图适合放报告pyecharts出的是HTML鼠标悬停有数据提示适合做内部看板和大屏。pyecharts本质是echarts的Python封装可以直接复用echarts的配置思维。柱状图是使用频次最高的图形这里以“各商圈店铺数量”为例。from pyecharts.charts import Bar from pyecharts import options as opts count_by_area df[area].value_counts().sort_values(ascendingFalse) bar ( Bar(init_optsopts.InitOpts(width1000px, height600px)) .add_xaxis(count_by_area.index.tolist()) .add_yaxis(店铺数, count_by_area.values.tolist(), category_gap30%) .set_global_opts( title_optsopts.TitleOpts(title大众点评商圈店铺数量TOP15), xaxis_optsopts.AxisOpts( axislabel_optsopts.LabelOpts(rotate30, interval0) ), yaxis_optsopts.AxisOpts(name店铺数量), ) .set_series_opts(label_optsopts.LabelOpts(positiontop)) .render(bar_area.html) )interval0是关键参数它强制x轴显示所有标签否则pyecharts默认会隔几个显示一个商圈少时看不出问题商圈一多标签就会重叠。rotate30用来旋转标签缓解长文本重叠。category_gap30%控制柱条宽度数值越大柱条越窄适合类别较多时使用。4.3 区域热力图与词云Geo和WordCloud的组合商圈分布除了柱状图还可以用Geo热力图展示“哪个片区热度高”。pyecharts的Geo组件需要地图数据旧版本需要额外安装echarts-china-cities-pypkg之类的扩展包新版本的部分地图数据已经内置或需要联网加载。最稳妥的做法是先按城市级别验证数据通路再细化到商圈。from pyecharts.charts import Geo from pyecharts import options as opts from pyecharts.globals import ChartType area_heat [list(x) for x in df[area].value_counts().items()] geo ( Geo() .add_schema(maptype上海, itemstyle_optsopts.ItemStyleOpts(border_color#ccc)) .add(商圈热度, area_heat, type_ChartType.HEATMAP) .set_series_opts(label_optsopts.LabelOpts(is_showFalse)) .set_global_opts( visualmap_optsopts.VisualMapOpts(max_100, is_calculableTrue), title_optsopts.TitleOpts(title上海大众点评商圈热度), ) .render(geo_heat.html) )maptype上海是Geo组件的地图标识pyecharts对直辖市和城市级地图支持相对完善区县级别经常需要第三方geojson遇到显示空白时先缩小范围到城市再排查。VisualMapOpts(max_100)里的max值决定颜色映射上限商圈数量超过100时建议改成实际最大值否则所有区域都映射到最深色图就失去了区分度。词云用来快速感知评价集中在哪些词上。词云前必须先做分词直接跑WordCloud会把“这家”“我们”“真的”这种噪声词放大。用jieba分词后再拼回空格分隔的文本交给WordCloud渲染。from wordcloud import WordCloud import jieba import matplotlib.pyplot as plt comments df[comment_excerpt].dropna().tolist() text .join(jieba.cut( .join(comments), cut_allFalse)) stopwords {这家, 感觉, 真的, 比较, 还是, 一个, 我们, 东西} wc WordCloud( font_path/System/Library/Fonts/PingFang.ttc, width800, height600, background_colorwhite, stopwordsstopwords, max_words100, ).generate(text) plt.figure(figsize(10, 8)) plt.imshow(wc, interpolationbilinear) plt.axis(off) plt.show()font_path必须显式指定中文字体WordCloud默认字体不支持中文不指定就会出来一堆方框。stopwords用集合去重注意集合里不要加“好吃”“不错”这类带评价倾向的词因为后面分析词频时它们是正向信号。max_words100限制词云中的词数量数字过小会丢掉长尾词过大则视觉上过于拥挤。4.4 可视化选型参考分析意图推荐图形对应库关键参数数值分布直方图matplotlibbins区间指定占比结构饼图/环形图matplotlibautopct显示百分比类别排序柱状图pyecharts Barinterval0防标签重叠地理密度热力图pyecharts Geomaptype城市标识评论高频词词云WordCloudfont_path中文字体选型表的核心原则是先想清楚要回答什么问题再决定图形。分布比的是数值占比比的是结构排序比的是高低地理密度比的是空间聚集。这个顺序不要反很多人一上来就做地图结果数据只有十几个商圈地图上稀疏的散点反而比柱状图更难读。5. 大众点评分析进阶相关性、情感得分与店铺分群5.1 价格与评分的关系计算性价比差值价格和评分到底有没有关系“贵的就是好的”在多数情况下不成立但我们需要一个可量化指标。常见做法是把价格和评分分别做0-1归一化然后用“评分排名减价格排名”得出性价比差值。差值为正说明价格低评分高是潜在的好店。from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler() df[price_norm] scaler.fit_transform(df[[price]]) df[rating_norm] scaler.fit_transform(df[[rating]]) # 评分为主、价格为辅的综合性价比 df[value_for_money] df[rating_norm] - df[price_norm] top_value df.nlargest(10, value_for_money)[[name, price, rating, value_for_money]]MinMaxScaler对单列做归一化时fit_transform必须传入二维数组所以用了df[[price]]而不是df[price]。value_for_money的数值范围在-1到1之间正值越大性价比越高。这里有个局限价格和评分的量纲不同直接相减会放大价格的影响。更稳的做法是把价格取对数后再归一化因为餐饮价格的分布本身就是长尾的。性价比区间含义运营建议 0.3便宜且口碑好重点推荐适合做引流款0 ~ 0.3价格与口碑匹配常规区间-0.3 ~ 0价格略高于口碑关注差评原因 -0.3贵且评价差谨慎推荐可能是雷店5.2 用SnowNLP给评论算情感分做舆情聚合评分是用户给出的显性态度评论内容里则藏着隐性情绪。SnowNLP是一个轻量级中文情感分析库sentiments属性输出0到1之间的情感倾向值大于0.5倾向正面。它基于朴素贝叶斯训练对电商类文本效果不错但对“性价比高”这类正面词有时会误判为中性所以更适合做批量聚合而不是单句精判。from snownlp import SnowNLP def sentiment_score(text: str): if not isinstance(text, str) or len(text.strip()) 5: return None # SnowNLP 对过短文本的判别不稳定 return SnowNLP(text).sentiments df[sentiment] df[comment_excerpt].apply(sentiment_score) df[sentiment_group] df[sentiment].apply( lambda s: 正面 if s 0.65 else (负面 if s 0.35 else 中性) ) sentiment_ratio df[sentiment_group].value_counts(normalizeTrue) * 100len(text.strip()) 5的过滤很重要一两句话的片段情感倾向会剧烈波动比如“好吃”两个字SnowNLP可能给0.9也可能给0.4。0.65和0.35两个阈值把中间0.3的区间划为中性避免了正负判断被噪声推来推去。聚合时按area或shop_id分组计算平均情感分可以对标大众点评页面的星级。两者出现背离时往往是评论内容与打分人群不一致的信号比如老客打高分但新客在抱怨。5.3 用层次聚类把店铺分成定位群组单看评分和价格只能得到一维结论把价格、评分、评论数、情感分放在一起做层次聚类能把店铺划分成几个可解释的群组。相比KMeans需要预判簇数层次聚类有树状图辅助可以根据业务理解决定切几刀这在探索阶段更实用。from scipy.cluster.hierarchy import linkage, fcluster from sklearn.preprocessing import StandardScaler feature_cols [price, rating, review_count, sentiment] cluster_df df[feature_cols].fillna(df[feature_cols].median()) std_scaler StandardScaler() X std_scaler.fit_transform(cluster_df) # ward 方法让组内方差最小簇间差异最明显 Z linkage(X, methodward) # t4 表示把树状图切成4个簇 df[cluster] fcluster(Z, t4, criterionmaxclust) profile df.groupby(cluster)[feature_cols].mean().round(2)StandardScaler把不同量纲的特征统一到均值为0、方差为1避免价格(几百)和评分(4.x)在距离计算中失衡。linkage里的ward方法用离差平方和衡量距离对数值型特征效果稳定换成complete会得到更均衡的簇大小。t4不是标准答案建议先画dendrogram树状图观察合并距离的跳变在跳变明显处选t值。聚类结果回填到原表后可以输出每个簇的价格和评分均值给簇起业务名比如“平价高口碑”“高端稳定”“低价低分”“高价高争议”。聚类不是越细越好4个簇已经能覆盖大众点评餐饮店铺的主要经营形态。如果某个簇内店铺数量过少通常是特征列里review_count量级差异太大导致可以尝试去掉该特征或改用np.log1p(review_count)压缩量级再做一次聚类。6. 让大众点评分析脚本长期跑下去的三个落地技巧6.1 增量爬取与断点续爬每天只抓新内容一次性爬完容易长期维护很难。每天重复跑全量采集一是浪费请求数二是容易触发风控。做法是在代码启动时先把已有shop_id读进内存新抓到的店铺如果ID已存在就直接跳过。点评的详情页URL里通常带有数字ID采集列表页时顺手把这个ID存下来即可。import os def load_seen_ids(filepath: str) - set: if not os.path.exists(filepath): return set() df pd.read_csv(filepath, usecols[shop_id]) return set(df[shop_id].astype(str)) seen_ids load_seen_ids(dianping_shops.csv) for shop in new_shop_list: sid str(shop[shop_id]) if sid in seen_ids: continue save_shop_data([shop]) # 追加写入 seen_ids.add(sid)seen_ids是内存集合每追加一条就add一次这样即使单次任务中断已处理的也不会重复写入。这套逻辑同样适用于评论详情用shop_id comment_id做联合主键去重。增量模式跑几天后数据量稳定增长关键词词云和情感分析的结果也会有足够样本支撑。6.2 定时生成报告与数据看板手动执行脚本不是长期方案用cron定时触达是Linux服务器上的常规做法。在crontab -e里写一行任务每天凌晨2点跑一次采集和可视化脚本生成的HTML推到指定目录内部看板直接指向这个目录即可。数据量再大一些可以把pyecharts输出的多个HTML拼成一个入口页形成简易数据大屏效果。0 2 * * * cd /home/user/dp_analysis /usr/bin/python3 run_pipeline.py logs/pipeline.log 21这行cron的含义是每天凌晨2点进入项目目录执行run_pipeline.py标准输出和错误统一追加到日志文件。21把stderr也重定向排查时不用翻两条日志流。拼HTML入口页时最省事的方式是写一个merge_html函数读取所有render出来的文件把body里的内容拼到一个新页面。6.3 绘图坐标过密与反爬触发后的快速处理matplotlib画排名图时如果店铺数量超过20家横坐标标签必然重叠。手动调整刻度是常规解法按总长度计算步长每隔N个显示一个标签再旋转45度。pyecharts方面直接调interval参数即可两类处理方式必须分开记因为它们所在组件的命名不同。import matplotlib.pyplot as plt names df.nlargest(30, review_count)[name].tolist() counts df.nlargest(30, review_count)[review_count].tolist() plt.figure(figsize(14, 6)) plt.bar(range(len(counts)), counts) # 按标签总数计算步长最多显示10个避免全部重叠 step max(1, len(names) // 10) plt.xticks(range(0, len(names), step), [names[i] for i in range(0, len(names), step)], rotation45) plt.tight_layout() plt.savefig(top30_shops.png, dpi150)step max(1, len(names) // 10)把标签数量限制在10个左右range(0, len(names), step)同时控制位置和标签索引不会出现错位。反爬触发后的处理也有一套标准动作查看日志里的状态码403表示IP受限响应文本里含“验证码”表示访问受限此时先停脚本等60到120秒再尝试用新的Cookie重启。如果把日志落在logs/pipeline.log每次触发都能看到是哪一步断了不用重新跑整个流程去复现。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价