You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.10 04:27:03