使用 Selenium 爬取动态网页数据 —— 实战与坑点详解

本文记录了笔者在爬取网页数据过程中遇到的各种技术挑战,包括页面动态渲染、JavaScript 注入等问题,并最终给出一个可运行的完整方案。

文章目录

网页获取不到数据

某网页数据依赖大量 JavaScript 动态渲染。笔者最初尝试通过直接请求页面源代码的方法(如 requests)来获取页面中的源代码内容,看看HTML的网页结构,结果发现页面核心数据并未写死在 HTML 中,而是通过 JS 模块懒加载,并渲染到 DOM 后才可见。

如下为典型的 script 标签:

html 复制代码
<script nonce="..." 
	type="module" 
	src="//c-cdn.qixin.com/web/_web/UbfmNsyd.js" 
	crossorigin>
</script>

直接请求 HTML 无法获得数据的,使用 BeautifulSoup 解析 <script> 可能也没用,因为内容可以是异步加载、并由 JS 渲染进 DOM 。

下述是一个下载网页源代码保存到本地 html 的 python代码:

python 复制代码
import requests

# 目标网页 URL
url = '替换成你要爬的网页地址'

# 自定义请求头,防止被识别为爬虫
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
                  '(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
}

# 发送 GET 请求
response = requests.get(url, headers=headers)

# 判断请求是否成功
if response.status_code == 200:
    # 保存为本地 HTML 文件
    with open('page.html', 'w', encoding='utf-8') as f:
        f.write(response.text)
    print("网页源代码已保存为 page.html")
else:
    print(f"请求失败,状态码: {response.status_code}")

保存到本地的HTML 文件:

所需要的数据都在 <script> 里面。

request库不能运行js代码,我们需要使用 selenium 借助浏览器运行js代码,完成dom的渲染。

🚀 尝试用 Selenium 渲染页面

为了解决数据渲染问题,我们使用 Selenium 启动一个真实的 Chrome 浏览器,让 JavaScript 有机会执行,待页面加载完毕后再提取数据

装包:

shell 复制代码
pip install selenium webdriver-manager
python 复制代码
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
python 复制代码
url = "https://www.qixin.com/company/7b752b1d-8d49-4a67-9472-04cef0b301ec"


options = webdriver.ChromeOptions()

## Chrome,无头模式(不弹窗)
# options.add_argument('--headless')  # 去掉这行可以看到浏览器
# options.add_argument('--disable-gpu')

driver = webdriver.Chrome(
    service=Service(ChromeDriverManager().install()), options=options
)


driver.get(url)
input(">扫码登陆,确认开始?")

# urls: 网址列表
for url in urls:
    lock = False

    while 1:
        try:
            driver.get(url)
            # 等待目标数据出现(最多等 30 秒)
            WebDriverWait(driver, 30).until(
                # EC.presence_of_element_located((By.CLASS_NAME, "table"))
                EC.presence_of_element_located(
                    (
                        By.CSS_SELECTOR,
                        "#__nuxt > div > ... > div > div.credit-number",
                    )
                )
            )
            lock = False
            
        except Exception as e:
            lock = True
            user_input = input("在网页上验证码,输入 skip 跳过,否则继续运行").strip()

            if user_input == "skip":
                lock = False

        if lock == False:
            break
    
    text = driver.find_element(By.XPATH, '//*[@id="__nuxt"]/div/.../div[2]').text.strip()
    print(text)

值得注意的是 javascritps 如果需要加载外部的js文件,存在网络传输的时间,如果在 driver.get(url) 后立即调用 driver.find_element 则找不到对应的值,因为js还没有执行完毕。上述代码使用 WebDriverWait 等待 js 运行完成。

💡 小贴士(Tip):

把 WebDriverWait 换成 sleep 一个较长的时间也是可以的,因为本质上都是等待 js 运行完成,但更推荐 使用WebDriverWait 。

python 复制代码
WebDriverWait(driver, 30).until(
    # EC.presence_of_element_located((By.CLASS_NAME, "table"))
    EC.presence_of_element_located(
        (
            By.CSS_SELECTOR,
            "#__nuxt > div > ... > div > div.credit-number",
        )
    )
)

#__nuxt > div > ... > div > div.credit-number 是 网页上目标数据的 css。

上述代码 一直等待目标数据在网页上渲染完成,最多等待30秒。

等到目标数据渲染成功后,使用 driver.find_element 就可以得到正确的目标数据。

为了尽可能多地保留每一条数据,使用while循环重复请求每个url,直到获取到正确的目标数据才退出,如果遇到报错,会被 try except 捕获到异常,当代码正确执行,lock为False会跳出 while,爬取下一个 url。

每一次报错,都会被 input 中断,可能是需要用户在网页上输入验证码,用户在输入完验证码后,输入任意字符会继续爬取。若不是需要验证码,而是该网页报错(比如:404),那么用户可以输入 skip 实现跳过当前页面的爬取。

相关推荐
MilesShi5 小时前
Playwright MCP 入门实战:自动化测试与 Copilot 集成指南
测试工具
SRC_BLUE_176 小时前
攻防世界 - Misc - Level 6 | Wireshark
网络·测试工具·wireshark
碳酸的唐9 小时前
Selenium:模拟真实用户的爬虫
爬虫·selenium·测试工具
大道归简11 小时前
自动化实现web端Google SignUp——selenium
前端·selenium·自动化
燃星cro13 小时前
关于MindVault项目测试报告
java·开发语言·测试工具·spring
CJH(本人账号)1 天前
Wireshark网络抓包工具基础使用教程
网络·测试工具·wireshark
春风又。1 天前
接口自动化——参数化
python·测试工具·自动化·pytest
Ac157ol1 天前
Wireshark抓取SMTP协议报文
网络·测试工具·wireshark
IT小白农民工2 天前
如何用postman进行批量操作
测试工具·postman·sap