【问题标题】:Is there any way to download csv file from “website button click” using Python?有没有办法使用 Python 从“网站按钮点击”下载 csv 文件?
【发布时间】:2019-09-18 23:12:04
【问题描述】:

我想从这个网站自动下载一个 CSV 文件 "Projects.csv"

https://www.vcsprojectdatabase.org/#/projects/st_/c_/ss_0/so_/di_/np_

可以通过单击 CSV 图标手动下载 CSV,但我不确定如何在 python 中自动下载并将 CSV 文件本地存储在我的驱动器上。

到目前为止,我已经尝试通过 chrome 开发人员控制台检查按钮元素,以在“网络”选项卡中找到正确的 url,就像这样?

https://www.vcsprojectdatabase.org/services/publicViewServices/fetchProjectsExport

但我不确定这个 URL 是否应该在末尾包含文件名,如下所示:

https://www.vcsprojectdatabase.org/services/publicViewServices/fetchProjectsExport/Projects.csv

这是我尝试过的,但它只是写了一个空白文件:

import requests

url = 'https://www.vcsprojectdatabase.org/services/publicViewServices/fetchProjectsExport/Projects.csv'

r = requests.get(url)
with open('a.csv', 'wb') as f:
    f.write(r.content) 

如何正确下载和保存 CSV 文件?

【问题讨论】:

    标签: javascript python url


    【解决方案1】:

    首先,你应该了解基于requests的HTTP协议。 JavaScript 执行的最终结果将形成 HTTP 请求,让服务器响应文件内容。您需要“反转”网页,找到如何创建正确的请求并尽可能地重复它。

    那么,让我们一步一步地尝试:

    1. 在执行下载的元素上单击鼠标右键,然后按“检查元素”
    2. 在源代码中,您可以看到该元素执行的 JavaScript 函数的名称
    3. 在控制台中输入函数名称,不带括号,然后单击应出现在控制台返回附近的按钮(此按钮将在源代码中打开此 JavaScript 函数)
    4. 在源代码中,我们看到函数在 ID 为 frmDownload 的 HTML 元素上执行提交。因此,返回“Inspector”选项卡并在搜索框中输入此 ID。
    5. 现在我们发现这个元素是 HTML form。此表单将 POST 请求发送到 URL https://www.vcsprojectdatabase.org/services/publicViewServices/fetchProjectsExport 以及下一个数据:

      searchTerm=
      country=
      sectoral_scope=0
      recentProjects=
      sort=projectId
      dir=DESC
      formatType=csv
      

      此信息足以尝试在 Python 中重复此请求。

    让我们编写一个小脚本,形成并发送相同的请求并将结果保存到 .csv 文件中:

    import requests
    
    data = {
        "searchTerm": "",
        "country": "",
        "sectoral_scope": "0",
        "recentProjects": "",
        "sort": "projectId",
        "dir": "DESC",
        "formatType": "csv"
    }
    
    file = requests.post("https://www.vcsprojectdatabase.org/services/publicViewServices/fetchProjectsExport", data)
    
    with open("res.csv", "wb+") as f:
        f.write(file.content)
    

    启动它,它......工作。 res.csv 包含正确的结果。

    但这还不是全部。通常一切都不是那么容易。为了让我们的请求看起来与浏览器发送的相同,我们应该查看request headers。要从浏览器捕获 HTTP 请求,我们可以打开“网络”选项卡:

    现在让我们按下网页上的下载按钮并下载 csv 文件。现在在 requests 表中,我们可以看到我们的 post 请求。单击它并查看“请求标头”部分中的“标头”选项卡。

    Cookie 标头,大部分在请求中并不重要,可以忽略。但是如果您对请求有一些问题,您应该查看以前的请求,在服务器响应中找到带有Set-Cookie 标头的请求并重复它。

    让我们改进我们的脚本并复制重要的(我们不包含主机、内容长度、连接,因为 Python 请求模块会自动添加它们;根本不需要 DNT 和升级不安全请求) 来自浏览器的标题。

    import requests
    
    data = {
        "searchTerm": "",
        "country": "",
        "sectoral_scope": "0",
        "recentProjects": "",
        "sort": "projectId",
        "dir": "DESC",
        "formatType": "csv"
    }
    
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Accept-Language":  "en-US,en;q=0.5",
        "Accept-Encoding": "gzip, deflate, br",
        "Referer": "https://www.vcsprojectdatabase.org/",
        "Content-Type": "application/x-www-form-urlencoded"
    }
    
    file = requests.post("https://www.vcsprojectdatabase.org/services/publicViewServices/fetchProjectsExport", data,
                         headers=headers)
    
    with open("res.csv", "wb+") as f:
        f.write(file.content)
    

    P.S.别忘了向网站所有者征求许可?

    【讨论】:

    • 非常感谢!这非常有效,您的逐步过程也可以帮助我更好地理解它。我现在需要去提高我的 JavaScript 知识!
    • @JackDoyle,如果需要,请随时问我。顺便说一句,accept我的回答,如果你觉得有用的话。
    • @OlvinR​​oght - 您的回答非常有帮助,如果您能帮助我遇到类似情况,我将不胜感激。 stackoverflow.com/q/66298758/10939394
    • 非常感谢您详细/逐步解释!
    【解决方案2】:

    请求类型为 POST,您需要指定相应的标头,您可以在开发者控制台中看到这些标头。此外,您不会提供表单数据。下面的代码有效。

    import requests
    
    url = 'https://www.vcsprojectdatabase.org/services/publicViewServices/fetchProjectsExport?searchTerm=&country=&sectoral_scope=0&recentProjects=&sort=projectId&dir=DESC&formatType=csv'
    headers = {'Content-Type': 'application/x-www-form-urlencoded','Accept': 'text/csv'}
    
    r = requests.post(url,headers=headers)
    with open('a.csv', 'wb') as f:
        f.write(r.content)
    

    【讨论】:

    • 将 POST 请求中的数据作为 GET 请求参数发送是非常糟糕的做法。是的,它适用于这个请求,但你肯定很快就会遇到问题。
    猜你喜欢
    • 2021-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-22
    • 1970-01-01
    • 2016-07-02
    • 2021-10-16
    相关资源
    最近更新 更多