URL无变化时如何使用requests模块爬取网页第二页的机构名称
问题原因
返回会话过期报错由三类错误导致:
- 硬编码了JSF框架动态生成的组件ID,不同加载场景下
j_idt开头的ID会动态变化,固定的contentForm:j_idt902:j_idt955_2_2参数不合法 - 缺失Ajax请求必须的请求头,被服务器判定为非法访问
javax.faces.ViewState为单次有效参数,后续请求需要同步更新,不能一直使用首次获取的值
解决方案
核心调整逻辑
- 从首次GET返回的页面中动态提取第二页按钮的ID、分页容器ID等动态参数,不硬编码
- 补全所有Ajax请求需要的请求头
- 从每次请求返回的XML响应中提取更新后的
javax.faces.ViewState值,用于后续翻页请求
可运行修正代码
import requests from bs4 import BeautifulSoup base_link = 'https://www.gebiz.gov.sg/ptn/opportunity/BOListing.xhtml?origin=menu' post_url = 'https://www.gebiz.gov.sg/ptn/opportunity/BOListing.xhtml' with requests.Session() as s: # 统一设置请求头 s.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept': 'application/xml, text/xml, */*; q=0.01', 'Accept-Language': 'zh-CN,zh;q=0.9', 'X-Requested-With': 'XMLHttpRequest', 'Faces-Request': 'partial/ajax', 'Origin': 'https://www.gebiz.gov.sg', 'Referer': base_link }) # 首次加载获取初始参数 r = s.get(base_link) init_soup = BeautifulSoup(r.text, "lxml") current_view_state = init_soup.select_one('input[name="javax.faces.ViewState"]')['value'] # 动态提取第二页按钮ID page2_btn = init_soup.select_one('div.ui-paginator-pages a.ui-paginator-page[aria-label*="2"]') if not page2_btn: page2_btn = init_soup.select_one('div.ui-paginator-pages span.ui-state-default + a') page_btn_id = page2_btn['id'] # 提取分页容器ID pagination_container_id = page2_btn.parent.parent['id'] payload = { 'javax.faces.partial.ajax': 'true', 'javax.faces.source': page_btn_id, 'javax.faces.partial.event': 'click', 'javax.faces.partial.execute': f'{page_btn_id} {pagination_container_id}', 'javax.faces.partial.render': f'{pagination_container_id} contentForm dialogForm', 'javax.faces.behavior.event': 'action', 'contentForm': 'contentForm', 'contentForm:j_idt171_windowName': '', 'contentForm:j_idt192_searchBar_INPUT-SEARCH': '', 'contentForm:j_id135_0': 'Title', 'contentForm:j_id135_1': 'Document No.', 'contentForm:j_id136': 'Match All', 'contentForm:j_idt859_select': '0', 'javax.faces.ViewState': current_view_state } res = s.post(post_url, data=payload) # 解析JSF返回的XML格式响应,提取更新后的页面片段 resp_xml = BeautifulSoup(res.text, "lxml") page_content = resp_xml.find('update', id=pagination_container_id).text page_soup = BeautifulSoup(page_content, "lxml") # 提取机构名称 for item in page_soup.select(".commandLink_TITLE-BLUE"): print(item.get_text(strip=True))
后续翻页说明
如果需要爬取更多分页,每次POST请求后从响应的XML中提取新的javax.faces.ViewState值替换旧值,再按同样逻辑提取对应页码按钮的ID构造payload即可。
内容的提问来源于stack exchange,提问作者MITHU
相关产品推荐
相关产品推荐

