使用python/selenium保存完整的网页(包括css、图像)

使用python/selenium保存完整的网页(包括css、图像),python,selenium,web-scraping,web-crawler,bioinformatics,Python,Selenium,Web Scraping,Web Crawler,Bioinformatics,我正在使用Python/Selenium将基因序列提交到在线数据库,并希望保存我得到的结果的完整页面。下面是使我获得所需结果的代码: from selenium import webdriver URL = 'https://blast.ncbi.nlm.nih.gov/Blast.cgi?PROGRAM=blastx&PAGE_TYPE=BlastSearch&LINK_LOC=blasthome' SEQUENCE = 'CCTAAACTATAGAAGGACAGCTCAAA

我正在使用Python/Selenium将基因序列提交到在线数据库,并希望保存我得到的结果的完整页面。下面是使我获得所需结果的代码:

from selenium import webdriver

URL = 'https://blast.ncbi.nlm.nih.gov/Blast.cgi?PROGRAM=blastx&PAGE_TYPE=BlastSearch&LINK_LOC=blasthome'
SEQUENCE = 'CCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACA' #'GAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGA'
CHROME_WEBDRIVER_LOCATION = '/home/max/Downloads/chromedriver' # update this for your machine

# open page with selenium
# (first need to download Chrome webdriver, or a firefox webdriver, etc)
driver = webdriver.Chrome(executable_path=CHROME_WEBDRIVER_LOCATION)
driver.get(URL)
time.sleep(5)

# enter sequence into the query field and hit 'blast' button to search
seq_query_field = driver.find_element_by_id("seq")
seq_query_field.send_keys(SEQUENCE)

blast_button = driver.find_element_by_id("b1")
blast_button.click()
time.sleep(60)
此时,我有一个页面,可以手动单击“另存为”,并获得一个本地文件(带有相应的image/js资产文件夹),该文件允许我在本地查看整个返回页面(减去向下滚动页面动态生成的内容,这很好)。我假设在python/selenium中有一种简单的方法来模拟这个“另存为”函数,但还没有找到。下面保存页面的代码只保存html,不会给我留下像在web浏览器中一样的本地文件、图像等

content = driver.page_source
with open('webpage.html', 'w') as f:
    f.write(content)
我也发现了,但被接受的答案只是显示了“另存为”框,并没有提供单击它的方式(正如两位评论者所指出的)

有没有一种简单的方法可以使用python将[整页]保存为?理想情况下,我更喜欢使用selenium,因为selenium使爬行部分变得非常简单,但如果有更好的工具用于此工作,我愿意使用另一个库。或者我只需要在代码中指定要下载的所有图像/表,而没有快捷方式来模拟右键单击的“另存为”功能

更新-詹姆斯回答的后续问题 因此,我运行James的代码生成一个
page.html
(和相关文件),并将其与手动单击“另存为”得到的html文件进行比较。通过James的脚本保存的
page.html
非常棒,包含了我所需要的一切,但是当在浏览器中打开时,它还显示了许多隐藏在手动保存的页面中的额外格式文本。请参阅附加的屏幕截图(左侧为手动保存的页面,右侧为带有额外格式文本的脚本保存页面)。

这对我来说尤其令人惊讶,因为James脚本保存的页面的原始html似乎表明这些字段仍然应该被隐藏。例如,请参见下面的html,这两个文件中显示的内容相同,但问题文本仅出现在由James脚本保存的页面上的浏览器呈现页面中:

<p class="helpbox ui-ncbitoggler-slave ui-ncbitoggler" id="hlp1" aria-hidden="true">
These options control formatting of alignments in results pages. The
default is HTML, but other formats (including plain text) are available.
PSSM and PssmWithParameters are representations of Position Specific Scoring Matrices and are only available for PSI-BLAST. 
The Advanced view option allows the database descriptions to be sorted by various indices in a table.
</p>


知道为什么会发生这种情况吗?

这不是一个完美的解决方案,但它会让你得到你所需要的大部分。通过解析html并将任何加载的文件(图像、css、js等)下载到它们相同的相对路径,可以复制“另存为完整网页(complete)”的行为

由于跨源请求阻塞,大多数javascript无法工作。但是内容看起来(大部分)是一样的

这使用
requests
保存加载的文件,
lxml
解析html,并使用
os
进行路径腿部工作

from selenium import webdriver
import chromedriver_binary
from lxml import html
import requests
import os

driver = webdriver.Chrome()
URL = 'https://blast.ncbi.nlm.nih.gov/Blast.cgi?PROGRAM=blastx&PAGE_TYPE=BlastSearch&LINK_LOC=blasthome'
SEQUENCE = 'CCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACA' 
base = 'https://blast.ncbi.nlm.nih.gov/'

driver.get(URL)
seq_query_field = driver.find_element_by_id("seq")
seq_query_field.send_keys(SEQUENCE)
blast_button = driver.find_element_by_id("b1")
blast_button.click()

content = driver.page_source
# write the page content
os.mkdir('page')
with open('page/page.html', 'w') as fp:
    fp.write(content)

# download the referenced files to the same path as in the html
sess = requests.Session()
sess.get(base)            # sets cookies

# parse html
h = html.fromstring(content)
# get css/js files loaded in the head
for hr in h.xpath('head//@href'):
    if not hr.startswith('http'):
        local_path = 'page/' + hr
        hr = base + hr
    res = sess.get(hr)
    if not os.path.exists(os.path.dirname(local_path)):
        os.makedirs(os.path.dirname(local_path))
    with open(local_path, 'wb') as fp:
        fp.write(res.content)

# get image/js files from the body.  skip anything loaded from outside sources
for src in h.xpath('//@src'):
    if not src or src.startswith('http'):
        continue
    local_path = 'page/' + src
    print(local_path)
    src = base + src
    res = sess.get(hr)
    if not os.path.exists(os.path.dirname(local_path)):
        os.makedirs(os.path.dirname(local_path))
    with open(local_path, 'wb') as fp:
        fp.write(res.content)  

你应该有一个名为
page
的文件夹,里面有一个名为
page.html
的文件,里面有你想要的内容。

我建议你尝试一下,这是一个基于图像的自动化工具,用于操作PC操作系统中的任何小部件,它支持python语法并使用命令行运行,可能是解决你问题的最简单方法。
你所需要做的只是给它一个屏幕截图,在你的python自动化脚本中调用sikulix脚本(带有OS.system(“xxxx”)或子进程…。

正如你所说的,Selenium不能与浏览器的上下文菜单交互以使用
另存为…
,因此,你可以使用一个外部自动化库,如

此代码通过其键盘快捷键
CTRL+S
打开
另存为…
窗口,然后按enter键将网页及其资源保存到默认下载位置。这段代码还将文件命名为序列,以便给它一个唯一的名称,尽管您可以根据您的用例更改此名称。如果需要,您还可以通过使用tab键和箭头键进行一些额外的工作来更改下载位置

在Ubuntu 18.10上测试;根据您的操作系统,您可能需要修改发送的密钥组合


完整代码,其中我还添加了条件等待以提高速度:

import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.expected_conditions import visibility_of_element_located
from selenium.webdriver.support.ui import WebDriverWait
import pyautogui

URL = 'https://blast.ncbi.nlm.nih.gov/Blast.cgi?PROGRAM=blastx&PAGE_TYPE=BlastSearch&LINK_LOC=blasthome'
SEQUENCE = 'CCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACA' #'GAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGA'

# open page with selenium
# (first need to download Chrome webdriver, or a firefox webdriver, etc)
driver = webdriver.Chrome()
driver.get(URL)

# enter sequence into the query field and hit 'blast' button to search
seq_query_field = driver.find_element_by_id("seq")
seq_query_field.send_keys(SEQUENCE)

blast_button = driver.find_element_by_id("b1")
blast_button.click()

# wait until results are loaded
WebDriverWait(driver, 60).until(visibility_of_element_located((By.ID, 'grView')))

# open 'Save as...' to save html and assets
pyautogui.hotkey('ctrl', 's')
time.sleep(1)
pyautogui.typewrite(SEQUENCE + '.html')
pyautogui.hotkey('enter')

受FThompson上述答案的启发,我想出了以下工具,可以下载给定页面url的完整html(请参阅:)

更新-跟进Max的建议,以下是使用该工具的步骤:

  • 克隆项目,然后运行maven生成:
  • 在目标文件夹中找到生成的jar文件:SinglePageFullHtml-1.0-SNAPSHOT-jar-with-dependencies.jar

  • 在命令行中运行jar,如下所示:

  • $>java-jar.target/SinglePageFullHtml-1.0-SNAPSHOT-jar-with-dependencies.jar
    
  • 结果文件名将有一个前缀“FP,后跟页面url的hashcode,文件扩展名为“.html”。它将在任何文件夹“/tmp”中找到(您可以通过System.getProperty(“java.io.tmp”)。如果没有,请尝试在java中的home dir或System.getProperty(“user.home”)中找到它)

  • 结果文件将是一个大而胖的自包含html文件,其中包含原始html源引用的所有内容(css、javascript、图像等)


  • 检查这个问题谢谢这个Moshe,尽管从描述来看它保存了“页面的html内容(没有CSS),而且它查找页面上的所有图像并保存它们“这不是我想要的。另外,对我来说更重要的是,我想利用像Selenium或者Scrapy这样的爬行工具,因为我需要做一些爬行来访问我的结果页面,我不能只给出一个URL
    import time
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.expected_conditions import visibility_of_element_located
    from selenium.webdriver.support.ui import WebDriverWait
    import pyautogui
    
    URL = 'https://blast.ncbi.nlm.nih.gov/Blast.cgi?PROGRAM=blastx&PAGE_TYPE=BlastSearch&LINK_LOC=blasthome'
    SEQUENCE = 'CCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACAGCTCAAACACAAAGTTACCTAAACTATAGAAGGACA' #'GAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGAGAAGA'
    
    # open page with selenium
    # (first need to download Chrome webdriver, or a firefox webdriver, etc)
    driver = webdriver.Chrome()
    driver.get(URL)
    
    # enter sequence into the query field and hit 'blast' button to search
    seq_query_field = driver.find_element_by_id("seq")
    seq_query_field.send_keys(SEQUENCE)
    
    blast_button = driver.find_element_by_id("b1")
    blast_button.click()
    
    # wait until results are loaded
    WebDriverWait(driver, 60).until(visibility_of_element_located((By.ID, 'grView')))
    
    # open 'Save as...' to save html and assets
    pyautogui.hotkey('ctrl', 's')
    time.sleep(1)
    pyautogui.typewrite(SEQUENCE + '.html')
    pyautogui.hotkey('enter')
    
    $> git clone https://github.com/markfront/SinglePageFullHtml.git
    
    $> cd ~/git/SinglePageFullHtml
    $> mvn clean compile package
    
    $> java -jar .target/SinglePageFullHtml-1.0-SNAPSHOT-jar-with-dependencies.jar <page_url>