Python - 从网页下载带链接的CSV文件

2 投票
2 回答
3203 浏览
提问于 2025-04-18 05:22

我想通过一个Python脚本从这个页面下载CSV文件。

但是,当我在浏览器中直接访问CSV文件的链接时,会出现一个协议表单。我必须先同意这个表单,才能下载文件。

我无法获取到CSV文件的确切网址。这个链接背后有一个值被发送到数据库,用来获取文件,比如说PERIOD_ID=2013-0

https://www.paoilandgasreporting.state.pa.us/publicreports/Modules/DataExports/ExportProductionData.aspx?PERIOD_ID=2013-0

我试过用urllib2.open()urllib2.read(),但是得到的却是协议表单的HTML内容,而不是文件内容。

我该怎么写Python代码来处理这个重定向,然后获取CSV文件并保存到磁盘上呢?

2 个回答

3

你需要设置一个叫做 ASP.NET_SessionId 的cookie。你可以通过在Chrome浏览器中右键点击网页,选择 检查元素 来找到它,或者使用Firefox浏览器和 Firebug 插件。

在Chrome中操作:

  1. 在网页上右键点击(确保你已经同意了相关条款),然后选择 检查元素
  2. 点击 资源 -> Cookies
  3. 选择列表中唯一的那个元素
  4. 复制 ASP.NET_SessionId 元素的

在Firebug中操作:

  1. 在网页上右键点击(同样要确保你已经同意了相关条款),然后点击 *用Firebug检查元素
  2. 点击 Cookies
  3. 复制 ASP.NET_SessionId 元素的

在我的情况下,我得到了 ihbjzynwfcfvq4nzkncbviou - 这个值可能对你也有效,如果不行,你需要按照上面的步骤来操作。

把这个cookie添加到你的请求中,然后使用 requests 模块下载文件(这个方法是根据 eladc 的一个回答):

import requests

cookies = {'ASP.NET_SessionId': 'ihbjzynwfcfvq4nzkncbviou'}
r = requests.get(
    url=('https://www.paoilandgasreporting.state.pa.us/publicreports/Modules/'
         'DataExports/ExportProductionData.aspx?PERIOD_ID=2013-0'),
    cookies=cookies
)

with open('2013-0.csv', 'wb') as ofile:
    for chunk in r.iter_content(chunk_size=1024):
        ofile.write(chunk)
        ofile.flush()
1

这是我的建议,关于如何自动应用服务器的cookie,基本上就是模拟标准的客户端会话行为。

(这灵感来自@pope的回答 554580。)

import urllib2
import urllib
from lxml import etree

_TARGET_URL = 'https://www.paoilandgasreporting.state.pa.us/publicreports/Modules/DataExports/ExportProductionData.aspx?PERIOD_ID=2013-0'
_AGREEMENT_URL = 'https://www.paoilandgasreporting.state.pa.us/publicreports/Modules/Welcome/Agreement.aspx'
_CSV_OUTPUT = 'urllib2_ProdExport2013-0.csv'


class _MyHTTPRedirectHandler(urllib2.HTTPRedirectHandler):

    def http_error_302(self, req, fp, code, msg, headers):
        print 'Follow redirect...'  # Any cookie manipulation in-between redirects should be implemented here.
        return urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)

    http_error_301 = http_error_303 = http_error_307 = http_error_302

cookie_processor = urllib2.HTTPCookieProcessor()

opener = urllib2.build_opener(_MyHTTPRedirectHandler, cookie_processor)
urllib2.install_opener(opener)

response_html = urllib2.urlopen(_TARGET_URL).read()

print 'Cookies collected:', cookie_processor.cookiejar

page_node, submit_form = etree.HTML(response_html), {}  # ElementTree node + dict for storing hidden input fields.
for input_name in ['ctl00$MainContent$AgreeButton', '__EVENTVALIDATION', '__VIEWSTATE']:  # Form `input` fields used on the ``Agreement.aspx`` page.
    submit_form[input_name] = page_node.xpath('//input[@name="%s"][1]' % input_name)[0].attrib['value']
    print 'Form input \'%s\' found (value: \'%s\')' % (input_name, submit_form[input_name])

# Submits the agreement form back to ``_AGREEMENT_URL``, which redirects to the CSV download at ``_TARGET_URL``.
csv_output = opener.open(_AGREEMENT_URL, data=urllib.urlencode(submit_form)).read()
print csv_output

with file(_CSV_OUTPUT, 'wb') as f:  # Dumps the CSV output to ``_CSV_OUTPUT``.
    f.write(csv_output)
    f.close()

祝你好运!

[编辑]

关于事情的原因,我觉得@Steinar Lima说得对,确实需要一个会话cookie。不过,除非你已经访问过Agreement.aspx页面并通过提供商的网站提交了响应,否则你从浏览器的网页检查器复制的cookie只会导致你再次跳转到欢迎来到PA DEP石油和天然气报告网站的欢迎页面。这当然就失去了用Python脚本来完成这项工作的意义。

撰写回答