
最近不少读者在群里问学Python爬虫到底拿什么项目练手比较合适。我自己试了一圈下来抖音网页版的公开用户主页其实是个不错的练习对象数据量够大、结构相对规整能顺手练到requests、正则、JSON解析这些基本功。不过必须先说清楚这个项目默认只做一件事就是采集用户主页上对外公开的资料比如昵称、抖音号、签名、作品数、粉丝数这些公开字段仅供学习爬虫原理绝不能用于商业用途更不能成套拿去采集真实用户的信息。想清楚这个边界再往下看。这篇不打算讲花哨的逆向也不教你绕过验证码、抓私密数据而是把一个正经的爬虫学习流程拆开选型、抓包、解析、存储、排错最后再聊聊合规和底线。下面直接进入正题。1. 明确边界能爬什么不能碰什么1.1 “个人资料”到底指什么很多人一看到“爬虫 抖音 个人资料”脑子里的第一反应是“能不能拿到手机号、私人账号、聊天记录”。这里必须把概念掰正在公开网页上能看到的用户资料指的是用户主动展示给所有人的信息打开抖音网页版进入任意公开主页不用登录也能看到的一张信息卡昵称、抖音号、头像、个性签名、作品数量、获赞数、关注数、粉丝数这些属于公开资料。采集这些字段属于学习爬虫时的常见练习和你拿浏览器看一遍别人主页在信息获取上没有什么区别。但公开之外的东西就不能碰了。私信内容、手机号、真实姓名、非公开状态下的任何数据都不在这个学习项目的范围内。市面上有些教程教你“爬取指定用户的所有隐私字段”它看起来很有诱惑力但既违反平台规则也可能触碰个人信息保护相关法律。作为一个爬虫学习者先把“能看”和“不能看”的边界搞清楚比会写代码更重要。1.2 “仅供学习”不等于完全免责项目标题写了“仅供学习参考 切勿用于商业”这很好但“仅供学习”不是一个万能挡箭牌。哪怕你的用途确实是学习下面几条底线也依然适用不用于商业用途不做数据倒卖不把抓回来的数据包装成付费服务。不恶意高频抓取不给目标站点造成正常的流量压力。不绕过登录、验证码等访问控制机制。不采集、不存储可识别个人身份的非公开信息。不批量抓取带用户ID、设备标识、联系方式等敏感字段的数据。尊重网站的robots.txt和服务条款。很多人觉得爬虫灰色地带大“我只要没拿去卖钱就没事”。实际操作中真正带来麻烦的往往不是“是否盈利”而是你有没有绕过技术保护措施、有没有影响网站正常运行。学习阶段最稳妥的做法是低频、少量、只碰公开信息。1.3 这次实战要掌握的核心能力既然把边界定清楚了学习目标也就明确了。用抖音公开主页当载体本质上是在练四件事第一理解HTTP请求的基本结构URL、请求头、响应体、状态码这是爬虫的地基。第二会分析动态页面的数据结构抖音网页版返回的HTML里通常内嵌一段JSON里面有用户信息这一步能练到正则提取和JSON解析。第三掌握数据清洗和存储把取到的字段整理成结构化数据写进CSV或SQLite。第四学会频率控制和异常处理爬虫不只是发请求更重要的是在被限制时知道怎么停手。我以前带过一些新手上来就找现成源码跑跑通了也不知道为什么。这个项目的目的恰恰相反每一步都拆开看就算页面改版了你也能跟着新结构重新写一遍这才是真正的学习收获。2. 技术选型requests还是Playwright2.1 为什么建议先学requestsPython实现爬虫有很多方案最基础也最经典的库就是requests。它封装了HTTP协议的大部分细节代码直观、调试方便发一个GET请求拿到响应后面的事情就交给解析逻辑。抖音网页版用户在浏览器里打开主页时会向服务器请求一个HTML文档这个文档里往往就带着用户基本信息requests可以直接获取这个文档不需要模拟浏览器。requests的优点在于轻量、快速、依赖少适合学习HTTP和解析逻辑。缺点也很明显抖音这种体量的平台风控严格直接请求某些接口可能会遇到签名校验、参数加密、接口字段变动。所以requests方案更适合“小规模、公开信息、学习用途”而不是去和平台的安全防护硬碰硬。我第一次用requests请求抖音主页时拿到的是一个403页面当时第一反应是“是不是IP被拉黑了”后来发现只是User-Agent太像个爬虫。换了一个常见的浏览器标识之后请求就正常了。这类小问题在爬虫学习里特别常见正好能练到排查能力。2.2 要不要一上来就上Selenium或Playwright新手看到requests被反爬拦住马上就会想那我用Selenium不就行了Selenium和Playwright是模拟真实浏览器的自动化工具它们会启动一个真正的Chromium内核让页面里的JavaScript自动执行签名、Cookie这些东西浏览器都帮你处理了理论上确实能绕开一部分风控。但代价也很明显慢、吃内存、依赖浏览器驱动而且平台同样能通过自动化特征检测到你的浏览器不是真人操作。更现实的问题是如果你连请求头、状态码、JSON解析这些概念都没搞明白直接上Playwright你会把大量的时间花在环境配置和无头浏览器调试上最后还是一头雾水。我的建议是分两步走第一步用requests把爬虫的底层原理吃透包括怎么分析请求、怎么解析数据、怎么处理异常。第二步再学Playwright用它解决复杂渲染页面和需要真实浏览器交互的场景。后者的学习门槛会低很多因为你已经知道HTTP这块是怎么回事了。2.3 选型小结把常用方案放在一起对比一下方便做决定方案优点缺点适合场景requests轻量、快速、易学、可读性好需要手动处理请求头、Cookie部分接口有签名问题学习HTTP基础、解析JSON、低频抓取公开信息Selenium真实浏览器渲染JS自动执行启动慢、占用资源多、需要浏览器驱动老牌浏览器自动化适合处理简单动态页面Playwright自动化能力强支持多种浏览器等待机制完善学习成本偏高环境配置略复杂需要完整浏览器交互、处理复杂页面这个项目以requests为主线最后再提一句怎么向Playwright方向扩展。3. 实操过程获取公开主页信息3.1 环境准备Python版本建议3.8以上需要安装requests库。如果你还没装Python先到官网下载安装包安装时勾选“Add Python to PATH”然后在命令行里验证python --version pip --version接着安装requestspip install requests如果网络比较慢可以用国内镜像源pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple这一步做完环境就好了。建议再用编辑器创建项目目录比如douyin_spider里面建一个main.py后面代码都写在这个文件里。3.2 找到目标页面打开浏览器进入抖音网页版搜索任意一个公开账号点进它的主页。这时地址栏的URL形如https://www.douyin.com/user/MS4wLjABAAAA...结尾那一长串字符就是用户的sec_uid是这个账号在平台内的公开标识不是手机号也不是微信号。我们可以把它当成爬虫的输入参数。需要注意不同账号的URL格式可能会有细微差异但只要是网页版用户主页基本都带这个规律。为了学习方便建议你先拿自己或身边朋友的公开主页来测试一来数据是你熟悉的二来也不涉及他人隐私。3.3 抓包分析页面上的数据在用户主页按F12打开开发者工具切到Network标签刷新页面。在请求列表里找到类型为document的请求这个就是浏览器请求主页HTML的请求。点击它然后切到Response标签能看到服务器返回的原生HTML源码。抖音网页版的HTML结构经常改版但它有一个习惯会把一部分页面数据以JSON的形式塞在HTML里的某个字段中比如RENDER_DATA或者window._ROUTER_DATA。你可以直接在Response里搜索“signature”或者“nickname”这类关键词大概率能找到用户信息的存放位置。找到之后建议复制一段JSON数据丢到在线格式化工具里看一下层级结构。这一步非常关键因为后面写解析代码时你必须知道字段在JSON里的具体路径。我每次写爬虫至少有一半时间花在“找出数据在哪、长什么样”上面这才是正常状态。3.4 构造请求头直接用requests默认参数去请求抖音主页大概率会被拒绝。最简单的判断依据是同一个URL放在浏览器里能打开用代码访问就返回403或验证码那问题多半出在请求头。最核心的是User-Agent它告诉服务器“我是谁”。一个常见的桌面浏览器UA长这样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, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/avif,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9, Referer: https://www.douyin.com/ }为什么Referer也要写很多平台会校验请求来源如果Referer显示请求来自外部网站服务器可能拒绝返回正常内容。把它设置为抖音自己的首页模拟“从站内跳转进来”能提高成功率。3.5 提取HTML中的内嵌JSON拿到HTML源码后需要把内嵌的数据字段提取出来。这里用正则表达式匹配一个特征标记然后用html模块反转义最后json.loads。代码可以这样写import re import json import html import requests def get_profile(sec_uid): url fhttps://www.douyin.com/user/{sec_uid} 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, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9, Referer: https://www.douyin.com/ } resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 html_text resp.text # 抖音HTML里常见的内嵌数据标识改版后需要自行更新 pattern rRENDER_DATA:(.*?)/script match re.search(pattern, html_text, re.S) if not match: print(未找到内嵌数据可能页面结构已改版) return None # JSON数据经过了HTML转义需要还原 json_str html.unescape(match.group(1)) data json.loads(json_str) return data这里有个容易踩的坑如果直接用json.loads去解析提取出来的字符串经常报错原因是抖音在输出JSON时把双引号、反斜杠都做了HTML实体转义。必须先调用html.unescape把quot;这类东西还原成正常字符再交给json解析。3.6 从JSON中提取公开字段data拿到手之后结构往往是一个多层嵌套的字典。因为页面结构可能调整我不打算写死路径而是教你怎么快速定位字段。在Python交互式环境里运行上面的函数然后手动搜数据data get_profile(你的测试sec_uid) # 把data转成字符串搜关键字 import json text json.dumps(data, ensure_asciiFalse) idx text.find(签名里的关键词) print(text[idx-500:idx500])通过这种“切片观察法”你可以看到目标字段上下文的完整结构然后确定正确的读取路径。比如常见的结构是user_info data[app][user][userInfo] nickname user_info[nickname] signature user_info[signature] follower_count user_info[followerCount]由于页面随时可能改版这里的关键不是记住路径而是学会“先打印再观察”。3.7 完整示例把公开资料存成JSON下面给一个完整的学习示例所有字段均来自公开主页import re import json import html import requests 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, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9, Referer: https://www.douyin.com/ } def get_html(url): resp requests.get(url, headersHEADERS, timeout10) resp.encoding utf-8 return resp.text def extract_data(html_text): pattern rRENDER_DATA:(.*?)/script match re.search(pattern, html_text, re.S) if not match: return None json_str html.unescape(match.group(1)) return json.loads(json_str) def fetch_profile(sec_uid): url fhttps://www.douyin.com/user/{sec_uid} html_text get_html(url) data extract_data(html_text) if data is None: return None # 以下路径以实际页面结构为准这里只展示思路 try: user_info data[app][user][userInfo] profile { nickname: user_info.get(nickname, ), signature: user_info.get(signature, ), follower_count: user_info.get(followerCount, 0), following_count: user_info.get(followingCount, 0), total_favorited: user_info.get(totalFavorited, 0), works_count: user_info.get(worksCount, 0) } return profile except KeyError as e: print(f字段解析失败: {e}) return None if __name__ __main__: test_uid 你的测试sec_uid profile fetch_profile(test_uid) if profile: print(json.dumps(profile, ensure_asciiFalse, indent2))运行后输出大概是{ nickname: 示例账号, signature: 记录Python学习日常, follower_count: 10240, following_count: 328, total_favorited: 15623, works_count: 45 }到这里你就完成了一个最基础的信息爬虫发送请求、获取HTML、提取JSON、整理字段、输出结果。这份代码可能过几天就不能用了因为抖音会调整页面结构但里面每一步的逻辑换到任何其他网站都能复用。3.8 实操心得先打印再写解析我见过很多新手拿到HTML直接凭经验猜字段路径猜不对就怀疑是请求被反爬了。其实一个很实用的习惯是把HTML保存到本地文件然后用编辑器搜索关键字一层一层看嵌套结构。爬虫不是“一把梭”的活而是“看菜下饭”。我在调试这个项目时最喜欢用这样的方式with open(debug.html, w, encodingutf-8) as f: f.write(html_text)把源码存下来之后可以反复打开查看而不必重新请求页面既省流量也避免因频繁请求触发风控。遇到字段结构变化时这个debug文件就是最好的排查依据。4. 反爬与频率控制别让IP变成靶子4.1 抖音常见的反爬手段爬几次就发现抖音不是那么容易直接抓数据的。常见的限制手段有几种第一是请求头校验。缺了UA、Referer等关键字段服务器直接返回403或者验证码页面。第二是签名参数很多数据接口需要携带经过算法生成的签名参数伪造不了。第三是频率限制短时间大量请求会触发账号风控或IP风控。第四是验证码常见的是滑块验证。第五是数据混淆字段名的可读性越来越差甚至动态变化。对于学习项目来说最常遇到的是第一种和第三种。我们需要做的不是去破解签名而是学会用“合规且低频”的方式抓取公开页面。4.2 合规应对策略不强攻只巧取针对上面的限制正确的应对思路是控制频率不要连续快速发起请求。可以在两次请求之间随机暂停几秒。用真实的浏览器UA不要所有请求都沿用同一个默认UA。设置超时时间防止某个请求卡死导致程序挂起。遇到验证码立即停下人工介入处理坚决不要写自动打码脚本。不破解签名算法而是走公开页面的HTML解析路线。不用代理池去轮换IP来规避限制这种做法风险极高尤其是“仅供学习”的项目完全没必要冒这个险。如果请求被限制了最正确的动作是停手等待一段时间自然冷却。我见过有人拼命重试结果从普通频率限制升级成验证码甚至更麻烦的风控本来学习项目硬生生变成了对抗项目完全没有必要。4.3 给代码加一个限速器写代码时把控制频率直接内置进去import time import random sec_uid_list [测试uid1, 测试uid2, 测试uid3] for uid in sec_uid_list: profile fetch_profile(uid) print(profile) time.sleep(random.uniform(3, 8))random.uniform(3, 8)的意思是每次请求结束后随机等3到8秒这样比固定sleep更接近人类操作习惯也能明显降低触发风控的概率。对于学习型爬虫这个速度已经足够。另外建议把每次请求的日志打印出来方便定位问题print(f[{time.strftime(%H:%M:%S)}] 正在抓取 {uid})这行日志成本极低但排查问题时能帮大忙。5. 常见问题与排查技巧实录5.1 常见问题速查表在实际操作中我几乎把下面的坑都踩过一遍。整理成表格方便你对照排查。现象可能原因解决办法返回403或验证码请求头缺失或请求过于频繁完善UA和Referer降低频率HTML里搜不到RENDER_DATA页面结构改版或该字段名已变化重新抓包搜索nickname等关键词找新字段json.loads报错内嵌JSON仍处于HTML转义状态先用html.unescape再解析中文显示乱码响应编码没有正确设置设置resp.encoding utf-8或用content.decode(utf-8)字段值为空字段名写错或数据走的是异步接口用Network里真实接口的返回字段对照连续请求后被封频率过高触发风控停止脚本等待冷却后续降低频率5.2 返回403或验证码怎么办第一步检查请求头UA和Referer是不是都带上了。第二步检查请求频率如果你之前连续跑了十几轮没有休息多半是触发了频率限制。等待一段时间再试通常是几分钟到几小时不等。千万不要去做自动过验证码或者尝试绕过滑块。学习项目完全可以在遇到验证码后停下甚至换一个时间段再采集。5.3 HTML里搜不到数据字段页面改版是非常正常的。遇到这种情况不要慌重新打开开发者工具刷新页面在Network面板里找document请求然后在Response里搜索“nickname”或者其他你认识的字段名。如果HTML里确实没有用户信息那说明页面数据已经改成了异步加载需要去XHR请求里找。在学习阶段不推荐直接追着XHR接口破解签名因为那会不由自主陷入绕过技术保护措施的方向。更稳妥的方式是换用Playwright模拟浏览器让页面自己把数据渲染出来再从DOM中提取公开文本。这也是我前面说“先学requests后续再补Playwright”的原因。5.4 中文乱码这个问题多半出在编码设置上。requests会从响应头猜测编码但有时猜不准导致中文变成乱码。处理方法是在拿到resp后加上resp.encoding utf-8如果还不行可以用html_text resp.content.decode(utf-8)正常UTF-8编码页面都能解决。5.5 频率限制的真实体验我在一次测试中连续抓取20个主页没有加延迟跑到第15个左右开始出现验证码。停了一个多小时才恢复。这给我的教训是哪怕只是公开信息也要把它当成一次“访问”而不是“扫荡”。学习爬虫的目的是利用技术提高效率而不是把平台当作免费数据源无限索取。6. 后续扩展从个人资料到更多公开数据6.1 扩展思路一公开作品列表掌握了个人资料的抓取思路后可以试着把用户主页上的公开作品列表也接下来做解析。抖音网页版的作品列表是懒加载的滚动页面会触发更多请求这正好用来练异步接口分析和分页参数理解。你可以在Network面板里找到对应的XHR请求观察它的请求参数和返回结构再仿照“请求、解析、提取、存储”这四个步骤完成。但这里还是要强调只取作品标题、视频封面、公开点赞数这类数据不下载和传播涉及他人隐私的内容。视频本身是否适合采集也要看平台规则指标。6.2 扩展思路二数据存储到CSV或SQLite抓到的信息如果只想看一眼打印出来就行了。但真正的学习项目应该把数据保存下来。CSV是最简单的存储方式import csv profiles [ {nickname: 示例账号, signature: 记录Python学习, follower_count: 10240}, # 更多数据 ] with open(douyin_profiles.csv, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[nickname, signature, follower_count]) writer.writeheader() writer.writerows(profiles)注意编码用了utf-8-sig这样用Excel打开CSV时中文不会乱码。这个细节在办公场景里非常实用。数据量变大后可以换成SQLiteimport sqlite3 conn sqlite3.connect(douyin.db) cur conn.cursor() cur.execute( CREATE TABLE IF NOT EXISTS profiles ( id INTEGER PRIMARY KEY AUTOINCREMENT, nickname TEXT, signature TEXT, follower_count INTEGER ) ) conn.commit() conn.close()SQLite的好处是轻量、免安装适合个人学习项目做数据存储。6.3 学习建议把自己放进真实的约束里最后聊一点经验之外的东西。爬虫能力说到底是一项工具能力它本身是中性的但怎么用差异很大。我见过有人学爬虫是为了做数据分析、内容监控、工作量自动化也有人把爬虫当成突破别人防护的“武器”。同样是几行代码方向不同结果完全不同。我的建议是尽量在自己的账号、公开的数据上做练习。抓数据的时候顺手统计一下“我抓的是什么、来自哪里、会不会对别人造成影响”。如果一个操作你内心会觉得不太对劲大概率已经越界了。再提醒一次本文涉及的所有技术都只用于学习Python爬虫原理目标页面的选择、字段的提取都必须限定在公开资料范围内绝不能用于商业用途更不能用于采集、存储、传播任何非公开的个人信息。6.4 我个人实操中的一点体会等你真的把requests、JSON解析、频率控制这些跑通之后会发现爬虫的核心难点从来不是代码而是“看透一个网页的数据流”数据从哪里来、以什么格式传输、怎么组织、怎么展示。一旦你习惯了这种分析思路面对任何网站都不会发怵。对我来说最值钱的经验其实就一句话慢慢来比较快。加延迟、写日志、留debug文件这些看起来“不够酷”的操作恰恰是爬虫项目能稳定跑下去的关键。如果你也想拿抖音练手建议从今天文章里的最小例子开始跑通之后再逐步扩展。技术是越用越顺手的但底线要一开始就立好。