基于BeautifulSoup爬取多页面列表时的翻页与日期范围筛选问题求助
解决慕尼黑BA申请页面爬取的分页与筛选问题
嘿,作为爬取新手遇到这种情况太正常了——很多官方网站的分页和筛选都不会用直观的URL参数,而是靠表单请求或者后台参数来处理。我给你梳理几个关键的解决方向,一步步来:
1. 先搞清楚网站的分页逻辑(为什么下一页链接没用?)
你遇到的“下一页链接跳回第一页”,大概率是因为这个网站的分页不是靠URL参数传递,而是通过POST表单参数或者隐藏的请求参数来实现的。你需要用浏览器的开发者工具来抓真实的请求:
- 打开目标页面,按F12打开开发者工具,切换到「Network」标签
- 点击页面上的「下一页」按钮,观察Network里新出现的请求:
- 看请求类型是GET还是POST
- 查看「Form Data」(POST请求)或者「Query String Parameters」(GET请求)里的参数,比如有没有
page、offset、size这类和分页相关的字段 - 同时看日期筛选的参数:如果页面上有选择年份的选项,选择2024年后,同样抓请求里的参数,比如
year=2024或者startDate=2024-01-01
2. 替换你的请求方式,模拟真实的分页/筛选请求
一旦拿到这些参数,你就可以用requests库直接构造包含分页和筛选参数的请求,而不是只请求初始URL。比如如果是POST请求,代码框架大概是这样:
import requests import time from bs4 import BeautifulSoup # 基础请求地址(从开发者工具里抓的真实请求地址) target_url = "https://risi.muenchen.de/risi/antrag/ba/baantraguebersicht?2" # 模拟浏览器的请求头,避免被反爬拦截 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36' } # 初始化请求参数:分页+筛选今年数据 request_params = { 'year': 2024, # 假设抓出来的年份参数是year 'page': 1, # 当前页码 'size': 10 # 每页条目数,对应你看到的10条/页 } all_entries = [] while True: # 根据实际请求类型选get/post,这里假设是POST response = requests.post(target_url, headers=headers, data=request_params) # 检查请求是否成功 if response.status_code != 200: print(f"请求第{request_params['page']}页失败,状态码:{response.status_code}") break soup = BeautifulSoup(response.content, 'html.parser') # 更精准地定位条目:不要用uls[17]这种不稳定的索引,找条目专属的class # 比如看页面里每个条目li的class,假设是"antrag-list-item" entries = soup.find_all('li', class_='antrag-list-item') if not entries: print("没有更多条目了,停止爬取") break for entry in entries: # 提取数据:用更可靠的方式定位,避免索引错误 title = entry.find('a', class_='headline-link').get_text(strip=True) # 先找到"Beschlossen am"的标签,再找它的兄弟节点(对应的数值) beschlossen = entry.find('div', string='Beschlossen am').find_next_sibling('div').get_text(strip=True) typ = entry.find('div', string='Antragstyp').find_next_sibling('div').get_text(strip=True) status = entry.find('div', string='Status').find_next_sibling('div').get_text(strip=True) ba = entry.find_all('a', class_='icon_action')[0].get_text(strip=True) referat = entry.find_all('a', class_='icon_action')[1].get_text(strip=True) all_entries.append({ '标题': title, '决议日期': beschlossen, '类型': typ, '状态': status, 'BA': ba, '部门': referat }) print(f"已获取第{request_params['page']}页,共{len(all_entries)}条数据") request_params['page'] += 1 # 页码加1,继续下一页 time.sleep(1) # 加个延迟,避免请求太频繁被封 # 后续就可以分析all_entries里的数据了
3. 几个关键的优化点(避免踩坑)
- 不要用索引定位元素:你之前用
uls[17]和find_all[index],一旦页面结构微调,代码就会报错。尽量用元素的class、文本内容来定位,比如find('div', string='Beschlossen am')。 - 添加请求头和延迟:官方网站一般有反爬机制,加
User-Agent模拟浏览器,再加time.sleep()控制请求频率,避免被IP封禁。 - 检查请求是否成功:每次请求后判断
status_code,如果返回403/500,就停止或者重试。
4. 如果遇到动态加载(比如AJAX渲染)
如果上面的方法还是不行,可能网站是用JavaScript动态加载内容的,这时候你需要用selenium或者playwright来模拟浏览器的点击操作,比如自动点击下一页、选择年份。不过先优先尝试抓请求参数的方法,因为这种方法更高效,也不需要额外安装浏览器驱动。
内容的提问来源于stack exchange,提问作者Max Kranabetter
相关产品推荐
相关产品推荐

