ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Python实战:混合策略破解巨量星图数据采集,Selenium与Requests的攻防博弈

Python实战:混合策略破解巨量星图数据采集,Selenium与Requests的攻防博弈 1. 项目缘起为什么盯上了巨量星图做营销投放或者内容分析的朋友对“巨量星图”这个名字应该不陌生。简单来说它是字节跳动旗下连接广告主、MCN机构和达人的官方内容营销服务平台。无论是想找抖音、头条的达人做推广还是想分析某个垂类下头部达人的报价和粉丝画像星图都是绕不开的核心数据源。我最近接手了一个项目需要持续追踪一批美妆和数码类达人的数据波动包括粉丝量、近期作品表现、星图报价变化等。手动去星图网站一个个查效率低不说数据还无法结构化分析。市面上的一些第三方数据平台要么价格昂贵要么数据维度不全。于是一个很自然的想法就冒出来了能不能用Python自己写个工具把星图上的公开数据“拿”下来这个想法听起来简单实操起来却是一步一个坎。星图作为一个商业平台对数据保护相当重视反爬机制层层叠叠。直接requests.get()大概率会吃闭门羹。这就需要我们仔细分析它的数据加载逻辑选择合适的工具和技术栈来应对。接下来我就把这次从零搭建星图数据采集工具的全过程包括技术选型、核心步骤、踩过的坑以及最终的解决方案毫无保留地分享出来。2. 技术选型Selenium vs. Requests Cookie一场攻防战面对一个动态渲染、反爬严格的现代Web应用技术路线的选择直接决定了项目的成败和后期维护成本。我主要评估了两种主流方案。2.1 方案一Selenium 模拟浏览器这是最直观的“重型”方案。Selenium通过程序控制一个真实的浏览器如Chrome去访问网页执行点击、滚动、输入等操作等页面完全加载后再从浏览器内存中提取渲染好的HTML数据。它的核心优势在于“真实”绕过前端加密页面上的所有数据包括通过JavaScript动态加载、加密的最终都会以明文HTML或网络请求的形式呈现给浏览器。Selenium能直接拿到最终结果无需关心中间复杂的JS加密逻辑。处理复杂交互如果需要登录、处理滑块验证码、点击加载更多等Selenium几乎可以模拟人的所有操作。降低初期分析成本在反爬机制不明朗时先用Selenium把数据跑通是快速验证数据可行性的好方法。但劣势同样明显资源消耗巨大每个爬虫实例都要启动一个完整的浏览器进程非常消耗内存和CPU。对于需要大量并发或长时间运行的任务服务器压力会很大。速度慢浏览器需要加载完整的页面资源图片、CSS、JS远比只下载数据接口的requests慢。稳定性挑战浏览器自动化本身就不如纯HTTP请求稳定可能遇到元素加载超时、弹窗干扰等问题需要大量的异常处理和等待逻辑。容易被识别虽然Selenium可以隐藏一些自动化特征但高级反爬系统仍然能通过WebDriver属性、浏览器指纹等特征进行检测。2.2 方案二Requests 逆向分析这是更偏向“黑客”思维的轻量级方案。核心思路是打开浏览器开发者工具F12手动操作星图网站观察浏览器与服务器之间实际发生了哪些网络请求XHR/Fetch。找到直接返回目标数据的那个API接口然后尝试用Python的requests库去模拟这个请求。这个方案的诱惑力极大效率极高直接请求数据接口传输的数据量极小速度极快资源占用几乎可以忽略不计。易于调度和并发requests作为纯HTTP库可以轻松集成到Scrapy等框架中实现高性能分布式爬取。更接近“本质”理解数据是如何通过API流动的本身就是一项有价值的技术洞察。然而拦路虎也非常凶猛接口参数加密星图的API请求其URL参数或请求体Payload很可能不是明文的。你可能会看到一长串毫无规律的sign、_signature、token、as、cp之类的参数。这些是服务器为了防止接口被随意调用而设计的加密签名。请求头校验除了常见的User-Agent、Cookie可能还需要携带特定的Referer、X-Requested-With甚至自定义的头部信息缺失或错误都会导致请求被拒。Cookie管理很多数据需要登录后才能访问这意味着你必须先模拟登录获取有效的Cookie或Token并在后续请求中携带。而登录过程本身可能就伴随着复杂的验证。2.3 我的决策与混合策略经过初步探查我发现星图的大部分列表数据如达人列表、任务列表可以通过分析接口获取但其参数确实存在加密。而详情页的数据部分直接渲染在HTML中部分通过额外接口加载。因此我决定采用一种混合策略这也是很多实战项目中的常见选择Selenium 用于“攻坚”和“辅助”获取初始Cookie用Selenium完成一次手动登录或处理登录验证然后将获取到的有效Cookie导出供requests后续使用。这避免了用代码逆向整个登录流程。应对极端情况当某个关键数据无论如何也找不到对应的纯净API或者加密逻辑过于复杂、逆向成本太高时作为保底方案用Selenium直接抓取渲染后的页面内容。Requests 用于“主战”在通过Selenium或手动方式拿到合法Cookie并初步分析出API格式后主要的数据采集任务由requests库完成。我们只需要专注于维护好Cookie并正确构造那些经过加密的请求参数。这个策略平衡了开发效率、运行效率和成功率。下面我们就进入具体的实战环节。3. 实战第一步用Selenium获取“通行证”Cookie我们的首要目标是拿到一个能访问星图数据接口的有效Cookie。这里假设你已经安装了Python以及selenium库pip install selenium。同时你需要下载与你的Chrome浏览器版本匹配的ChromeDriver并放在系统PATH能找到的位置。3.1 初始化Selenium并访问登录页from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time import json # 初始化Chrome浏览器并添加一些选项以看起来更“像人” options webdriver.ChromeOptions() # 防止网站检测到自动化工具部分特征 options.add_argument(--disable-blink-featuresAutomationControlled) # 可选项无头模式不显示浏览器窗口调试时建议关闭 # options.add_argument(--headless) driver webdriver.Chrome(optionsoptions) driver.get(https://star.toutiao.com) # 星图官网 # 等待页面加载找到登录入口并点击 try: login_button WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, 你的登录按钮选择器)) ) login_button.click() time.sleep(2) # 等待登录弹窗或跳转 except Exception as e: print(f未找到登录按钮或点击失败: {e}) driver.quit()注意这里的‘你的登录按钮选择器’需要你手动在星图页面使用浏览器的“检查”功能去定位。可能是.login-btn也可能是#login或者是一段复杂的XPath。这是Selenium自动化中最基础也最重要的一步。3.2 处理登录过程星图可能支持手机号验证码登录、密码登录或扫码登录。以手机号密码登录为例# 假设登录方式是切换到“账号密码登录”标签 try: switch_to_pwd WebDriverWait(driver, 5).until( EC.element_to_be_clickable((By.XPATH, //div[text()账号密码登录])) ) switch_to_pwd.click() time.sleep(1) except: print(可能默认就是密码登录或切换元素未找到) # 定位手机号和密码输入框 phone_input driver.find_element(By.NAME, mobile) # 根据实际name属性修改 pwd_input driver.find_element(By.NAME, password) # 根据实际name属性修改 # 输入你的账号密码重要切勿将真实密码提交到代码仓库 phone_input.send_keys(你的手机号) time.sleep(0.5) # 模拟人的输入间隔 pwd_input.send_keys(你的密码) time.sleep(1) # 点击登录按钮 submit_btn driver.find_element(By.CSS_SELECTOR, button[typesubmit]) submit_btn.click() # 等待登录成功通常页面会跳转或出现用户头像 try: WebDriverWait(driver, 15).until( EC.presence_of_element_located((By.CSS_SELECTOR, .user-avatar)) # 用户头像选择器 ) print(登录成功) time.sleep(3) # 确保Cookie完全加载 except Exception as e: print(f登录可能失败或等待超时: {e}) # 这里可能需要处理滑块验证码等情况会复杂很多这里是一个巨大的分水岭。如果顺利进入恭喜你。但更可能的情况是你会遇到滑块验证码。处理验证码是一个专门的、复杂的课题可能涉及第三方打码平台、机器学习模型或手动干预。对于本项目如果遇到验证码一个务实的做法是在此处暂停自动化改为手动完成滑块验证然后让程序继续执行后续步骤。你可以去掉--headless选项让浏览器窗口显示出来手动滑一下。3.3 提取并保存Cookie登录成功后浏览器里就存储了服务器颁发的、标识你身份的Cookie。我们需要把它拿出来。# 获取当前所有Cookie cookies driver.get_cookies() print(f共获取到 {len(cookies)} 个Cookie) # 将Cookie保存为JSON文件供requests使用 cookie_dict {} for cookie in cookies: cookie_dict[cookie[name]] cookie[value] with open(xingtu_cookies.json, w, encodingutf-8) as f: json.dump(cookie_dict, f, ensure_asciiFalse, indent2) print(Cookie已保存至 xingtu_cookies.json) # 关闭浏览器 driver.quit()现在你得到了一个xingtu_cookies.json文件里面包含了关键的登录态信息比如sessionid、ssid等。这就是我们通往星图数据API的“通行证”。4. 核心战场逆向分析数据接口有了Cookie我们相当于有了进入大门的钥匙。但进去后具体怎么走调用哪个API以及进门时要不要对暗号参数加密还需要仔细侦查。4.1 使用浏览器开发者工具进行网络抓包保持登录状态在浏览器中访问星图达人列表页例如https://star.toutiao.com/下的某个列表。打开开发者工具F12切换到Network网络选项卡。勾选“Preserve log”保留日志防止页面跳转时请求记录被清除。刷新页面或滚动页面触发加载更多。在纷繁复杂的请求列表中寻找疑似返回数据的请求。重点关注Type为 XHR 或 Fetch的请求。URL中包含关键字如list,search,creator,api,graphql等。Preview预览或Response响应选项卡里能看到结构化的JSON数据其中包含达人昵称、粉丝数、报价等信息。我通过分析发现了一个疑似接口注意以下URL和参数均为示例实际需要你自行分析确认https://star.toutiao.com/api/v1/creator/search/list查看它的Headers请求头和Payload负载对于POST请求或Query String Parameters查询参数对于GET请求。4.2 解析请求的关键参数你可能会看到类似这样的请求参数在“载荷”或“查询字符串参数”里keyword: 美妆 category_id: 3 sort_type: 2 page: 1 size: 20 _signature: xxxxxxxx...很长一串 as: yyyyyyyy cp: zzzzzzzz这里的keyword,category_id,page等参数含义明确我们可以直接构造。但_signature,as,cp这三个参数看起来就是加密签名。这就是核心难点。4.3 尝试定位签名生成逻辑在Network面板中找到这个数据请求右键选择“Copy” - “Copy as cURL”。将复制出来的cURL命令粘贴到一个文本编辑器里。你会看到一个非常长的命令包含了所有请求头、Cookie和参数。尝试简化用Python的requests库只携带最基本的User-Agent、Cookie和你看到的那些明文参数keyword,page等去掉_signature、as、cp发送请求。如果返回错误如signature error那就证实了这些参数是必须的。逆向JS签名参数通常由前端JavaScript代码生成。在开发者工具的Sources源代码或Console控制台中搜索这些参数名如_signature。你可能会找到相关的JS文件。这需要一定的JavaScript逆向工程能力可能需要使用AST解析、Python执行JS环境如PyExecJS,js2py等工具来还原算法。实操心得对于字节系的产品其签名算法常被称为_signature,as,cp,mas等往往经过混淆和更新逆向难度较高且一旦官方更新算法你的爬虫就会立即失效。因此对于快速启动、对实时性要求不是极端高的项目混合策略中的Selenium保底方案显得尤为重要。我们可以先尝试用requestsCookie调用那些不需要签名或签名简单的接口对于核心的加密接口暂时用Selenium获取渲染后的HTML数据。5. 实施混合采集Requests为主Selenium为辅基于以上分析我们设计一个简单的采集流程。5.1 加载Cookie并配置Requests Sessionimport requests import json from typing import Dict, Any class XingTuSpider: def __init__(self, cookie_filexingtu_cookies.json): self.session requests.Session() # 设置一个合理的浏览器User-Agent self.session.headers.update({ 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://star.toutiao.com/, # 来源页很重要 Accept-Language: zh-CN,zh;q0.9,en;q0.8, }) self.load_cookies(cookie_file) def load_cookies(self, cookie_file): 从JSON文件加载Cookie到Session try: with open(cookie_file, r, encodingutf-8) as f: cookies_dict json.load(f) # 将字典形式的Cookie添加到Session中 # 注意requests的cookies需要是CookieJar对象但直接更新字典到session.cookies也可以 for name, value in cookies_dict.items(): self.session.cookies.set(name, value) print(f已从 {cookie_file} 加载Cookie) except FileNotFoundError: print(fCookie文件 {cookie_file} 未找到请先运行Selenium登录脚本。) # 这里可以抛出一个异常或者尝试其他登录方式 raise def get_by_api(self, params: Dict[str, Any]) - Dict[str, Any]: 尝试通过API接口获取数据如果参数加密不复杂 url https://star.toutiao.com/api/v1/creator/search/list # 示例URL try: # 这里params应该包含所有必要的参数包括可能逆向得到的签名参数 # 目前我们先只放明文参数测试是否会失败 test_params { keyword: params.get(keyword, ), page: params.get(page, 1), size: 20, # _signature: ..., // 暂时注释掉 # as: ..., # cp: ..., } resp self.session.get(url, paramstest_params, timeout10) resp.raise_for_status() # 检查HTTP错误 data resp.json() # 检查API返回的业务码 if data.get(code) 0: # 假设0表示成功 return data.get(data, {}) else: print(fAPI返回错误: {data.get(message)}, 代码: {data.get(code)}) # 错误信息可能提示签名无效这证实了需要加密参数 return {} except requests.exceptions.RequestException as e: print(f网络请求失败: {e}) return {} except json.JSONDecodeError as e: print(f响应不是有效的JSON: {e}, 响应文本: {resp.text[:200]}) return {} def get_by_selenium_fallback(self, url: str) - str: 当API无法直接调用时使用Selenium作为后备方案获取页面HTML # 这里需要重新初始化一个Selenium driver并加载之前保存的Cookie # 为了简化我们可以直接让Selenium访问详情页然后解析HTML # 注意此方法效率低仅作为后备 from selenium import webdriver from selenium.webdriver.chrome.options import Options import time print(f正在使用Selenium后备方案访问: {url}) options Options() options.add_argument(--headless) # 无头模式 options.add_argument(--disable-blink-featuresAutomationControlled) driver webdriver.Chrome(optionsoptions) try: # 先访问首页以设置域名 driver.get(https://star.toutiao.com) # 加载之前保存的Cookie到Selenium with open(xingtu_cookies.json, r, encodingutf-8) as f: cookies json.load(f) for name, value in cookies.items(): driver.add_cookie({name: name, value: value}) # 携带Cookie访问目标URL driver.get(url) time.sleep(3) # 等待页面JS加载和数据渲染 page_source driver.page_source return page_source finally: driver.quit()5.2 解析HTML获取数据Selenium后备方案当get_by_api因为签名问题失败时我们调用get_by_selenium_fallback拿到HTML然后用BeautifulSoup或lxml进行解析。from bs4 import BeautifulSoup def parse_creator_list_from_html(html: str): 从达人列表页HTML中解析达人信息示例需根据实际页面结构调整 soup BeautifulSoup(html, html.parser) creators [] # 你需要在这里分析星图列表页的HTML结构找到包含达人信息的容器 # 例如每个达人卡片可能在一个 classcreator-card 的div里 creator_cards soup.find_all(div, class_creator-card) # 这是假设实际选择器不同 for card in creator_cards: try: name_elem card.find(a, class_creator-name) fans_elem card.find(span, class_fans-count) # ... 解析其他字段 if name_elem and fans_elem: creator { name: name_elem.text.strip(), fans: fans_elem.text.strip(), # ... 其他字段 } creators.append(creator) except Exception as e: print(f解析单个达人卡片时出错: {e}) continue return creators5.3 主程序逻辑def main(): spider XingTuSpider(xingtu_cookies.json) # 尝试用API获取第一页数据 api_data spider.get_by_api({keyword: 美妆, page: 1}) if api_data: print(f通过API成功获取数据共 {len(api_data.get(list, []))} 条记录) # 处理api_data... else: print(API方式失败启用Selenium后备方案...) # 构造列表页URL需要你观察实际页面URL规律 list_url https://star.toutiao.com/awesome/creator_list?keyword美妆page1 html spider.get_by_selenium_fallback(list_url) if html: creators parse_creator_list_from_html(html) print(f通过Selenium解析到 {len(creators)} 个达人) # 处理creators... if __name__ __main__: main()6. 关键细节、避坑指南与优化建议在整个过程中我踩了不少坑也总结出一些让爬虫更稳定、更高效的经验。6.1 Cookie的有效性与更新Cookie会过期星图的登录会话Session有有效期。你不能指望一个Cookie用一辈子。解决方案是定期例如每天运行一次Selenium登录脚本更新xingtu_cookies.json文件。可以将这个脚本设置为定时任务如Cron Job。Cookie的作用域确保Selenium保存Cookie和requests使用Cookie时访问的域名star.toutiao.com是一致的。检查Cookie有效性在发起正式数据请求前可以先用一个简单的、不需要签名的请求如个人中心页面测试Cookie是否有效。6.2 请求频率与反爬策略控制请求速度即使有了合法Cookie过于频繁的请求也会触发风控。在requests循环中使用time.sleep(random.uniform(1, 3))来增加随机延迟。模拟真实用户行为requests的Session对象会维持连接但也可以偶尔更换User-Agent从一个池中随机选择并合理设置Referer让它看起来是从站内上一个页面跳转过来的。处理封禁如果IP或账号被暂时封禁代码中应该有重试机制和报警如记录日志、发送邮件通知。可以考虑使用代理IP池但这会大大增加复杂度和成本。6.3 Selenium的稳定性优化显式等待优于隐式等待和time.sleep使用WebDriverWait配合expected_conditions来等待元素出现这比固定的sleep更智能、更高效。健壮的元素定位使用相对稳定、不易变化的CSS选择器或XPath。避免使用绝对路径或依赖动态生成的ID和Class。无头模式下的资源限制在无头模式下运行Selenium时可以禁用图片、CSS加载以加速但要注意这可能影响页面渲染和某些JS的执行。chrome_options webdriver.ChromeOptions() prefs {profile.managed_default_content_settings.images: 2} chrome_options.add_experimental_option(prefs, prefs) chrome_options.add_argument(--headless) chrome_options.add_argument(--disable-gpu)6.4 数据解析与存储结构化数据无论是从API获得的JSON还是从HTML解析出来的数据都应该尽快转换成结构化的格式如Python字典、列表。使用数据库对于持续采集的项目建议将数据存入数据库如SQLite、MySQL、PostgreSQL。这便于查询、去重和增量更新。异常处理与日志在每一个网络请求、数据解析的步骤周围都要用try...except包裹并记录详细的错误日志。这能帮助你在爬虫半夜崩溃时快速定位问题。7. 进阶思考关于JS逆向与签名如果你决定挑战星图的签名算法这里有一些方向搜索入口在开发者工具的Sources面板中全局搜索CtrlShiftF_signature、as、cp等关键词。关注那些经过webpack打包的JS文件通常是一个很大的chunk-vendors.js或app.xxxx.js。Hook关键函数在Console中可以使用JavaScript重写Hook一些关键函数比如window.encodeURIComponent、Object.keys、JSON.stringify或者设置XHR/Fetch的断点来追踪参数是如何被构造和添加的。使用自动化工具像PyExecJS这样的库允许你在Python中执行JavaScript代码片段。你可以尝试将找到的、包含签名逻辑的JS函数代码提取出来在Python环境中调用它来生成签名参数。关注网络请求栈在Network面板中找到那个数据请求查看它的Initiator发起者标签页它能告诉你是哪个JS文件发起了这个请求点击可以跳转到源码位置。我必须坦诚地说对于像字节这样的大厂完全逆向其签名算法是一项耗时且需要深厚JS功底的工作而且对方一旦更新你的努力可能就白费了。因此在商业或时间敏感的项目中需要权衡投入产出比。混合策略主用requestsCookie调用简单接口辅以Selenium攻坚复杂页面在大多数情况下是一个务实且高效的折中方案。整个项目下来我的体会是数据采集从来不是单纯的编程问题而是分析、工程和策略的结合。你需要像侦探一样分析网络请求像工程师一样设计稳健的代码架构还要像策略家一样在“效率”和“成功率”之间做出权衡。希望这篇详细的实践记录能为你解锁巨量星图数据乃至其他类似平台的数据提供一条清晰的路径和实用的工具箱。
返回列表