Selenium+Python 爬虫:动态加载头条问答爬取

在互联网数据采集领域,静态网页爬取早已是基础操作,但随着前端技术的迭代,大量网站采用 JavaScript 动态渲染页面,传统基于 requests + 正则、BeautifulSoup 的静态爬虫已无法满足需求。今日头条旗下的头条问答(现整合入头条搜索 / 头条内容生态)便是典型的动态加载页面 ------ 其问答列表、详情内容、评论数据均通过 AJAX 异步加载,直接请求网页源码无法获取完整数据。

Selenium 作为主流的 Web 自动化测试工具,能够模拟真实浏览器操作,完美解决动态页面渲染难题。本文将以Python+Selenium为技术核心,从零到一实现头条问答的定向爬取,涵盖环境配置、浏览器驱动、动态页面解析、数据存储、反爬规避等全流程,不仅能获取问答标题、作者、回答内容,还能实现分页自动加载与数据持久化存储,为数据分析、内容聚合提供技术支撑。

一、技术背景与核心原理

1.1 动态页面爬取痛点

传统爬虫通过 HTTP 请求获取网页 HTML 源码,而动态页面的核心数据由 JavaScript 在浏览器端渲染生成。以头条问答为例:

  • 页面初始加载仅返回骨架 HTML,无问答正文、列表数据;
  • 滚动页面、点击分页时,前端通过 AJAX 请求后台接口,动态插入数据;
  • 部分接口携带加密参数、Token 校验,直接抓包接口难度较高。

1.2 Selenium 核心优势

Selenium 是一款跨平台、跨浏览器的自动化工具,支持 Chrome、Firefox、Edge 等主流浏览器,直接驱动浏览器渲染页面,完全模拟人类操作(点击、滚动、输入、等待),能获取浏览器渲染后的完整 DOM 结构,彻底解决动态数据加载问题。

结合 Python 的简洁语法,Selenium+Python 成为动态爬虫的最优解之一,无需分析复杂接口加密,降低爬取门槛,适配 90% 以上的动态网站。

1.3 爬取目标明确

本文爬取目标:头条问答指定关键词下的问答列表 + 详情数据,包含字段:

  1. 问答标题
  2. 问题发布者
  3. 问题发布时间
  4. 优质回答内容
  5. 回答者昵称
  6. 回答点赞数、评论数
  7. 问答链接

二、环境配置:Python+Selenium 基础搭建

2.1 开发环境准备

  • Python 版本:3.8 及以上(推荐 3.9/3.10)
  • 操作系统:Windows/Mac/Linux 均可
  • 核心依赖库:
    • <font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">selenium</font>:Web 自动化核心库
    • <font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">lxml</font>:XPath 解析页面元素
    • <font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">pandas</font>:数据格式化存储
    • <font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">time/random</font>:反爬延时控制

2.2 浏览器驱动配置(关键步骤)

Selenium 需要浏览器驱动来控制浏览器,最常用 Chrome 浏览器,需下载对应版本的 ChromeDriver:

  1. 查看 Chrome 版本:打开 Chrome → 右上角三个点 → 设置 → 关于 Chrome,记录版本号(如 124.0.6367.60);
  2. 下载 ChromeDriver:访问ChromeDriver 官方镜像,下载对应版本驱动;
  3. 驱动配置:
    • Windows:将<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">chromedriver.exe</font>放到 Python 安装根目录(与 python.exe 同级);
    • Mac/Linux:将驱动放到<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">/usr/local/bin/</font>目录,或代码中指定路径。

进阶:Selenium 4.6 + 版本支持自动驱动管理,无需手动下载,代码中会自动匹配驱动,新手推荐使用该特性。

三、核心思路:头条问答爬取流程设计

本次爬取采用模拟浏览器操作 + 动态等待 + XPath 解析的方案,完整流程:

  1. 初始化 Selenium 浏览器对象,配置反爬参数;
  2. 访问头条问答搜索页面,输入目标关键词;
  3. 模拟页面滚动,加载动态问答列表;
  4. 遍历问答列表,获取详情链接;
  5. 进入详情页,等待动态内容加载完成;
  6. 解析页面 DOM,提取目标数据;
  7. 自动分页爬取,循环获取多页数据;
  8. 数据清洗、去重,保存为 Excel/CSV 文件。

四、代码实现:从零编写动态爬虫

4.1 配置浏览器启动参数(反爬核心)

头条问答具备基础反爬机制,直接使用裸浏览器会被识别为爬虫,需配置无头模式、禁用自动化提示、模拟 UA,代理IP等参数:

python

运行

plain 复制代码
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
import time

def init_browser():
    # 代理信息(你提供的)
    proxyHost = "www.16yun.cn"
    proxyPort = "5445"
    proxyUser = "16QMSOML"
    proxyPass = "280651"

    # 创建Chrome配置对象
    chrome_options = Options()
    
    # 基础配置:禁用自动化控制提示(关键反爬)
    chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
    chrome_options.add_experimental_option('useAutomationExtension', False)
    
    # 模拟真实浏览器UA
    chrome_options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36')
    
    # 禁用图片加载(提升爬取速度,可选)
    # chrome_options.add_argument('blink-settings=imagesEnabled=false')
    
    # 无头模式(后台运行,不显示浏览器,正式爬取开启)
    # chrome_options.add_argument('--headless=new')

    # ===================== 代理配置开始 =====================
    # 方案:Chrome插件注入账号密码代理(最稳定)
    manifest_json = """
    {
        "version": "1.0.0",
        "manifest_version": 2,
        "name": "Chrome Proxy",
        "permissions": [
            "proxy",
            "tabs",
            "unlimitedStorage",
            "storage",
            "<all_urls>",
            "webRequest",
            "webRequestBlocking"
        ],
        "background": {
            "scripts": ["background.js"]
        },
        "minimum_chrome_version":"22.0.0"
    }
    """

    background_js = """
    var config = {
            mode: "fixed_servers",
            rules: {
              singleProxy: {
                scheme: "http",
                host: "%s",
                port: %s
              },
              bypassList: ["localhost"]
            }
          };

    chrome.proxy.settings.set({value: config, scope: "regular"}, function() {});

    function callbackFn(details) {
        return {
            authCredentials: {
                username: "%s",
                password: "%s"
            }
        };
    }

    chrome.webRequest.onAuthRequired.addListener(
                callbackFn,
                {urls: ["<all_urls>"]},
                ['blocking']
    );
    """ % (proxyHost, proxyPort, proxyUser, proxyPass)

    # 载入代理插件
    import zipfile
    import os
    plugin_file = "proxy_auth_plugin.zip"

    with zipfile.ZipFile(plugin_file, 'w') as zp:
        zp.writestr("manifest.json", manifest_json)
        zp.writestr("background.js", background_js)
    chrome_options.add_extension(plugin_file)
    # ===================== 代理配置结束 =====================

    # 初始化浏览器对象
    driver = webdriver.Chrome(options=chrome_options)
    
    # 修改浏览器特征,绕过Selenium检测
    driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
        'source': '''
            Object.defineProperty(navigator, 'webdriver', {
                get: () => undefined
            })
        '''
    })
    
    # 设置浏览器窗口大小
    driver.maximize_window()
    # 设置全局等待时间
    driver.implicitly_wait(10)

    # 启动后删除临时插件文件
    try:
        os.remove(plugin_file)
    except:
        pass

    return driver

核心作用 :通过修改浏览器<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">webdriver</font>属性,隐藏自动化特征,绕过网站的爬虫检测。

4.3 搜索关键词 + 加载动态列表

头条问答的搜索入口为<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">https://m.toutiao.com/search</font>(移动端页面,结构更简洁,适合爬取),代码实现关键词搜索与动态滚动加载:

python

运行

plain 复制代码
def search_question(driver, keyword):
    # 访问头条搜索页面
    driver.get("https://m.toutiao.com/search")
    # 随机延时,模拟人类操作
    time.sleep(random.uniform(1, 2))
    
    try:
        # 等待搜索框加载完成
        search_box = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.XPATH, '//input[@placeholder="搜索内容"]'))
        )
        # 输入关键词
        search_box.clear()
        search_box.send_keys(keyword)
        time.sleep(random.uniform(0.5, 1))
        
        # 点击搜索按钮
        search_btn = driver.find_element(By.XPATH, '//button[contains(text(),"搜索")]')
        search_btn.click()
        time.sleep(random.uniform(2, 3))
        
        # 切换到【问答】标签(核心:筛选问答内容)
        question_tab = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//div[text()="问答"]'))
        )
        question_tab.click()
        time.sleep(random.uniform(2, 3))
        print(f"✅ 成功搜索关键词:{keyword},并切换到问答标签")
        
        # 模拟页面滚动,加载动态数据(滚动3次,加载更多问答)
        for i in range(3):
            driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            time.sleep(random.uniform(1.5, 2.5))
        print("✅ 动态问答列表加载完成")
        
    except Exception as e:
        print(f"❌ 搜索失败:{str(e)}")

4.4 解析问答列表,获取详情链接

滚动加载完成后,页面渲染完整问答列表,通过 XPath 提取每条问答的标题、链接等基础信息:

python

运行

plain 复制代码
def get_question_list(driver):
    question_links = []
    try:
        # 获取所有问答项(XPath路径:通过浏览器F12开发者工具获取)
        question_items = driver.find_elements(By.XPATH, '//div[contains(@class,"search-result-item")]//a[contains(@href,"question")]')
        
        for item in question_items:
            # 提取标题和链接
            title = item.text
            link = item.get_attribute("href")
            if title and link and link not in [q["link"] for q in question_links]:
                question_links.append({
                    "title": title,
                    "link": link
                })
        print(f"✅ 共获取到{len(question_links)}条问答链接")
        return question_links
    
    except Exception as e:
        print(f"❌ 获取问答列表失败:{str(e)}")
        return []

技巧:XPath 路径可通过浏览器「检查元素」-「复制 XPath」快速获取,适配页面结构。

4.5 爬取问答详情(核心数据提取)

进入详情页后,等待动态内容加载,提取问题、回答、作者等核心数据:

python

运行

plain 复制代码
def parse_question_detail(driver, link):
    data = {}
    try:
        # 访问详情页
        driver.get(link)
        time.sleep(random.uniform(2, 3))
        
        # 1. 提取问题标题
        data["question_title"] = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.XPATH, '//h1[@class="question-title"]'))
        ).text
        
        # 2. 提取问题发布者
        try:
            data["question_author"] = driver.find_element(By.XPATH, '//div[@class="author-name"]').text
        except:
            data["question_author"] = "匿名用户"
        
        # 3. 提取问题发布时间
        try:
            data["publish_time"] = driver.find_element(By.XPATH, '//span[@class="publish-time"]').text
        except:
            data["publish_time"] = "未知时间"
        
        # 4. 提取优质回答内容(头条问答默认展示优质回答)
        answer_contents = driver.find_elements(By.XPATH, '//div[@class="answer-content"]//p')
        data["answer_content"] = "\n".join([p.text for p in answer_contents if p.text.strip()])
        
        # 5. 提取回答者信息
        try:
            data["answer_author"] = driver.find_element(By.XPATH, '//div[@class="answer-author"]').text
        except:
            data["answer_author"] = "匿名用户"
        
        # 6. 提取点赞数、评论数
        try:
            data["like_count"] = driver.find_element(By.XPATH, '//span[@class="like-count"]').text
            data["comment_count"] = driver.find_element(By.XPATH, '//span[@class="comment-count"]').text
        except:
            data["like_count"] = "0"
            data["comment_count"] = "0"
        
        # 7. 存储问答链接
        data["question_link"] = link
        
        print(f"✅ 成功爬取:{data['question_title']}")
        return data
    
    except Exception as e:
        print(f"❌ 爬取详情失败:{link},错误:{str(e)}")
        return None

4.6 数据存储与主函数调用

将爬取的数据保存为 Excel 文件,方便后续分析,同时编写主函数整合所有流程:

python

运行

plain 复制代码
def save_data(data_list, keyword):
    # 转换为DataFrame格式
    df = pd.DataFrame(data_list)
    # 去重
    df = df.drop_duplicates(subset=["question_title"])
    # 保存为Excel文件
    filename = f"头条问答_{keyword}_数据.xlsx"
    df.to_excel(filename, index=False, encoding="utf-8")
    print(f"\n🎉 数据保存成功!共{len(df)}条数据,文件名为:{filename}")

def main():
    # 1. 初始化浏览器
    driver = init_browser()
    # 2. 设置爬取关键词(可自定义修改)
    keyword = "Python学习方法"
    # 3. 搜索关键词并加载问答列表
    search_question(driver, keyword)
    # 4. 获取问答链接
    question_links = get_question_list(driver)
    if not question_links:
        driver.quit()
        return
    
    # 5. 循环爬取详情数据
    all_data = []
    for index, q in enumerate(question_links, 1):
        print(f"\n正在爬取第{index}/{len(question_links)}条:{q['title']}")
        detail_data = parse_question_detail(driver, q["link"])
        if detail_data:
            all_data.append(detail_data)
        # 每爬取1条随机延时,避免请求过快
        time.sleep(random.uniform(1, 2))
    
    # 6. 保存数据
    save_data(all_data, keyword)
    # 7. 关闭浏览器
    driver.quit()
    print("\n🚀 头条问答爬取任务全部完成!")

if __name__ == "__main__":
    main()

五、反爬规避:降低被封禁风险

头条问答对爬虫有基础限制,若爬取过快会出现验证码、IP 封禁,以下是实用反爬策略:

  1. 随机延时 :在点击、滚动、页面切换时添加<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">random.uniform(1,3)</font>延时,模拟人类操作频率;
  2. 禁用无头模式调试:调试时显示浏览器窗口,确认操作正常,正式爬取再开启无头模式;
  3. 控制爬取数量:单次爬取不超过 50 条,分时段爬取,避免高频请求;
  4. IP 代理池(进阶):大规模爬取时,配置代理 IP,避免单 IP 封禁;
  5. 不爬取敏感数据:遵守《网络安全法》,仅爬取公开数据,不获取用户隐私信息。

六、问题排查与优化方案

6.1 常见问题

  1. 元素找不到 :页面未加载完成,增加<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">WebDriverWait</font>显式等待,替代固定<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">time.sleep</font>
  2. 被检测为爬虫 :检查浏览器配置,确保禁用了<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">webdriver</font>特征;
  3. 动态内容未加载:增加页面滚动次数,或等待目标元素出现再解析。

6.2 性能优化

  1. 禁用图片 / JS 加载:减少浏览器资源消耗,提升爬取速度;
  2. 批量爬取:将多个关键词存入列表,循环爬取;
  3. 异常重试:为失败的链接添加重试机制,提升数据完整性。

七、技术总结与合规说明

7.1 技术核心总结

本文通过Selenium+Python完美解决了头条问答动态页面的爬取难题,核心价值:

  1. 无需分析复杂 AJAX 接口与加密参数,降低动态爬虫开发门槛;
  2. 模拟真实浏览器操作,适配 90% 以上动态渲染网站;
  3. 完整实现数据采集、清洗、存储全流程,可直接复用至其他动态网站。

Selenium 不仅适用于爬虫,还可用于自动化测试、表单自动填写、定时任务等场景,是 Python 开发者必备技能。

相关推荐
Hui Baby2 小时前
springboot读取配置文件
后端·python·flask
阿Y加油吧2 小时前
回溯法经典难题:N 皇后问题 深度解析 + 二分查找入门(搜索插入位置)
开发语言·python
leo_messi942 小时前
2026版商城项目(三)-- ES+认证服务
后端·python·django
NPE~2 小时前
[App逆向]环境搭建下篇 — — 逆向源码+hook实战
android·javascript·python·教程·逆向·hook·逆向分析
财经资讯数据_灵砚智能2 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(夜间-次晨)2026年4月7日
人工智能·python·信息可视化·自然语言处理·ai编程
deephub2 小时前
向量数据库对比:Pinecone、Chroma、Weaviate 的架构与适用场景
人工智能·python·大语言模型·embedding·向量检索
星马梦缘2 小时前
强化学习实战5——BaseLine3使用自定义环境训练【输入状态向量】
pytorch·python·jupyter·强化学习·baseline3·gymnasium
阿捞23 小时前
JVM排查工具单
java·jvm·python
weixin_423533993 小时前
【ubuntu20.04安装nvidia显卡驱动及pytorch】
python