首页 / 资讯中心 / 文章详情

Python爬虫实战:携程景点数据采集与可视化分析

Python爬虫实战:携程景点数据采集与可视化分析 ★ FEATURED ARTICLE
前阵子应几个朋友的要求把一个用Python抓取携程网景点数据并做可视化分析的项目重新整理了一遍。这个项目断断续续改了三个版本从最初只抓价格和评分两列数据到后来补充了点评数量、所在城市、所属区域信息再到现在加入了一套完整的数据清洗流程和五个维度的可视化图表。整个项目跑下来的结果比我预想中要好至少能清晰反映出热门旅游城市的价格分布、游客评分倾向和点评热度之间的关系。这个项目很适合三类人参考一是正在找Python练手项目的学生二是想入门爬虫加数据分析的开发者三是做旅游相关产品需要了解竞品或市场行情的从业者。它覆盖了从网络请求、HTML解析、数据清洗到数据库存储、图表展示的完整链路用到的都是Python生态里最常用的库不会引入太重的东西环境搭建也不麻烦。1. 项目整体思路从散乱页面到结构化数据1.1 项目到底要解决什么问题携程网上的景点信息其实隐藏了很丰富的数据资产——门票价格、评分、点评数量、位置、热度排序等等。单独打开一个景点页面你感受不到它的价值但如果你把某个城市或者某个区域内的几十上百个景点全部抓下来横向对比之后就能看出很多有意思的东西。比如一线城市景点和四五线景区之间的价格差高分低价的“性价比之王”点评量高但评分一般的“热门争议景点”等等。当时和几个朋友聊的时候大家最关心的问题是去外地旅游的时候到底哪些景点是“去了不后悔”的这个问题的答案很难靠主观描述说清楚但数据可以。通过抓取大量景点数据然后做筛选和排序就能得到一份相对客观的参考清单。这个项目的定位就是做出这样一套“景点数据采集与分析系统”。从技术层面来讲这个项目真正解决的是三个问题第一如何绕过网站的基础反爬机制稳定拿到数据第二拿到散乱的HTML数据之后如何变成规整的表格第三怎么用图表把数据里的规律直观地表达出来。这三个问题覆盖了爬虫、数据处理和可视化三个Python最常用的方向对一个练手项目来说非常完整。1.2 技术选型为什么是requests加pyecharts项目用到的技术栈非常简单严格来说只需要五个库requests负责发起网络请求BeautifulSoup负责解析HTMLpandas负责数据整理sqlite3负责数据存储pyecharts负责生成可视化图表。整套组合不需要部署任何额外服务也没有复杂的依赖关系装完Python环境之后直接用就好。requests加BeautifulSoup这个组合比较老派现在很多人一提爬虫就默认用scrapy或者playwright。但我觉得做数据分析类的小项目能用轻量方案解决的就没必要上重武器。scrapy的异步机制确实快但这个项目要抓的数据量通常在一万条以内同步请求加合理延时完全足够。而且requests加BeautifulSoup的代码更好理解整个请求、解析流程一目了然排查起问题来也直观很多。playwright这种浏览器自动化方案虽然能搞定更难的反爬但对内存的消耗大部署也麻烦对这个项目来说属于杀鸡用牛刀。可视化这边用pyecharts而不是matplotlib原因是前者的交互能力明显更强。pyecharts生成的图表是HTML格式的鼠标悬停能看到具体数值支持缩放和平移还能很轻松地组合多个图成一个仪表盘。相比之下matplotlib虽然画静态图也很优秀但做数据报告的时候交互感差了不少。如果你更习惯matplotlib的工作方式后面我会给出一个简单的替换方案两个库在做最终展示时的体验差别不算特别大。1.3 数据规模与预期产出根据目标城市数量的不同这个项目最终能产出的数据规模差异很大。假设你想分析20个热门旅游城市每个城市抓前30个景点那么数据总量大约在600条左右。每条数据包含的主要字段有景点名称、所属城市、所属区域、门票价格、评分、点评数量、热度排名。这几个字段看似简单但组合起来就能回答很多问题哪些城市的景区平均评分更高门票价格和评分之间是否存在正相关点评数量最多的景点是不是评分也最高热门城市里哪类景点性价比最好最终项目的产出是一套可视化报表包含柱状图、散点图、箱线图、地图和词云。不需要数据库的复杂操作也不需要搭建任何展示平台生成HTML之后用浏览器打开就能直接查看。这个项目做完之后把图表截图放进报告或者PPT里效果也是非常能打的。2. 数据采集爬虫设计与反爬应对2.1 找到真实的数据接口这是整个项目里最核心的一个环节。很多人写爬虫的时候习惯性地直接去解析HTML页面在div标签堆里翻来翻去找数据这种做法既费劲又容易出错。实际上现代的网站基本都会在页面加载时通过AJAX请求JSON数据然后在前端用JavaScript把JSON渲染成HTML。直接解析HTML的话要面对各种动态属性名和嵌套结构过程相当痛苦。正确的做法是打开浏览器的开发者工具F12切到Network面板刷新页面之后在列表里找XHR类型的请求。携程网的景点列表页会请求一个JSON接口这个接口返回的数据里就包含了景点名称、价格、评分、点评数等几乎所有关键字段。找到这个接口之后直接把接口的URL复制下来用requests请求它就能拿到结构化的JSON数据解析起来比解析HTML省了十倍力气。需要注意的是这种JSON接口通常会有若干参数包括城市ID、页码、排序方式等。参数的具体含义可能需要通过修改页面上的筛选条件来对比确认。比如在城市切换之后观察请求URL或者表单数据的变化就能找到隐含的城市ID字段。这个研究过程稍微有点费时间但一旦搞清楚了后面所有页面的数据都能用同一个接口、不同的参数来获取。2.2 请求头设置与频率控制拿到接口之后最忌讳的事情就是直接循环请求几十个页面每页间隔零秒。这种请求方式在现在的反爬体系下几乎是秒挂IP被临时限制的话轻则弹出验证码重则直接封禁一段时间。我在项目初期就因为这个问题吃过亏连续快速请求了十几个城市的数据结果半小时后所有请求都被服务器拒绝。合理的做法是设置一个完整的请求头至少包含User-Agent和Referer。User-Agent模拟的是浏览器的身份标识我建议用Chrome或者Edge浏览器的UA不要用Python默认的python-requests/x.x.x标识否则服务器一眼就能识别出是爬虫。Referer字段表示请求来源页面一般填景点列表页的URL就行它能让服务器认为这个JSON请求是从正常页面触发的前端请求。频率控制方面第一次运行建议每请求一个页面就time.sleep(2)这个延时不长但对减轻服务器压力很关键。数据量小的话全部抓完也就是十几分钟的事情。如果抓取过程中发现偶发的请求失败可以做一个简单的重试机制比如连续失败三次就停止避免重试过于猛烈导致进一步触发封禁。import time import random import requests def fetch_page(city_id, page_num): params { cityId: city_id, page: page_num, pageSize: 30, sortType: default } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://www.ctrip.com/, } try: resp requests.post(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() return resp.json() except Exception as e: return None上面这段代码是项目里的核心请求函数。实际请求的时候用的是POST还是GET要根据你抓到的接口来决定携程有些接口是POST有些是GET具体看Network面板里的请求方式。参数里的timeout10很重要没有超时设置的话一个卡死的请求可能让整个爬虫在那边干等几分钟。2.3 字段解析与JSON结构梳理JSON接口虽然好用但也有它的麻烦之处——数据嵌套层级深。我抓的接口大致结构是外层一个data对象里面有一个hotelList或者说是productList数组数组的每个元素才是单个景点的完整信息。景点信息里面又有嵌套的子对象比如极小通兑、价格详情、评论信息等。解析这种嵌套结构时使用Python的字典索引链往往显得啰嗦而且容易踩坑。假设价格信息在item[priceInfo][originalPrice]里如果中间某一层不存在就会直接抛出KeyError。在这个项目里我选择写一个专门的解析函数用dict.get()加默认值的方式来逐层获取字段宁可拿到空值也不让整个爬虫因为一个字段缺失而停下来。def parse_item(item): name item.get(name, ) city item.get(cityName, ) district item.get(districtName, ) price_info item.get(priceInfo, {}) or {} if price_info: price price_info.get(originalPrice, 0) else: price 0 comment_info item.get(commentInfo, {}) or {} score comment_info.get(commentScore, 0) comment_count comment_info.get(commentCount, 0) rank item.get(rank, 0) return { 景点名称: name, 城市: city, 区域: district, 价格: price, 评分: score, 点评数量: comment_count, 热度排名: rank }这里有个细节容易被忽略JSON里的价格字段可能是字符串也可能是整数评分可能是带小数的字符串点评数量可能是被逗号分割的格式。在解析的时候最好先做类型统一否则后面用pandas处理的时候会出现各种类型错误。我在第一版项目里就遇到过一次点评数量是字符串“1,234”转成整数时报错的情况所以后来在解析阶段就直接把逗号去掉再转类型。2.4 数据抓取过程记录抓取过程本身没有太多技术含量但有两点经验值得记录。第一最好把每一次请求的返回状态打出来方便观察是否有请求失败的页面。你可以给每个页面打印一行日志比如“cityxxx pagexxx success”这样在爬完之后能快速检查数据是否完整是否有某个页面的数据缺失。第二把请求的起始位置记录下来。项目初期我是在控制台敲一条命令就抓一个城市的后来改成了读取一个城市列表配置文件循环抓取所有城市。这样改成配置驱动的好处很明显以后想增加城市或调整排序方式只需要改配置文件不用动代码。这一点对于想把这个项目当作毕业设计或者作品集展示的人来说尤其有用因为你要让评审和你自己都清楚“数据是怎么来的”一个清晰的抓取记录文件就是最好的说明。3. 数据存储与清洗落地为可查可算的数据资产3.1 用SQLite做轻量持久化数据抓回来之后第一件事是落库。很多人习惯直接把数据放在DataFrame里用to_excel导出Excel就算了。这在小数据量时没问题但如果你的目标是做一个持续更新的追踪项目就应该考虑用数据库。这倒不是说要上一个MySQL或者PostgreSQL这种重量级服务SQLite这种轻量级嵌入式数据库就够用了它不需要安装任何东西Python标准库自带sqlite3模块一个.db文件就能承载全部数据。建表逻辑很简单一张scenic_spot表就够。为了让数据能够重复抓取而不产生大量重复记录我在表设计上加了两个约束UNIQUE约束施加在“城市景点名称”的组合字段上配合INSERT OR REPLACE语法在每次抓取时自动去重更新。这个设计在后面做增量更新的时候帮了大忙不用每次抓完都手动去重一遍。CREATE TABLE IF NOT EXISTS scenic_spot ( id INTEGER PRIMARY KEY AUTOINCREMENT, city TEXT NOT NULL, name TEXT NOT NULL, district TEXT, price REAL, score REAL, comment_count INTEGER, rank_score INTEGER, UNIQUE(city, name) );数据库的好处不只是去重和持久化它还能承担一部分数据筛选的工作。比如分析的时候想只保留评分大于4分且点评数量大于100的景点直接用SQL查询就行返回的结果传到pandas里做进一步分析效率比把几万条数据全加载到内存里再筛选高得多。3.2 数据清洗的常见坑爬虫拿到的原始数据一般都不太干净这个项目的清洗工作量主要集中在三个方面。第一个是价格字段很多景点的价格是“0”或者空值这些代表免门票景点或者门票信息缺失的。分析时要决定是保留还是剔除。我当时选择保留价格为零的数据但单独标记一个“免票”标签因为免票景点的位置和名称本身就有分析价值直接在清洗阶段删掉会损失不少样本。第二个是评分字段的处理。有些景点可能只有几条评论评分却接近满分这会导致散点图里出现一些孤立的极端点。为了让分析更加客观我在做“评分和点评数关系”分析时专门筛掉点评数量少于10条的样本这样得出的相关性结论更可信。这个过滤标准可以自己调整根据你的数据量大小来选一个合适的阈值就行。第三个是城市的规范化。携程返回的城市名称可能有一些不统一的情况比如“市辖区”这种名称出现在区域字段里或者同一座城市在接口里返回的是历史名称。这种问题没有统一的解决方案最好是把抓回来的数据做一个分组统计看看有哪些城市名出现的频率最高然后把明显有误的统一替换掉。清洗阶段虽然琐碎但它决定了后续所有的分析结论是否可靠是整个项目里润物细无声的环节。3.3 从数据到指标的三个分析维度数据入库清洗完之后正式进入分析阶段。这个项目我设计了三组分析维度每组的可视化图表都和一组具体业务问题对应价格分布维度用箱线图看不同城市景区的门票价格分布找出高价和低价城市。箱线图的好处是能把异常值和四分位数展示出来不会因为个别高价景区比如某些主题乐园而误导整体判断。评分与热度维度用散点图看评分和点评数量之间的关系。评分越多说明游客对景区的认可度越高点评数量越多说明这个景区的流量越大。两者结合起来看可以有效区分“叫好不叫座”和“叫座不叫好”的景区。区域与品类维度用地图和词云看不同城市和景区的分布。地图适合展示景点密度的地域差异词云适合观察什么类型的景区最容易出现在热门榜单里。这三个维度其实对应的是三个基本商业分析问题价格、口碑、热度。不管你在哪个行业做数据分析这三维都是评价产品或服务的核心指标。这个项目的价值某种程度上就在于把这三个维度串成了一个整体。4. 可视化与结论呈现让数据自己说话4.1 图表选型与适用场景拿到清洗后的数据接下来就是画图。pyecharts里图表类型非常多但不是每种都适合这个项目。项目本身的数据结构是“城市-景点-指标”的三层结构所以我在选图的时候遵循了一个原则能看清分布规律的就用散点和箱线能比较城市差异的就用柱状和地图能展示文本内容的就用词云。柱状图适合展示单一指标的城市均值对比比如各城市平均门票价格。它的优势是简单直观适合数据报告的开篇展示。箱线图适合展示单一指标在多个城市之间的分布差异看出中位数、上四分位、下四分位和离群点。散点图适合分析两个指标之间的相关关系比如点评数量和评分的关系。用pyecharts的Scatter图鼠标悬浮就能显示该点的景点名称和具体数值。地图适合展示城市级别的数据汇总比如把每个城市的景点数量或者平均评分映射到中国地图上颜色深浅直接代表数值高低。词云适合分析景点名称中出现频率高的词汇比如“山”“湖”“古城”“博物馆”这些词被推荐出来的次数最多。如果只想展示一个静态的总结性结果matplotlib完全能用代码还更简单。但若希望图表具备交互能力比如鼠标悬停看数据、缩放看细节、切换不同城市的数据那pyecharts是更好的选择。这个选择取决于你做出来的报告是要发给别人看静态截图的还是发布成HTML文件供人点击浏览的。前者用matplotlib后者用pyecharts。4.2 核心图表代码实现柱状图与箱线图柱状图在这个项目里用来展示各城市平均门票价格。数据从SQLite里读出来之后先按城市分组求均值再排序让柱状图从左到右依次从高到低排列。这样一张图下来哪个城市景点平均票价贵哪个便宜一眼就看清楚了。import sqlite3 import pandas as pd from pyecharts import options as opts from pyecharts.charts import Bar conn sqlite3.connect(ctrip_data.db) df pd.read_sql_query(SELECT city, price FROM scenic_spot, conn) conn.close() city_avg_price df.groupby(city)[price].mean().sort_values(ascendingTrue) bar ( Bar() .add_xaxis(city_avg_price.index.tolist()) .add_yaxis(平均门票价格(元), [round(v, 2) for v in city_avg_price.values]) .set_global_opts( title_optsopts.TitleOpts(title各城市景点平均门票价格对比), xaxis_optsopts.AxisOpts(axislabel_optsopts.LabelOpts(rotate30)), toolbox_optsopts.ToolboxOpts(), ) ) bar.render(output/avg_price_bar.html)这里有个细节值得说一下X轴的城市名如果太多标签会拥挤在一起。解决办法是把rotate30加在LabelOpts里让标签旋转30度就不会重叠了。这也是不少人用pyecharts做城市对比时的第一个坑。另一个细节是排序做柱状图之前一定要排序否则一长串城市名忽高忽低没有规律读图的人很难形成直观印象。我第一次画这个图的时候就偷懒没排序结果整个图看起来像随机噪声后来加了sorted_values(ascendingTrue)信息表达瞬间清晰了。箱线图的代码也类似只是把Bar换成Boxplot。pyecharts的Boxplot做了两步转化先用Boxplot.prepare_data将原始数据按城市分组后计算箱线图的四分位数和上下边缘然后再把计算结果放入图表。这个方法对理解数据分布特别有用比单纯看平均值的说服力强很多。4.3 散点图与地图发现数据中的关系散点图是这个项目里我自认为最出彩的一张图。X轴是点评数量Y轴是评分每个点代表一个景点。这样分布下来你就能明显看到大部分景点聚集在什么区域——正常情况下点评数量多且评分高的景点是主流玩家点评数量少评分低的景点是尾部。而真正有意思的是那些“偏离主流”的点点评几千条但评分只有3.5以下的是典型的游客体验差但流量极大的“网红坑”点评只有几十条但评分接近满分的则是还没被大规模发现的潜力股。from pyecharts.charts import Scatter def create_scatter(df): x df[comment_count].tolist() y df[score].tolist() data [[x_i, y_i] for x_i, y_i in zip(x, y)] scatter ( Scatter() .add_xaxis(x) .add_yaxis(景点, data, symbol_size8) .set_global_opts( title_optsopts.TitleOpts(title景点点评数量与评分关系), xaxis_optsopts.AxisOpts(name点评数量, type_log), yaxis_optsopts.AxisOpts(name评分, min_0, max_5), ) ) return scatter散点图里有一个容易忽略的调优点X轴最好用对数轴。因为点评数量的分布很不均匀少数热门景点的评论数可能上万而多数景点只有几十条。如果是普通的线性坐标轴几十条到一百条的那些点全都会挤在最左边什么都看不出来。我一开始用的线性轴结果90%的数据点缩成一团暗点根本没法分析。后来把X轴改成type_log整个图的分布立刻舒展开了。地图的生成用的是pyecharts的Map组件把城市名称和指标值配对传入地图会根据数值大小渲染出不同的颜色深浅。由于携程网的接口会返回经纬度之类的字段我已经在字段解析阶段把它们剥离了所以在这里需要用城市名而不是经纬度来匹配。pyecharts内部的地图数据是依据省份和城市名称来匹配的所以城市名的准确度就非常重要。我在项目里遇到过“大理”显示不出来地图色块的问题后来统一改成“大理白族自治州”才正常。这类地名规范化问题处理的时候就是多一个映射表的事但如果你不做图上那一块就是空白很影响成品的观感。4.4 图表配色与报告排版图表参数调节上我觉得最影响观感的有三点。第一是色彩方案。pyecharts的默认配色其实还可以但默认值大家都一样看多了有点审美疲劳。项目里我使用了set_colors([#3E7CB1, #A8BD3A, #F68E56, #AC4813])这样的自定义配色整体色调偏冷图表看起来更专业。第二是Tooltip的触发方式。pyecharts的Tooltip默认是悬浮触发这在散点图上偶尔会遮挡观察区域。我一般设置triggeritem再加一点formatter让悬浮框显示更多字段比如景点名称、城市名。这样读图的人不需要猜某个点代表什么直接把鼠标放上去就能看到完整信息。第三是图表的尺寸和布局。如果你要把多张图合并展示pyecharts提供了Page类可以一次性把所有图表放进同一个HTML页面里上下排列每张图之间自动留出间隔。适合做报告展示。组合图的好处是可以把“城市均价柱状图”“评分箱线图”“点评评分散点图”放在一起让读者在同一屏内看到多个维度的信息。我自己最后交付的HTML就是这种多图表拼接的产物打开之后能滚动浏览所有分析结果整个报告的连贯感很强。5. 常见问题与排查技巧实录5.1 请求超时与IP被限制这个问题出现的频率最高尤其是跑整个城市列表的时候。症状很明显前几个城市都正常到后面的城市开始大量请求超时或者返回的JSON里没有数据只剩一个错误码。遇到这种情况我的经验是先停下来别硬冲。先用浏览器试试正常的页面能不能打开如果能打开说明是请求频率的问题把延时从2秒调到5秒就能大概率恢复。如果延时调到5秒还是不行就要考虑临时切换网络环境。这个项目本身是对个人使用友好的换一个网络环境之后IP变了之前的限流记录就清零了。很多初学者在这种情况下会无限循环重试结果只会让限流的时间越来越长。总之记住一句话爬虫是数据获取的工具不是网络压测工具别为了省几分钟时间把目标服务器搞崩了。5.2 登录与动态Token问题有些接口需要登录后才能访问或者请求参数里带了一个动态变化的Token。携程网的景点列表接口大部分是开放的不需要登录但个别接口会有短期Token或者加密参数。应对思路有两条一是想办法绕过比如换个不用登录的接口二是硬碰硬但这就需要研究JavaScript代码成本会高不少。具体到这个项目我当时选了一条更稳妥的路优先抓那些免登录的公开接口。既然做的是数据可视化分析关键是数据的覆盖面和准确性而不是非要用某一个指定的接口。在网页版和移动端之间切换一下往往就能找到不需要登录的接口。另外如果系统要求你传Cookie你可以从浏览器里复制一份Cookie粘贴到请求头里大多数情况下Cookie的有效期足够支持你把数据抓完。抓完之后如果遇到过期的问题重新进浏览器复制一份就好。5.3 中文乱码与编码处理项目里出现的中文乱码问题有两个来源。一个来源是请求响应体的编码格式不对requests库默认会用HTTP头里的编码解码但有些接口声明的编码和实际返回的编码不一致。解决方法是拿到响应二进制数据后先用resp.encoding resp.apparent_encoding这句话强制校正再调用resp.json()或者resp.text。另一个来源是控制台打印时出现乱码。这个其实不影响程序本身因为pandas处理的数据还是正确的只是Windows终端显示的时候出现问号或者乱码。解决办法很粗暴设置一下操作系统的代码页就行比如在命令行里执行chcp 65001切换成UTF-8编码。这个坑和爬虫本身的关系不大但每次跑脚本的时候看到一堆乱码终究是不舒服的所以还是提前处理好。5.4 内存与性能优化数据量小的时候不用考虑性能问题。但如果你的目标城市很多抓了一两万条数据再用pandas读全表分析内存占用就可能到两三百兆虽然不至于卡死但体验确实不好。我这边的优化方法是第一在SQL查询阶段就筛选需要的列不用的字段直接从SELECT子句里去掉第二分析分组时用pandas的groupby配合聚合函数不要先把全量数据读出来再手写循环统计。图表生成方面pyecharts生成的数据点如果太多比如散点图里画了一万个点浏览器渲染会有点吃力。这个项目里我用了一个实用的做法在画散点图之前先对数据做个筛选只保留评分大于3分或者点评数量大于50的数据。这样既过滤掉了没什么分析价值的冷门景点又让图表不至于太拥挤。这种根据分析目的做数据降维的方法在真实的数据分析工作中非常常用。5.5 常见问题速查表问题表现可能原因解决方案请求返回空数据参数错误或触发限流检查URL参数增加延时更换网络某个字段拿不到值JSON层级路径写错用调试工具打印响应体逐层检查图表中文显示为方框缺少中文字体或编码问题检查环境字体统一编码为UTF-8评分列变成字符串类型原始数据就是字符串解析阶段先转成float再存库地图上某城市无色块城市名和地图映射表不匹配建立城市名到省级行政区划的映射生成的HTML文件过大图表数据点过多分析前过滤数据减少渲染点数量6. 项目扩展与个人的一些体会6.1 可以在此基础上做的扩展这个项目做完之后可以扩展的方向其实非常多。最基本的是增加数据源除了携程网再把去哪儿、同程、艺龙等平台的景点数据抓下来做跨平台比价或者口碑对比。如果技术能力允许还可以把这个爬虫改成定时任务每周自动抓一次数据长期积累就能看到景点热度随季节变化的趋势那才是最有价值的时间序列数据。再进一步可以把项目包装成一个小工具或者服务。用streamlit或者gradio写一个交互界面输入城市名就能自动触发抓取、分析和可视化输出一份完整的HTML报告。这种工具型的作品在求职时是很加分的因为它不仅展现了爬虫和数据处理能力还体现了产品思维。可惜我在做这个项目的时候还没接触这些框架否则肯定给它套一个前端壳子。6.2 项目做得多了才有的几点心得第一个心得是做数据分析项目的核心真的不是会画图、会调库就行而是要先想清楚你想回答什么问题。所有的爬虫字段设计、清洗逻辑、图表选择都应该从问题出发。我在第一版项目里就是因为只想“展示一下爬虫能力”抓了一堆实际没用的字段最后在分析阶段发现缺这个缺那个又回头重新抓数据白白浪费了不少时间。第二次重做的时候先花了一晚上把要分析的问题和字段画成表格然后再动手写爬虫效率高了很多。第二个心得是这个项目里最花时间的技术环节不是爬虫、不是可视化而是数据处理中的各种边角情况。数据缺失、类型不一致、城市名称不统一、价格为零的特例……这些琐碎的问题才是真实的开发日常。在做这个项目之前我总觉得爬虫拿到数据就万事大吉真下手做了才发现爬虫只是开始后面的清洗和分析才是真正考验耐心和细心的地方。第三个心得是数据可视化不是目的只是手段。我在第二次做这个项目的时候折腾了很久配色和样式后来却发现看图表的人最关心的还是信息和结论。比如我花了心思把散点图的点调成圆润的半透明样式但朋友看完之后问的第一句话是“哪些景点是点评率低但评分高的潜力的”。从此之后我再做可视化都会先确认图表能否清楚传达出这个问题的答案再考虑好不好看的问题。整个项目从搭建到完成大概用了两三个周末的时间。现在框架和代码都已经整理得非常顺手你想加个城市只需要在配置文件的列表里加一行然后重新跑一遍数据抓取和分析脚本就可以。如果你照着本文的流程从头到尾自己实现一遍我相信你学到的会比我做这个项目的过程中得到的更多。
阅读完成 · 觉得有帮助?
咨询建站