请求协助:Nordnet网站个股页面行业信息爬取异常问题
解决Nordnet个股页面动态加载的行业信息爬取问题
你已经精准定位了核心问题——部分个股的「Om bolaget」数据并非嵌入在初始HTML的__initialState__中,而是通过动态XHR POST请求异步加载的。下面是具体的解决思路和代码改进方案:
第一步:分析动态请求的结构
打开浏览器开发者工具(F12)切换到Network标签,点击EQT页面的「Om bolaget」标签,就能看到关键的POST请求细节:
- 请求URL:
https://www.nordnet.se/api/2/instruments/{instrument_id}/company_info(其中instrument_id是个股URL里的数字串,比如EQT的是17117956) - 必要请求头:需要携带
X-CSRF-Token(从初始页面HTML提取)、Referer(当前个股页面URL)、Content-Type: application/json等 - 请求体:通常是一个空的JSON对象
{}
第二步:改进爬取逻辑
我们可以做「双重提取」策略:先尝试从初始HTML的__initialState__拿数据,失败则发起POST请求获取动态数据。
改进后的完整代码
import requests from bs4 import BeautifulSoup import re import json import time def get_csrf_token(html_content): # 从初始页面的meta标签提取CSRF Token soup = BeautifulSoup(html_content, 'html.parser') meta_tag = soup.find('meta', attrs={'name': 'csrf-token'}) return meta_tag['content'] if meta_tag else None def get_sector(url): sector, sector_group = None, None # 使用Session保持会话,自动处理Cookie,提升请求稳定性 session = requests.Session() session.headers.update({ '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' }) # 第一阶段:尝试从初始页面的__initialState__提取数据 resp = session.get(url) soup = BeautifulSoup(resp.text, 'html.parser') for tag in soup.findAll('script'): content = tag.get_text().replace('\\', '') if '__initialState__' not in content: continue try: # 优化正则匹配,直接提取字段值 sector_match = re.findall(r'"sector":"([^"]+)"', content) if sector_match: sector = sector_match[0] sector_group_match = re.findall(r'"sector_group":"([^"]+)"', content) if sector_group_match: sector_group = sector_group_match[0] # 拿到数据直接返回,无需走POST流程 if sector and sector_group: return sector, sector_group except Exception as e: print(f"解析initialState出错[{url}]: {str(e)}") break # 第二阶段:initialState未拿到数据,发起POST请求补充获取 # 从URL中提取instrument_id id_match = re.search(r'/(\d+)-', url) if not id_match: print(f"无法提取instrument_id[{url}]") return sector, sector_group instrument_id = id_match.group(1) csrf_token = get_csrf_token(resp.text) if not csrf_token: print(f"无法获取CSRF Token[{url}]") return sector, sector_group # 构造API请求参数 api_url = f"https://www.nordnet.se/api/2/instruments/{instrument_id}/company_info" headers = { 'X-CSRF-Token': csrf_token, 'Referer': url, 'Content-Type': 'application/json', 'Accept': 'application/json' } try: # 发起POST请求,请求体为空JSON api_resp = session.post(api_url, headers=headers, json={}) api_resp.raise_for_status() # 捕获HTTP错误 data = api_resp.json() # 从返回的JSON中提取目标字段(可根据实际返回结构调整键名) sector = data.get('sector') sector_group = data.get('sector_group') if not sector or not sector_group: print(f"API返回无目标字段[{url}]") except requests.exceptions.RequestException as e: print(f"POST请求失败[{url}]: {str(e)}") # 增加延迟,避免触发反爬 time.sleep(1) return sector, sector_group
关键细节说明
- Session会话:自动维护Cookie和请求状态,减少重复连接,降低被网站识别为恶意请求的概率。
- CSRF Token验证:网站的POST请求需要该Token做身份校验,必须从初始页面的
<meta name="csrf-token">标签提取。 - 请求频率控制:添加
time.sleep(1)避免短时间内大量请求触发反爬机制,可根据实际情况调整时长。 - 容错处理:每个环节都增加了异常捕获和日志输出,避免单个请求失败导致整个程序崩溃。
内容的提问来源于stack exchange,提问作者olino
相关产品推荐
相关产品推荐

