如何使用BeautifulSoup处理网页抓取中的类名变更问题
解决Google新闻动态类名导致的抓取问题
嘿,这个坑我之前踩过!你遇到的问题核心是JavaScript渲染:浏览器里看到的那些类名(比如mCBkyc JQe2Ld nDgy9d)是页面加载后通过JS动态生成的,但requests库只能拿到服务器返回的原始静态HTML,所以这些动态生成的类名根本不在静态响应里,反而会看到另一套静态类名(比如BNeawe UPmit AP7Wnd)。
下面给你几种靠谱的解决方案:
1. 用Selenium模拟真实浏览器(最直观)
Selenium会打开一个真实的浏览器窗口,等待页面JS执行完毕后再抓取内容,完美解决动态渲染问题。
首先安装依赖:
pip install selenium
还要下载对应浏览器的驱动(比如Chrome的ChromeDriver),确保驱动版本和浏览器版本匹配,然后把驱动路径配置好。
示例代码:
from bs4 import BeautifulSoup 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 # 初始化浏览器驱动(这里以Chrome为例) driver = webdriver.Chrome() try: # 打开目标页面 url = "https://www.google.com/search?q=beautiful+soup+get+text+a&safe=active&rlz=1C1GCEB_enIN960IN960&source=lnms&tbm=nws&sa=X&ved=2ahUKEwjNzsv-iaTzAhX6yzgGHfeBDzgQ_AUoA3oECAEQBQ&biw=1707&bih=770&dpr=1.13" driver.get(url) # 等待新闻标题加载完成(用浏览器里看到的类名定位) WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, "mCBkyc")) ) # 获取渲染后的页面源码 page_source = driver.page_source soup = BeautifulSoup(page_source, "html.parser") # 现在可以用浏览器里看到的类名抓取标题了 for title in soup.find_all('div', class_='mCBkyc JQe2Ld nDgy9d'): print(title.get_text(strip=True)) finally: # 关闭浏览器 driver.quit()
2. 用Playwright(更现代,无需手动装驱动)
Playwright是微软推出的自动化工具,API更简洁,还能自动管理浏览器驱动,体验比Selenium更好。
安装依赖:
pip install playwright playwright install # 自动安装所需浏览器驱动
示例代码:
from bs4 import BeautifulSoup from playwright.sync_api import sync_playwright with sync_playwright() as p: # 启动浏览器(可选headless模式,不显示窗口) browser = p.chromium.launch(headless=True) page = browser.new_page() url = "https://www.google.com/search?q=beautiful+soup+get+text+a&safe=active&rlz=1C1GCEB_enIN960IN960&source=lnms&tbm=nws&sa=X&ved=2ahUKEwjNzsv-iaTzAhX6yzgGHfeBDzgQ_AUoA3oECAEQBQ&biw=1707&bih=770&dpr=1.13" page.goto(url) # 等待标题元素出现 page.wait_for_selector('.mCBkyc') # 获取渲染后的源码 page_source = page.content() soup = BeautifulSoup(page_source, "html.parser") for title in soup.find_all('div', class_='mCBkyc JQe2Ld nDgy9d'): print(title.get_text(strip=True)) browser.close()
3. 避开动态类名,用稳定的结构定位(不推荐,易失效)
如果你不想用自动化工具,可以尝试不依赖这些随机类名,而是通过页面的层级结构或其他稳定属性来定位标题。比如Google新闻的标题通常嵌套在<a>标签里,父元素有特定的结构:
from bs4 import BeautifulSoup import requests content = requests.get( "https://www.google.com/search?q=beautiful+soup+get+text+a&safe=active&rlz=1C1GCEB_enIN960IN960&source=lnms&tbm=nws&sa=X&ved=2ahUKEwjNzsv-iaTzAhX6yzgGHfeBDzgQ_AUoA3oECAEQBQ&biw=1707&bih=770&dpr=1.13", headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"} # 模拟浏览器UA,避免被拦截 ).content soup = BeautifulSoup(content, features="html.parser") # 找所有包含新闻标题的a标签(通过父元素的类名或结构) for a_tag in soup.find_all('a', href=True): # 筛选出新闻链接(href包含"/url?q=") if '/url?q=' in a_tag['href']: # 标题通常在a标签下的某个div里,比如你之前看到的BNeawe类 title_div = a_tag.find('div', class_='BNeawe vvjwJb AP7Wnd') if title_div: print(title_div.get_text(strip=True))
不过这种方法缺点很明显:Google随时可能调整页面结构,导致选择器失效,不如自动化工具稳定。
为什么会出现类名不一致的情况?
Google为了反爬,会动态生成元素类名,这些类名在每次页面加载或不同请求中可能都不一样。而requests请求的是服务器返回的初始HTML,还没经过浏览器的JS渲染,所以只能看到静态的类名,和浏览器里最终渲染的DOM结构完全不同。
内容的提问来源于stack exchange,提问作者user17014278




