Python - 如何在 https 上搜索驻留在 iframe 中的 zip 文件


  • Python - 2.7.5
  • 谷歌浏览器

首先,我是自学成才的程序员,将接受对我在下面发布的任何代码的任何批评和/或建议。 解决这个问题是一种乐趣,因为我喜欢挑战自己,但我担心我已经碰壁了,需要一些指导。 我将在下面尽可能详细地解释我的脚本的整体情况,然后通过标题中解释的实际问题展示我所处的位置。

我正在整理一个脚本,该脚本将自动输出并下载数据,升级并导出到GDB。 我们为广泛的用户提供服务,并拥有一个非常大的企业SDE设置,其中包含大量公共数据,我们必须为最终用户进行搜索和更新。 我们的大部分数据由地方政府实体每月更新,我们必须手动搜索数据,下载,解压缩,QAQC等。 我想把一个脚本放在一起,通过出去为我下载我的所有数据并导出到本地 GDB,自动化此过程的第一部分,从那里我可以 QAQC 所有内容并上传到我们的 SDE 供我们的用户访问。

到目前为止,这个过程非常简单,直到我谈到我面前的这个问题。 我的脚本将在网页中搜索特定关键字,并找到相关链接并开始下载。 在这篇文章中,我将使用两个示例,一个有效,另一个当前给我带来问题。 有效的是我用于搜索和下载Metro GIS数据集的功能,下面显示了我当前查找此数据集的过程。到目前为止,我包含的所有http网站都将使用下面发布的功能。 就像显示Metro一样,我计划为每组数据定义一个函数。

import requests, zipfile, StringIO, time, arcpy, urllib2, urlparse
from BeautifulSoup import BeautifulSoup
arcpy.env.overwriteOutput = True
workPath = -- #The output GDB
timestr = time.strftime("%Y%m%d")
gdbName = "GlobalSDEUpdate_" + timestr
gdbPath = workPath + "\" + gdbName + ".gdb"
class global_DataFinder(object):
def __init__(self):
object.__init__(self)
self.gdbSetup()
self.metro()
def gdbSetup(self):       
arcpy.CreateFileGDB_management(workPath, gdbName)
def fileDownload(self, key, url, dlPath, dsName):
page = urllib2.urlopen(url).read()
urlList = []
soup = BeautifulSoup(page)
soup.prettify()
for link in soup.findAll('a', href = True):
if not 'http://' in link['href']:
if urlparse.urljoin(url, link['href']) not in urlList:
zipDL = urlparse.urljoin(url, link['href'])
if zipDL.endswith(".zip"):
if key in zipDL:
urlList.append(zipDL)        
for x in urlList:
print x
r = requests.get(x, stream=True)
z = zipfile.ZipFile(StringIO.StringIO(r.content))        
z.extractall(dlPath)        
arcpy.CreateFeatureDataset_management(gdbPath, dsName)
arcpy.env.workspace = dlPath
shpList = []
for shp in arcpy.ListFeatureClasses():
shpList.append(shp)
arcpy.FeatureClassToGeodatabase_conversion(shpList, (gdbPath + "\" + dsName))
del shpList[:]
def metro(self):
key = "METRO_GIS_Data_Layers"
url = "http://www.ridemetro.org/Pages/NewsDownloads.aspx"
dlPath = -- *#Where my zipfiles output to*  
dsName = "Metro"
self.fileDownload(key, url, dlPath, dsName)
global_DataFinder()

正如您在上面看到的,这是我开始使用 Metro 作为我的第一个测试点的方法,目前效果很好。 我希望我以后的所有网站都喜欢这个,但当我到达 FEMA 时,我遇到了一个问题。

国家洪水灾害图层 (NFHL) 状态网站托管全国许多县的洪泛区数据,任何希望使用它的人都可以免费使用。 到达网站时,您将看到您可以搜索所需的县,然后表格查询搜索,然后您只需单击并下载所需的县。 在检查源代码时,这是我遇到的并在iframe中注意到它。

当通过Chrome访问iframe源链接并检查png源网址时,这就是你得到的 -https://hazards.fema.gov/femaportal/NFHL/searchResult

现在这就是我的问题所在,与 http 站点不同,我很快就了解到访问安全的 https 站点和抓取页面是不同的,尤其是当它使用 javascript 显示表格时。 我花了几个小时在论坛上搜索并尝试了不同的 python 包,如 selenium、mechanize、requests、urllib、urllib2,在我安全地建立连接并解析网页和搜索我的县 zipfile 之前,我似乎总是陷入死胡同。下面的代码显示了我得到的最接近的代码,并显示了我得到的错误代码。

(我总是在单独的脚本中进行测试,然后当它工作时,我会将其带到我的主脚本中,所以这就是为什么下面的代码片段与我的原始代码片段分开的原因)

import urllib2, httplib, socket, ssl
from BeautifulSoup import BeautifulSoup
url = "http://www.floodmaps.fema.gov/NFHL/status.shtml"
def test():  
page = urllib2.urlopen(url).read()
urlList = []
soup = BeautifulSoup(page)
soup.prettify()
for link in soup.findAll("iframe", src=True):
r = urllib2.urlopen(link['src'])
iFrame = link['src']
print iFrame
def connect_patched(self):
"Connect to a host on a given (SSL) port."
sock = socket.create_connection((self.host, self.port),
self.timeout, self.source_address)
if self._tunnel_host:
self.sock = sock
self._tunnel()
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
ssl_version=ssl.PROTOCOL_SSLv2)
httplib.HTTPSConnection.connect = connect_patched
test()

运行此测试时出现错误

网址库2.URLError: urlopen error [errno 6] _ssl.c:504: TLS/SSL 连接已关闭

我希望一个更有经验的编码人员能够看到我所做的工作,并告诉我我目前的方法是否是要走的路,如果是的话,如何克服这个最终错误并正确解析数据表。

使用@crmackey进行编辑

import requests
import os
import zipfile
from pyquery import PyQuery
from requests.packages.urllib3.exceptions import InsecureRequestWarning, InsecurePlatformWarning, SNIMissingWarning
import httplib
httplib.HTTPConnection._http_vsn = 10
httplib.HTTPConnection._http_vsn_str = 'HTTP/1.0'
# disable ssl warnings (we are not verifying SSL certificates at this time...future ehnancement?)
for warning in [SNIMissingWarning, InsecurePlatformWarning, InsecureRequestWarning]:
requests.packages.urllib3.disable_warnings(warning)
def download_zips(out_path):
url = 'http://www.floodmaps.fema.gov/NFHL/status.shtml'
download_prefix = 'https://hazards.fema.gov/femaportal/NFHL'
pq = PyQuery(requests.get(url, verify=False).content) #verify param important for SSL
src = pq.find('iframe').attr('src')
pq = PyQuery(requests.get(src, verify=False).content)
table = pq.find('table')
for a in table.find('a'):
href = a.attrib.get('href')
print href
url = '/'.join([download_prefix, href])
print url
r = requests.get(url, stream=True, verify=False)
out_zip = os.path.join(out_path, href.split('=')[-1])
with open(out_zip, 'wb') as f:
for chunk in r.iter_content(1024 *16): #grab 1KB at a time
if chunk:
f.write(chunk)
print 'downloaded zip: "{}"'.format(href.split('=')[-1])

out_path = r"C:UsersbarrDesktopTest"
download_zips(out_path)

我添加的只是httplib,并更改了顶部的HTTPConnection。 这允许我使用您的脚本连接到该站点。 现在这是当前的问题。 我的out_path中只有 1 个 zip 文件,而 zip 文件是空的。 我在调试窗口中检查了打印的源代码,它显示它试图从表中下载维尔京岛领土 zip 文件,所以它看起来像是正在尝试但未下载任何内容。输出一个空的 zip 文件后,脚本完成并且不会再显示错误消息。 我暂时删除了解压缩文件的行,因为它们返回错误,因为文件夹为空。

我能够使用请求模块下载zip文件,并且还选择使用PyQuery而不是Beautiful Soup。 我认为您面临的问题与SSL证书验证有关,如果将verify参数设置为False,则requests模块将允许您跳过检查证书。

下面的函数将下载所有 zip 文件并将其解压缩,从那里,您可以将 shapefile 导入地理数据库:

import requests
import os
import zipfile
from pyquery import PyQuery
from requests.packages.urllib3.exceptions import InsecureRequestWarning, InsecurePlatformWarning, SNIMissingWarning
# disable ssl warnings (we are not verifying SSL certificates at this time...future ehnancement?)
for warning in [SNIMissingWarning, InsecurePlatformWarning, InsecureRequestWarning]:
requests.packages.urllib3.disable_warnings(warning)
def download_zips(out_path):
url = 'http://www.floodmaps.fema.gov/NFHL/status.shtml'
download_prefix = 'https://hazards.fema.gov/femaportal/NFHL'
pq = PyQuery(requests.get(url, verify=False).content) #verify param important for SSL
src = pq.find('iframe').attr('src')
pq = PyQuery(requests.get(src, verify=False).content)
table = pq.find('table')
for a in table.find('a'):
href = a.attrib.get('href')
url = '/'.join([download_prefix, href])
r = requests.get(url, stream=True, verify=False)
out_zip = os.path.join(out_path, href.split('=')[-1])
with open(out_zip, 'wb') as f:
for chunk in r.iter_content(1024 *16): #grab 1KB at a time
if chunk:
f.write(chunk)
print 'downloaded zip: "{}"'.format(href.split('=')[-1])
# do more stuff like unzip?
unzipped = out_zip.split('.zip')[0]
with zipfile.Zipfile(out_zip, 'r') as f:
f.extractall(unzipped)

最新更新