Python 无法使用请求从网页中刮取名称

Python 无法使用请求从网页中刮取名称,python,python-3.x,web-scraping,python-requests,Python,Python 3.x,Web Scraping,Python Requests,我已经用python创建了一个脚本来获取一个名称,这个名称在填写网页中的输入时填充。以下是如何获得该名称->打开该网页后(下面给出了sitelink),将16803放在CP Number旁边,然后点击搜索按钮 我知道如何使用selenium抓住它,但我不想走这条路。我在这里尝试使用请求模块收集名称。我试着模仿脚本中的步骤(我可以在chrome开发工具中看到),了解如何将请求发送到该站点。我唯一不能在有效负载参数中自动提供的是滚动顶 这是我的尝试: import requests from bs

我已经用python创建了一个脚本来获取一个名称,这个名称在填写网页中的输入时填充。以下是如何获得该名称->打开该网页后(下面给出了sitelink),将
16803
放在
CP Number
旁边,然后点击搜索按钮

我知道如何使用
selenium
抓住它,但我不想走这条路。我在这里尝试使用
请求
模块收集名称。我试着模仿脚本中的步骤(我可以在chrome开发工具中看到),了解如何将请求发送到该站点。我唯一不能在
有效负载
参数中自动提供的是
滚动顶

这是我的尝试:

import requests
from bs4 import BeautifulSoup

URL = "https://www.icsi.in/student/Members/MemberSearch.aspx"

with requests.Session() as s:
    r = s.get(URL)
    cookie_item = "; ".join([str(x)+"="+str(y) for x,y in r.cookies.items()])
    soup = BeautifulSoup(r.text,"lxml")

    payload = {
        'StylesheetManager_TSSM':soup.select_one("#StylesheetManager_TSSM")['value'],
        'ScriptManager_TSM':soup.select_one("#ScriptManager_TSM")['value'],
        '__VIEWSTATE':soup.select_one("#__VIEWSTATE")['value'],
        '__VIEWSTATEGENERATOR':soup.select_one("#__VIEWSTATEGENERATOR")['value'],
        '__EVENTVALIDATION':soup.select_one("#__EVENTVALIDATION")['value'],
        'dnn$ctlHeader$dnnSearch$Search':soup.select_one("#dnn_ctlHeader_dnnSearch_SiteRadioButton")['value'],
        'dnn$ctr410$MemberSearch$ddlMemberType':0,
        'dnn$ctr410$MemberSearch$txtCpNumber': 16803,
        'ScrollTop': 474,
        '__dnnVariable': soup.select_one("#__dnnVariable")['value'],
    }

    headers = {
        'Content-Type':'multipart/form-data; boundary=----WebKitFormBoundaryBhsR9ScAvNQ1o5ks',
        'Referer': 'https://www.icsi.in/student/Members/MemberSearch.aspx',
        'Cookie':cookie_item,
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
    }
    res = s.post(URL,data=payload,headers=headers)
    soup_obj = BeautifulSoup(res.text,"lxml")
    name = soup_obj.select_one(".name_head > span").text
    print(name)
当我执行上述脚本时,出现以下错误:

AttributeError: 'NoneType' object has no attribute 'text'
如何获取在使用请求填写网页输入时填充的名称?


代码的主要问题是数据编码。我注意到您已经将内容类型头设置为“multipart/formdata”,但这还不足以创建多部分编码的数据。事实上,这是一个问题,因为实际编码不同,因为您使用的是URL编码POST数据的
data
参数。要创建多部分编码数据,应使用
文件
参数

您可以通过向
文件
传递一个额外的伪参数来实现这一点

res = s.post(URL, data=payload, files={'file':''})
(这将改变所有POST数据的编码,而不仅仅是
'file'
字段)

或者,您可以将
有效负载
字典中的值转换为元组,这是发布带有请求的文件时所需的结构

payload = {k:(None, str(v)) for k,v in payload.items()}
第一个值是文件名;在这种情况下不需要它,所以我将它设置为
None

接下来,您的POST数据应该包含获得有效响应所需的
\uu EVENTTARGET
值。(创建POST data dictionary时,提交服务器所需的所有数据非常重要。我们可以从浏览器中获取这些数据:通过检查HTML表单或网络流量)

import requests
from bs4 import BeautifulSoup

URL = "https://www.icsi.in/student/Members/MemberSearch.aspx"

with requests.Session() as s:
    r = s.get(URL)
    soup = BeautifulSoup(r.text,"lxml")

    payload = {i['name']: i.get('value', '') for i in soup.select('input[name]')}
    payload['dnn$ctr410$MemberSearch$txtCpNumber'] = 16803
    payload["__EVENTTARGET"] = 'dnn$ctr410$MemberSearch$btnSearch'
    payload = {k:(None, str(v)) for k,v in payload.items()}

    r = s.post(URL, files=payload)
    soup_obj = BeautifulSoup(r.text,"lxml")
    name = soup_obj.select_one(".name_head > span").text
    print(name)

经过更多的测试,我发现服务器也接受URL编码的数据(可能是因为没有发布文件)。因此,只要不更改默认的内容类型标题,就可以使用
数据
文件
获得有效的响应


不需要添加任何额外的标题。使用
会话
对象时,默认情况下存储并提交cookie。使用
数据
参数时,会自动创建内容类型标题-“application/x-www-form-urlencoded”,使用
文件
“multipart/form data”。不需要更改默认的用户代理或添加引用程序。

我认为传递的cookie多于脚本中显示的cookie。另外,您的前两个选择器检索空字符串文本,尽管看起来您可以从其他地方获取这些值(至少可以确定一个)。这些值在不同的请求中保持稳定,但可能不会持续更长的时间。您的答案总是干净、简洁、完美@t.m.adam。我几乎以为这样做是不可能的。让我们再等几天,只要悬赏还在,其他人也会看到你的解决方案。如果我再贪心一点,我想你不会责怪我@t.m.adam。我发现这个
res=s.post(URL,data=payload,files={'file':''''})
非常有趣,我愿意在以后的案例中遵守。然而,这个
files={'file':''}
似乎不知从何而来。你能建议我如何使用这个吗?我只是使用了
res=s.post(URL,data=payload,files={'file':'''}),headers=headers)
,情况可能不是这样。谢谢。对不起,逗号错了。
{'file':''}
字典是一个伪参数,您可以使用它对数据进行多部分编码。如果将值(非空字典)传递给
文件
,则所有POST数据都是多部分编码的(数据
文件
)。因此,是的,如果不想将
有效载荷
的值转换为元组,可以在
文件中使用伪参数。@MITHU有一个清除按钮,用于清除表单的内容,如果提交此值,表单似乎不会被处理。如果删除
'ctl00$ctl00$MainContent$MainContentContainer$Clear'
键,则第二个脚本也可以工作。如果将数据逐个与成功请求的数据进行比较,您可以检测到有用(或不需要的)值。是的,它现在可以工作。你一直是我的好老师@t.m.adam。顺便说一句,这就是我如何让它工作的
payload={item['name']:item.get('value','')用于汤中的项目。如果项目['name']中没有“$Clear”,请选择('form1 input[name]')