网页爬虫如何选取多个元素?纽约时报爬取元素匹配异常排查
问题根因
你拿不到目标元素是三个核心问题导致的:
- 纽约时报搜索结果页是JavaScript动态渲染的,直接用
requests请求拿到的初始HTML里根本没有搜索结果相关的DOM节点,所有新闻内容都是页面加载完成后,浏览器异步调用接口拉取数据再渲染到页面上的。 - 你用来定位的
css-16nhkrn这类类名是前端打包时自动生成的哈希值,每次网站发版都可能变化,本身就不适合作为爬虫的稳定定位依据。而且你写的定位规则soup.find_all("h4", "p.css-16nhkrn")语法逻辑错误,这个写法是查找标签名为h4、class属性等于p.css-16nhkrn的元素,和你要找的p标签完全不匹配。 - 代码本身存在低级变量错误:你把
find_all的返回值赋值给了单数变量title,循环的时候却用了复数变量titles,就算页面结构对了也会直接报变量不存在的错误;最后生成词云时text = title只取了循环最后一个元素的值,根本拿不到全量标题。另外代码里导入了大量没用到的库,属于冗余代码。
可运行实现方案
不要硬解析动态渲染的前端DOM,直接调用页面拉取搜索结果用的后台接口,拿结构化JSON数据效率更高、稳定性更强:
import requests import time import pandas as pd from wordcloud import WordCloud, STOPWORDS import matplotlib.pyplot as plt # 纽约时报搜索结果公开接口 base_url = 'https://www.nytimes.com/svc/search/v2/articlesearch.json' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.0.0', 'Accept': 'application/json' } all_titles = [] # 示例爬取前3页结果,可按需调整页数 for page in range(3): params = { 'q': 'ukraine war', 'page-size': 20, 'page': page } resp = requests.get(base_url, headers=headers, params=params) resp.raise_for_status() article_data = resp.json() # 从返回的结构化数据里提取标题 for article in article_data['response']['docs']: current_title = article['headline']['main'].strip() all_titles.append(current_title) print(current_title) # 加延时避免请求太频繁被拦截 time.sleep(2) # 拼接全量标题生成词云 text = ' '.join(all_titles) # 可按需补充停用词,过滤无意义的高频词汇 stopwords = set(STOPWORDS) stopwords.update(['Ukraine', 'War', 'Russia', 'said', 'will']) wordcloud = WordCloud( stopwords=stopwords, background_color='white', width=1200, height=700 ).generate(text) plt.figure(figsize=(12, 7)) plt.imshow(wordcloud, interpolation="bilinear") plt.axis("off") plt.show() # 需要存储数据的话直接导出即可 # pd.DataFrame({'title': all_titles}).to_csv('nyt_ukraine_war_titles.csv', index=False)
补充说明
- 如果你一定要解析渲染后的页面DOM,不要直接用
requests请求,换用Playwright、Selenium这类可以驱动真实浏览器加载页面的工具,等JS渲染完成后再提取元素。 - 爬取时控制请求频率,不要短时间发起大量请求,否则会被网站反爬机制拦截。
- 所有带
css-前缀的类名基本都是前端工程化打包生成的动态哈希值,不要作为长期有效的定位规则,优先找固定的id、属性,或者直接抓后台接口是更优方案。
内容的提问来源于stack exchange,提问作者Ricky Kuo
相关产品推荐
相关产品推荐

