使用 python/selenium 保存完整网页(包括 css、图像)
Save complete web page (incl css, images) using python/selenium
我正在使用 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)
那时我有一个页面,我可以手动单击 "save as," 并获取一个本地文件(具有相应的 image/js 资产文件夹),让我可以在本地查看整个返回的页面(减去通过向下滚动页面动态生成的内容,这很好)。我以为会有一种简单的方法可以在 python/selenium 中模仿这个 'save as' 函数,但还没有找到。下面保存页面的代码只是保存 html,并没有给我留下一个看起来像在网络浏览器中一样的本地文件,带有图像等
content = driver.page_source
with open('webpage.html', 'w') as f:
f.write(content)
我也找到了 this question/answer on SO,但被接受的答案只是显示了 'save as' 框,并没有提供点击它的方法(正如两位评论者指出的那样)
有使用python 'save [full page] as' 的简单方法吗?理想情况下,我更喜欢使用 selenium 的答案,因为 selenium 使爬行部分变得如此简单,但如果有更好的工具来完成这项工作,我愿意使用另一个库。或者也许我只需要在代码中指定所有我想下载的images/tables,并且没有模拟右键单击'save as'功能的快捷方式?
更新 - 跟进詹姆斯回答的问题
因此,我 运行 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 等)下载到它们相同的相对路径来复制 "save as full web page (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
的文件,里面有你想要的内容。
我建议你试试 sikulix,这是一个基于图像的自动化工具,用于在 PC OS 中操作任何小部件,它支持 python 语法和 运行 使用命令行,也许是解决您的问题的最简单方法。
您需要做的只是给它一个屏幕截图,在您的 python 自动化脚本中调用 sikulix 脚本(使用 OS.system("xxxx") 或子进程...)。
如您所述,Selenium 无法与浏览器的上下文菜单交互以使用 Save as...
,因此您可以使用外部自动化库,例如 pyautogui
。
pyautogui.hotkey('ctrl', 's')
time.sleep(1)
pyautogui.typewrite(SEQUENCE + '.html')
pyautogui.hotkey('enter')
此代码通过键盘快捷键 CTRL+S
打开 Save as...
window,然后按 Enter 将网页及其资源保存到默认下载位置。此代码还将文件命名为序列,以便为其提供唯一名称,但您可以根据您的用例更改此名称。如果需要,您还可以通过使用 Tab 键和箭头键进行一些额外操作来更改下载位置。
测试于 Ubuntu 18.10;根据您的 OS,您可能需要修改发送的组合键。
完整代码,其中我还添加了条件等待以提高速度:
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 下载 full/complete html(请参阅:https://github.com/markfront/SinglePageFullHtml)
更新 - 跟进 Max 的建议,以下是使用该工具的步骤:
- 克隆项目,然后 运行 maven 构建:
$> git clone https://github.com/markfront/SinglePageFullHtml.git
$> cd ~/git/SinglePageFullHtml
$> mvn clean compile package
在目标文件夹中找到生成的jar文件:SinglePageFullHtml-1.0-SNAPSHOT-jar-with-dependencies.jar
运行 命令行中的 jar,如:
$> java -jar .target/SinglePageFullHtml-1.0-SNAPSHOT-jar-with-dependencies.jar <page_url>
结果文件名将有一个前缀“FP,后跟页面的哈希码 url,文件扩展名为“.html”。它将被找到在任一文件夹“/tmp”中(您可以通过 System.getProperty("java.io.tmp") 获取。如果没有,请尝试在您的主目录或 System.getProperty("user.home") 在 Java).
结果文件将是一个巨大的 self-contained html 文件,其中包含所有内容(css、javascript、图像等)由原始 html 来源引用。
我正在使用 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)
那时我有一个页面,我可以手动单击 "save as," 并获取一个本地文件(具有相应的 image/js 资产文件夹),让我可以在本地查看整个返回的页面(减去通过向下滚动页面动态生成的内容,这很好)。我以为会有一种简单的方法可以在 python/selenium 中模仿这个 'save as' 函数,但还没有找到。下面保存页面的代码只是保存 html,并没有给我留下一个看起来像在网络浏览器中一样的本地文件,带有图像等
content = driver.page_source
with open('webpage.html', 'w') as f:
f.write(content)
我也找到了 this question/answer on SO,但被接受的答案只是显示了 'save as' 框,并没有提供点击它的方法(正如两位评论者指出的那样)
有使用python 'save [full page] as' 的简单方法吗?理想情况下,我更喜欢使用 selenium 的答案,因为 selenium 使爬行部分变得如此简单,但如果有更好的工具来完成这项工作,我愿意使用另一个库。或者也许我只需要在代码中指定所有我想下载的images/tables,并且没有模拟右键单击'save as'功能的快捷方式?
更新 - 跟进詹姆斯回答的问题
因此,我 运行 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 等)下载到它们相同的相对路径来复制 "save as full web page (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
的文件,里面有你想要的内容。
我建议你试试 sikulix,这是一个基于图像的自动化工具,用于在 PC OS 中操作任何小部件,它支持 python 语法和 运行 使用命令行,也许是解决您的问题的最简单方法。 您需要做的只是给它一个屏幕截图,在您的 python 自动化脚本中调用 sikulix 脚本(使用 OS.system("xxxx") 或子进程...)。
如您所述,Selenium 无法与浏览器的上下文菜单交互以使用 Save as...
,因此您可以使用外部自动化库,例如 pyautogui
。
pyautogui.hotkey('ctrl', 's')
time.sleep(1)
pyautogui.typewrite(SEQUENCE + '.html')
pyautogui.hotkey('enter')
此代码通过键盘快捷键 CTRL+S
打开 Save as...
window,然后按 Enter 将网页及其资源保存到默认下载位置。此代码还将文件命名为序列,以便为其提供唯一名称,但您可以根据您的用例更改此名称。如果需要,您还可以通过使用 Tab 键和箭头键进行一些额外操作来更改下载位置。
测试于 Ubuntu 18.10;根据您的 OS,您可能需要修改发送的组合键。
完整代码,其中我还添加了条件等待以提高速度:
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 下载 full/complete html(请参阅:https://github.com/markfront/SinglePageFullHtml)
更新 - 跟进 Max 的建议,以下是使用该工具的步骤:
- 克隆项目,然后 运行 maven 构建:
$> git clone https://github.com/markfront/SinglePageFullHtml.git
$> cd ~/git/SinglePageFullHtml
$> mvn clean compile package
在目标文件夹中找到生成的jar文件:SinglePageFullHtml-1.0-SNAPSHOT-jar-with-dependencies.jar
运行 命令行中的 jar,如:
$> java -jar .target/SinglePageFullHtml-1.0-SNAPSHOT-jar-with-dependencies.jar <page_url>
结果文件名将有一个前缀“FP,后跟页面的哈希码 url,文件扩展名为“.html”。它将被找到在任一文件夹“/tmp”中(您可以通过 System.getProperty("java.io.tmp") 获取。如果没有,请尝试在您的主目录或 System.getProperty("user.home") 在 Java).
结果文件将是一个巨大的 self-contained html 文件,其中包含所有内容(css、javascript、图像等)由原始 html 来源引用。