如何使用 python 请求和 bs4 (BeautifulSoup) 模块使用 cookie 和 javascript“javax.faces.ViewState”CDATA 在 .jsf 网站中进行网页抓取?

ped*_*gis 5 python jsf cdata web-scraping python-requests

我想自动从该网站提取数据:

\n

http://www.snirh.gov.br/hidroweb/publico/medicoes_historicas_abas.jsf

\n

提取我想要的数据应遵循的步骤的说明:

\n

从上面的 url 开始,单击“S\xc3\xa9ries Hist\xc3\xb3ricas”。您应该看到一个页面,其中包含带有一些输入的表单。就我而言,我只需要在“C\xc3\xb3digo da Esta\xc3\xa7\xc3\xa3o”输入中输入车站代码。假设电台代码是 938001,插入该代码并点击“Consultar”。现在您应该看到很多复选框。选中“Selecionar”下面的一项,该选项将选中所有复选框。假设我不需要各种数据,我想要降雨率和流量,我只选中“Chuva”下面的复选框和“Vaz\xc3\xa3o”下面的另一个复选框。之后需要选择要下载的文件类型,选择“Arquivo Texto (.TXT)”,这是.txt格式。之后需要生成文件,为此单击“Gerar Arquivo”。之后就可以下载文件,只需单击“Baixar Arquivo”即可。

\n

注意:该网站目前版本为v1.0.0.12,以后可能会有所不同。

\n

我有一个车站代码列表。想象一下,这些操作执行超过 1000 次会有多糟糕?!我想自动化这个!

\n

巴西的许多人一直在尝试从该网站自动提取数据。我发现的一些:

\n

非常旧的:https://www.youtube.com/watch?v =IWCrC0MlasQ

\n

其他:\n https://pt.stackoverflow.com/questions/60124/gerar-e-baixar-links-programaticamente/86150#86150

\n

https://pt.stackoverflow.com/questions/282111/r-download-de-dados-do-portal-hidroweb

\n

我发现的早期尝试,但这也不起作用,因为该网站已更改: https: //github.com/duartejr/pyHidroWeb

\n

所以很多人都需要这个,并且由于网站的更新,上述解决方案都没有更有效。

\n

我不想使用 selenium,与使用 requests 库的解决方案相比,它很慢,并且需要一个接口。

\n

我的尝试:

\n
#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom bs4 import BeautifulSoup\nimport requests\nfrom urllib import parse\n\n\nURL = \'http://www.snirh.gov.br/hidroweb/publico/apresentacao.jsf\'\n\ns = requests.Session()\n\nr = s.get(URL)\n\nJSESSIONID = s.cookies[\'JSESSIONID\']\n\nsoup = BeautifulSoup(r.content, "html.parser")\n\njavax_faces_ViewState = soup.find("input", {"type": "hidden", "name":"javax.faces.ViewState"})[\'value\']\n\n\nd = {}\nd[\'menuLateral:menuForm\'] = \'menuLateral:menuForm\'\nd[\'javax.faces.ViewState\'] = javax_faces_ViewState\nd[\'menuLateral:menuForm:menuSection:j_idt68:link\'] = \'menuLateral:menuForm:menuSection:j_idt68:link\'\n\nh = {}\nh[\'Accept\'] = \'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\'\nh[\'Accept-Encoding\'] = \'gzip, deflate\'\nh[\'Accept-Language\'] = \'pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7\'\nh[\'Cache-Control\'] = \'max-age=0\'\nh[\'Connection\'] = \'keep-alive\'\nh[\'Content-Length\'] = \'218\'\nh[\'Content-Type\'] = \'application/x-www-form-urlencoded\'\nh[\'Cookie\'] = \'_ga=GA1.3.4824711.1520011013; JSESSIONID={}; _gid=GA1.3.743342153.1522450617\'.format(JSESSIONID)\nh[\'Host\'] = \'www.snirh.gov.br\'\nh[\'Origin\'] = \'http://www.snirh.gov.br\'\nh[\'Referer\'] = \'http://www.snirh.gov.br/hidroweb/publico/apresentacao.jsf\'\nh[\'Upgrade-Insecure-Requests\'] = \'1\'\nh[\'User-Agent\'] = \'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\'\n\nURL2 = \'http://www.snirh.gov.br/hidroweb/publico/medicoes_historicas_abas.jsf\'\npost_response = s.post(URL2, headers=h, data=d)\n\n\nsoup = BeautifulSoup(post_response.text, "html.parser")\njavax_faces_ViewState = soup.find("input", {"type": "hidden", "name":"javax.faces.ViewState"})[\'value\']\n\n\ndef f_headers(JSESSIONID):\n    headers = {}\n    headers[\'Accept\'] = \'*/*\'\n    headers[\'Accept-Encoding\'] = \'gzip, deflate\'\n    headers[\'Accept-Language\'] = \'pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7\'\n    headers[\'Connection\'] = \'keep-alive\'\n    headers[\'Content-Length\'] = \'672\'\n    headers[\'Content-type\'] = \'application/x-www-form-urlencoded;charset=UTF-8\'\n    headers[\'Cookie\'] = \'_ga=GA1.3.4824711.1520011013; JSESSIONID=\' + str(JSESSIONID)\n    headers[\'Faces-Request\'] = \'partial/ajax\'\n    headers[\'Host\'] = \'www.snirh.gov.br\'\n    headers[\'Origin\'] = \'http://www.snirh.gov.br\'\n    headers[\'Referer\'] = \'http://www.snirh.gov.br/hidroweb/publico/medicoes_historicas_abas.jsf\'\n    headers[\'User-Agent\'] = \'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\'\n\n    return headers\n\n\ndef build_data(data, n, javax_faces_ViewState):\n\n    if n == 1:\n        data[\'form\'] = \'form\'\n        data[\'form:fsListaEstacoes:codigoEstacao\'] = \'938001\'\n        data[\'form:fsListaEstacoes:nomeEstacao\'] = \'\'\n        data[\'form:fsListaEstacoes:j_idt92\'] = \'a39c3713-c0f7-4461-b2c8-c2814b3a9af1\'\n        data[\'form:fsListaEstacoes:j_idt101\'] = \'a39c3713-c0f7-4461-b2c8-c2814b3a9af1\'\n        data[\'form:fsListaEstacoes:nomeResponsavel\'] = \'\'\n        data[\'form:fsListaEstacoes:nomeOperador\'] = \'\'\n        data[\'javax.faces.ViewState\'] = javax_faces_ViewState\n        data[\'javax.faces.source\'] = \'form:fsListaEstacoes:bt\'\n        data[\'javax.faces.partial.event\'] = \'click\'\n        data[\'javax.faces.partial.execute\'] = \'form:fsListaEstacoes:bt form:fsListaEstacoes\'\n        data[\'javax.faces.partial.render\'] = \'form:fsListaEstacoes:pnListaEstacoes\'\n        data[\'javax.faces.behavior.event\'] = \'action\'\n        data[\'javax.faces.partial.ajax\'] = \'true\'\n\n\ndata = {}\nbuild_data(data, 1, javax_faces_ViewState)\n\nheaders = f_headers(JSESSIONID)\n\npost_response = s.post(URL, headers=headers, data=data)\n\nprint(post_response.text)\n
Run Code Online (Sandbox Code Playgroud)\n

打印:

\n
<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<partial-response><changes><update id="javax.faces.ViewState"><![CDATA[-18212878\n48648292010:1675387092887841821]]></update></changes></partial-response>\n
Run Code Online (Sandbox Code Playgroud)\n

关于我尝试过的解释:

\n

我使用chrome开发工具,实际上单击“F12”,单击“网络”,然后在网站页面中单击“S\xc3\xa9ries Hist\xc3\xb3ricas”来发现什么是标题和表单。我认为我做对了。还有其他方法或者更好的方法吗?有些人告诉我关于邮递员和邮递员拦截器,但不知道如何使用以及它是否有帮助。

\n

之后,我在“C\xc3\xb3digo da Esta\xc3\xa7\xc3\xa3o”输入中填写了车站代码 938001,然后点击“Consultar”查看标题和表格。

\n

为什么网站返回 xml?这意味着出了什么问题吗?

\n

该 xml 有一个 CDATA 部分。

\n

XML 中的 <![CDATA[]]> 是什么意思?

\n

了解 CDATA 的基本思想,但它如何在这个网站中使用,以及我应该如何在网络抓取中使用它?我猜测它是用来保存部分信息的,但这只是一个猜测。我搞不清楚了。

\n

我也针对其他点击尝试了此操作,并获得了更多表单,并且响应是 xml。我没有把它放在这里,因为它使代码变得更大,并且 xml 也很大。

\n

与我相关的一个不完整的答案是:
\n /sf/answers/603770051/

\n

这个答案解释了使用 java 将文件上传到 JSF 生成的表单的步骤。这不是我的情况,我想使用 python requests 下载文件。

\n

一般的问题:

\n
    \n
  1. 什么时候不可能和可能使用 requests + bs4 来抓取网站?

    \n
  2. \n
  3. 进行这种网络抓取的步骤是什么?

    \n
  4. \n
  5. 在像这个网站这样的情况下,是否可以直接在一个请求中提取信息,或者我们必须像手动填写表格一样一步一步地模仿?根据这个答案,答案似乎是否定的/sf/answers/2496587061/

    \n
  6. \n
\n

我遇到过很多困难和疑惑。在我看来,对这种情况的解释存在差距。\n我同意这个SO问题\n Python urllib2 or requests post method \n在这一点上,大多数教程对于像我正在尝试的这个网站这样的情况都是无用的。像这样的问题/sf/ask/3065579891/ \n这和我的一样难,没有答案。

\n

这是我在 stackoverflow 上的第一篇文章,如果我犯了错误并且我的母语不是英语,请随时纠正我。

\n

Dan*_*she 0

1)使用 bs4 总是可以抓取 html 网站。但要获得您想要的反应,需要的不仅仅是美味的汤。

2)我对bs4的做法通常如下:

response = requests.request(
        method="GET",
        url='http://yourwebsite.com',
        params=params #(params should be a python object)
    )
soup = BeautifulSoup(response.text, 'html.parser')
Run Code Online (Sandbox Code Playgroud)

3) 如果您注意到,当您填写第一个表单(历史系列)并单击“提交”时,页面网址(或操作网址)不会更改。这是因为正在发出 ajax 请求来检索和更新当前页面上的数据。由于您看不到该请求,因此您不可能模仿该请求。

要提交表单,我建议查看Mechanize(一个用于填写和提交表单数据的 python 库)

import re
from mechanize import Browser

b = Browser()
b.open("http://yourwebsite.com")
b.select_form(name="form")
b["bacia"] = ["value"] 
response = b.submit()  # submit the form
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

4396 次

最近记录:

4 年,6 月 前