【问题标题】:Python: Unable to download with selenium in webpagePython:无法在网页中使用 selenium 下载
【发布时间】:2017-10-19 16:48:40
【问题描述】:

我的目的是从https://www.shareinvestor.com/prices/price_download_zip_file.zip?type=history_all&market=bursa 下载一个 zip 文件 这是此网页中的链接https://www.shareinvestor.com/prices/price_download.html#/?type=price_download_all_stocks_bursa。然后保存到这个目录"/home/vinvin/shKLSE/(我用的是pythonaywhere)。然后解压,csv文件解压到目录中。

代码运行到最后没有错误,但没有下载。 当手动点击https://www.shareinvestor.com/prices/price_download_zip_file.zip?type=history_all&market=bursa 时,会自动下载 zip 文件。

我的代码使用了有效的用户名和密码。使用真实的用户名和密码,以便更容易理解问题。

    #!/usr/bin/python
    print "hello from python 2"

    import urllib2
    from selenium import webdriver
    from selenium.webdriver.common.keys import Keys
    import time
    from pyvirtualdisplay import Display
    import requests, zipfile, os    

    display = Display(visible=0, size=(800, 600))
    display.start()

    profile = webdriver.FirefoxProfile()
    profile.set_preference('browser.download.folderList', 2)
    profile.set_preference('browser.download.manager.showWhenStarting', False)
    profile.set_preference('browser.download.dir', "/home/vinvin/shKLSE/")
    profile.set_preference('browser.helperApps.neverAsk.saveToDisk', '/zip')

    for retry in range(5):
        try:
            browser = webdriver.Firefox(profile)
            print "firefox"
            break
        except:
            time.sleep(3)
    time.sleep(1)

    browser.get("https://www.shareinvestor.com/my")
    time.sleep(10)
    login_main = browser.find_element_by_xpath("//*[@href='/user/login.html']").click()
    print browser.current_url
    username = browser.find_element_by_id("sic_login_header_username")
    password = browser.find_element_by_id("sic_login_header_password")
    print "find id done"
    username.send_keys("bkcollection")
    password.send_keys("123456")
    print "log in done"
    login_attempt = browser.find_element_by_xpath("//*[@type='submit']")
    login_attempt.submit()
    browser.get("https://www.shareinvestor.com/prices/price_download.html#/?type=price_download_all_stocks_bursa")
    print browser.current_url
    time.sleep(20)
    dl = browser.find_element_by_xpath("//*[@href='/prices/price_download_zip_file.zip?type=history_all&market=bursa']").click()
    time.sleep(30)

    browser.close()
    browser.quit()
    display.stop()

   zip_ref = zipfile.ZipFile(/home/vinvin/sh/KLSE, 'r')
   zip_ref.extractall(/home/vinvin/sh/KLSE)
   zip_ref.close()
   os.remove(zip_ref)

HTML sn-p:

<li><a href="/prices/price_download_zip_file.zip?type=history_all&amp;market=bursa">All Historical Data</a> <span>About 220 MB</span></li>

请注意,当我复制 sn-p 时会显示 &amp。它是从视图源中隐藏的,所以我猜它是用 JavaScript 编写的。

我发现的观察

  1. 即使我运行代码没有错误,目录home/vinvin/shKLSE 也没有创​​建

  2. 我尝试下载一个小得多的 zip 文件,它可以在一秒钟内完成,但在等待 30 秒后仍然无法下载。 dl = browser.find_element_by_xpath("//*[@href='/prices/price_download_zip_file.zip?type=history_daily&amp;date=20170519&amp;market=bursa']").click()

【问题讨论】:

  • 想知道您是否可以接受 Windows 平台上的解决方案?谢谢
  • @Dev 可以接受。只要它能够始终如一地工作。
  • 观察到您在代码中使用了folderListshowWhenStartingdownload.dirneverAsk.saveToDisk,但您在描述中没有提到它们。您对这些功能有什么要求吗?谢谢
  • 没有,只要下载到zip目录下解压即可。

标签: javascript python selenium


【解决方案1】:

我认为您的代码块没有任何重大缺陷。但这里有一些通过这个解决方案和这个自动化测试脚本的执行的建议:

  1. 此代码在非市场时段完美运行。在市场开放时间,很多 JavaScriptAjax Calls 都在发挥作用,处理这些问题超出了本问题的范围。
  2. 您可以考虑首先检查预期的下载目录,如果不可用,请创建一个新目录。此功能的代码块采用 Windows 风格,可在 Windows 平台上完美运行。
  3. 点击“登录”后,会产生一些wait,以便 HTML DOM 正确呈现。
  4. 当您想查看下载过程时,您需要在FirefoxProfile 中设置更多首选项,如下面的代码所述。
  5. 始终考虑通过browser.maximize_window() 最大化浏览器窗口
  6. 开始下载时,您需要等待足够长的时间才能完全下载文件。
  7. 如果最后使用browser.quit(),则不需要使用browser.close()
  8. 您可以考虑将所有time.sleep() 替换为ImplicitlyWaitExplicitWaitFluentWait
  9. 这是您自己的代码块,其中包含一些简单的调整:

    #!/usr/bin/python
    print "hello from python 2"
    
    import urllib2
    from selenium import webdriver
    from selenium.webdriver.common.keys import Keys
    import time
    from pyvirtualdisplay import Display
    import requests, zipfile, os    
    
    display = Display(visible=0, size=(800, 600))
    display.start()
    
    newpath = 'C:\\home\\vivvin\\shKLSE'
    if not os.path.exists(newpath):
        os.makedirs(newpath)    
    
    profile = webdriver.FirefoxProfile()
    profile.set_preference("browser.download.dir",newpath);
    profile.set_preference("browser.download.folderList",2);
    profile.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/zip");
    profile.set_preference("browser.download.manager.showWhenStarting",False);
    profile.set_preference("browser.helperApps.neverAsk.openFile","application/zip");
    profile.set_preference("browser.helperApps.alwaysAsk.force", False);
    profile.set_preference("browser.download.manager.useWindow", False);
    profile.set_preference("browser.download.manager.focusWhenStarting", False);
    profile.set_preference("browser.helperApps.neverAsk.openFile", "");
    profile.set_preference("browser.download.manager.alertOnEXEOpen", False);
    profile.set_preference("browser.download.manager.showAlertOnComplete", False);
    profile.set_preference("browser.download.manager.closeWhenDone", True);
    profile.set_preference("pdfjs.disabled", True);
    
    for retry in range(5):
        try:
            browser = webdriver.Firefox(profile)
            print "firefox"
            break
        except:
            time.sleep(3)
    time.sleep(1)
    
    browser.maximize_window()
    browser.get("https://www.shareinvestor.com/my")
    time.sleep(10)
    login_main = browser.find_element_by_xpath("//*[@href='/user/login.html']").click()
    time.sleep(10)
    print browser.current_url
    username = browser.find_element_by_id("sic_login_header_username")
    password = browser.find_element_by_id("sic_login_header_password")
    print "find id done"
    username.send_keys("bkcollection")
    password.send_keys("123456")
    print "log in done"
    login_attempt = browser.find_element_by_xpath("//*[@type='submit']")
    login_attempt.submit()
    browser.get("https://www.shareinvestor.com/prices/price_download.html#/?type=price_download_all_stocks_bursa")
    print browser.current_url
    time.sleep(20)
    dl = browser.find_element_by_xpath("//*[@href='/prices/price_download_zip_file.zip?type=history_all&market=bursa']").click()
    time.sleep(900)
    
    browser.close()
    browser.quit()
    display.stop()
    
    zip_ref = zipfile.ZipFile(/home/vinvin/sh/KLSE, 'r')
    zip_ref.extractall(/home/vinvin/sh/KLSE)
    zip_ref.close()
    os.remove(zip_ref)
    

如果这能回答您的问题,请告诉我。

【讨论】:

  • 谢谢。您如何建议更换ImplicitlyWaitExplicitWait FluentWait.?确保在处理之前下载它是最好的主意。
  • @bkcollection 您可以在脚本的开头引入一次ImplicitlyWait,以便 Selenium 持续等待。您还可以使用ExplicitWait 使某些元素在 HTML DOM 中可见/可点击。或者,您可以使用FluentWait 通过频繁轮询来加速执行,忽略异常。对于Make sure it is downloaded before procedd,您可以考虑使用urllib.request.urlretrieve 模块,该模块将阻塞直到下载完成。谢谢
  • 您能否编辑答案。我知道time.sleep 是个坏主意。谢谢
  • @bkcollection 这都是你自己的代码。我刚刚添加了使您的代码块正常工作所需的最小调整:) 谢谢
  • @DebanjanB 它对我有用。但是有没有办法重命名下载的文件?
【解决方案2】:

我重写了您的脚本,并由 cmets 解释了我做出这些更改的原因。我认为你的主要问题可能是一个糟糕的 mimetype,但是,你的脚本有一个系统性问题的日志,这充其量是不可靠的。此重写使用显式等待,完全无需使用time.sleep(),使其能够尽可能快地运行,同时还消除了由网络拥塞引起的错误。

您需要执行以下操作以确保已安装所有模块:

pip install requests explicit selenium retry pyvirtualdisplay

脚本:

#!/usr/bin/python

from __future__ import print_function  # Makes your code portable

import os
import glob
import zipfile
from contextlib import contextmanager

import requests
from retry import retry
from explicit import waiter, XPATH, ID
from selenium import webdriver
from pyvirtualdisplay import Display
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.wait import WebDriverWait

DOWNLOAD_DIR = "/tmp/shKLSE/"


def build_profile():
    profile = webdriver.FirefoxProfile()
    profile.set_preference('browser.download.folderList', 2)
    profile.set_preference('browser.download.manager.showWhenStarting', False)
    profile.set_preference('browser.download.dir', DOWNLOAD_DIR)
    # I think your `/zip` mime type was incorrect. This works for me
    profile.set_preference('browser.helperApps.neverAsk.saveToDisk',
                           'application/vnd.ms-excel,application/zip')

    return profile


# Retry is an elegant way to retry the browser creation
# Though you should narrow the scope to whatever the actual exception is you are
# retrying on
@retry(Exception, tries=5, delay=3)
@contextmanager  # This turns get_browser into a context manager
def get_browser():
    # Use a context manager with Display, so it will be closed even if an
    # exception is thrown
    profile = build_profile()
    with Display(visible=0, size=(800, 600)):
        browser = webdriver.Firefox(profile)
        print("firefox")
        try:
            yield browser
        finally:
            # Let a try/finally block manage closing the browser, even if an
            # exception is called
            browser.quit()


def main():
    print("hello from python 2")
    with get_browser() as browser:
        browser.get("https://www.shareinvestor.com/my")

        # Click the login button
        # waiter is a helper function that makes it easy to use explicit waits
        # with it you dont need to use time.sleep() calls at all
        login_xpath = '//*/div[@class="sic_logIn-bg"]/a'
        waiter.find_element(browser, login_xpath, XPATH).click()
        print(browser.current_url)

        # Log in
        username = "bkcollection"
        username_id = "sic_login_header_username"
        password = "123456"
        password_id = "sic_login_header_password"
        waiter.find_write(browser, username_id, username, by=ID)
        waiter.find_write(browser, password_id, password, by=ID, send_enter=True)

        # Wait for login process to finish by locating an element only found
        # after logging in, like the Logged In Nav
        nav_id = 'sic_loggedInNav'
        waiter.find_element(browser, nav_id, ID)

        print("log in done")

        # Load the target page
        target_url = ("https://www.shareinvestor.com/prices/price_download.html#/?"
                      "type=price_download_all_stocks_bursa")
        browser.get(target_url)
        print(browser.current_url)

        # CLick download button
        all_data_xpath = ("//*[@href='/prices/price_download_zip_file.zip?"
                          "type=history_all&market=bursa']")
        waiter.find_element(browser, all_data_xpath, XPATH).click()

        # This is a bit challenging: You need to wait until the download is complete
        # This file is 220 MB, it takes a while to complete. This method waits until
        # there is at least one file in the dir, then waits until there are no
        # filenames that end in `.part`
        # Note that is is problematic if there is already a file in the target dir. I
        # suggest looking into using the tempdir module to create a unique, temporary
        # directory for downloading every time you run your script
        print("Waiting for download to complete")
        at_least_1 = lambda x: len(x("{0}/*.zip*".format(DOWNLOAD_DIR))) > 0
        WebDriverWait(glob.glob, 300).until(at_least_1)

        no_parts = lambda x: len(x("{0}/*.part".format(DOWNLOAD_DIR))) == 0
        WebDriverWait(glob.glob, 300).until(no_parts)

        print("Download Done")

        # Now do whatever it is you need to do with the zip file
        # zip_ref = zipfile.ZipFile(DOWNLOAD_DIR, 'r')
        # zip_ref.extractall(DOWNLOAD_DIR)
        # zip_ref.close()
        # os.remove(zip_ref)

        print("Done!")


if __name__ == "__main__":
    main()

完全披露:我维护显式模块。它旨在使使用显式等待更容易,就像这样的情况,网站根据用户交互缓慢加载动态内容。您可以用直接显式等待替换上面的所有 waiter.XXX 调用。

【讨论】:

  • 你能解释一下@retry(Exception, tries=5, delay=3) @contextmanager 吗?有没有其他通用模块可以用来代替retryexplicit? Pythonanywhere 没有这两个模块。
  • @bkcollection,在我更新我的答案之前,您能先告诉我是否可以使用它来安装外部依赖项吗?具体来说,retryexplicit? https://help.pythonanywhere.com/pages/InstallingNewModules/
  • 它基本上可以正常工作。但它通常在print "firefox" 之后卡住,需要重新运行。它发生在三分之二
  • 更多细节解释会更适合初学者
【解决方案3】:

我没有在您提到的网站上尝试过,但是以下代码可以完美运行并下载 ZIP。如果您无法下载 zip,则 Mime 类型可能不同。您可以使用 chrome 浏览器和网络检查来检查您尝试下载的文件的 mime 类型

profile = webdriver.FirefoxProfile()
profile.set_preference('browser.download.folderList', 2)
profile.set_preference('browser.download.manager.showWhenStarting', False)
profile.set_preference('browser.download.dir', "/home/vinvin/shKLSE/")
profile.set_preference('browser.helperApps.neverAsk.saveToDisk', 'application/zip')

browser = webdriver.Firefox(profile)
browser.get("http://www.colorado.edu/conflict/peace/download/peace.zip")

【讨论】:

    【解决方案4】:

    原因是网页加载缓慢。我在打开网页链接后添加了20秒的等待

    login_attempt.submit()
    browser.get("https://www.shareinvestor.com/prices/price_download.html#/?type=price_download_all_stocks_bursa")
    print browser.current_url
    time.sleep(20)
    dl = browser.find_element_by_xpath("//*[@href='/prices/price_download_zip_file.zip?type=history_all&market=bursa']").click()
    

    它没有返回错误。

    另外, /zip 的 MIME 类型不正确。改成profile.set_preference('browser.helperApps.neverAsk.saveToDisk', 'application/zip')

    最后的修正:

       #!/usr/bin/python
        print "hello from python 2"
    
        import urllib2
        from selenium import webdriver
        from selenium.webdriver.common.keys import Keys
        import time
        from pyvirtualdisplay import Display
        import requests, zipfile, os    
    
        display = Display(visible=0, size=(800, 600))
        display.start()
    
        profile = webdriver.FirefoxProfile()
        profile.set_preference('browser.download.folderList', 2)
        profile.set_preference('browser.download.manager.showWhenStarting', False)
        profile.set_preference('browser.download.dir', "/home/vinvin/shKLSE/")
        # application/zip not /zip
        profile.set_preference('browser.helperApps.neverAsk.saveToDisk', 'application/zip')
    
        for retry in range(5):
            try:
                browser = webdriver.Firefox(profile)
                print "firefox"
                break
            except:
                time.sleep(3)
        time.sleep(1)
    
        browser.get("https://www.shareinvestor.com/my")
        time.sleep(10)
        login_main = browser.find_element_by_xpath("//*[@href='/user/login.html']").click()
        print browser.current_url
        username = browser.find_element_by_id("sic_login_header_username")
        password = browser.find_element_by_id("sic_login_header_password")
        print "find id done"
        username.send_keys("bkcollection")
        password.send_keys("123456")
        print "log in done"
        login_attempt = browser.find_element_by_xpath("//*[@type='submit']")
        login_attempt.submit()
        browser.get("https://www.shareinvestor.com/prices/price_download.html#/?type=price_download_all_stocks_bursa")
        print browser.current_url
        time.sleep(20)
        dl = browser.find_element_by_xpath("//*[@href='/prices/price_download_zip_file.zip?type=history_all&market=bursa']").click()
        time.sleep(30)
    
        browser.close()
        browser.quit()
        display.stop()
    
       zip_ref = zipfile.ZipFile('/home/vinvin/shKLSE/file.zip', 'r')
       zip_ref.extractall('/home/vinvin/shKLSE')
       zip_ref.close()
       # remove with correct path
       os.remove('/home/vinvin/shKLSE/file.zip')
    

    【讨论】:

    • 谢谢,您的代码可以正常工作,只需稍作调整
    【解决方案5】:

    把它放在硒的范围之外。更改首选项设置,以便在单击链接时(首先检查链接是否有效)它会给您一个要求保存的弹出窗口,现在使用 sikuli http://www.sikuli.org/ 单击弹出窗口。 哑剧类型并不总是有效,并且没有黑白答案为什么它不起作用。

    【讨论】:

    • 如前所述,没有弹出窗口。你试试可以手动输入用户名和密码。
    猜你喜欢
    • 2021-12-01
    • 2017-08-11
    • 2017-05-12
    • 2021-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-02
    • 2016-02-20
    相关资源
    最近更新 更多