我正在写一个脚本,从各个节目主持人那里下载整个BBC播客集。我的脚本使用BS4、机械化和wget。
我想知道如何测试URL请求是否会从服务器产生"404"的响应代码。我写了以下函数:
def getResponseCode(br, url):
print("Opening: " + url)
try:
response = br.open(url)
print("Response code: " + str(response.code))
return True
except (mechanize.HTTPError, mechanize.URLError) as e:
if isinstance(e,mechanize.HTTPError):
print("Mechanize error: " + str(e.code))
else:
print("Mechanize error: " + str(e.reason.args))
return False
我将我的Browser()
对象和一个URL字符串传递给它。它返回True
或False
,这取决于响应是"404"还是"200"(实际上,如果它不是"200",则机械化抛出和异常,因此进行异常处理)。
在main()
中,我基本上是在这个函数上循环传递我用BS4抓取的URL列表中的一些URL。当函数返回True
时,我继续下载带有wget
的MP3。
但是。我的问题是:
- URL是指向遥控器上播客MP3文件的直接路径服务器和我注意到,当URL可用时,
br.open(<URL>)
将挂起。我怀疑这是因为机械化从服务器缓存/下载实际数据。我不想这是因为如果响应代码为"200"。我怎么能不缓存/DL,只测试响应代码呢
我试过使用br.open_novisit(url, data=None)
,但挂起的问题仍然存在。。。
我认为没有什么好方法可以让机械化做你想做的事。机械化的全部意义在于,它试图模拟浏览器访问URL,而浏览器访问URL会下载页面。如果你不想这样做,就不要使用为之设计的API。
除此之外,无论您使用什么API,通过发送对URL的GET
请求,您就要求服务器向您发送整个响应。为什么这么做只是为了尽快挂断电话?使用HEAD
请求询问服务器是否可用。(有时服务器即使在应该的时候也不会HEAD
,所以你必须回到GET
。但如果你遇到它,就要跨过这座桥。)
例如:
req = urllib.request.Request(url, method='HEAD')
resp = urllib.request.urlopen(req)
return 200 <= resp.code < 300
但这引发了一个问题:
当函数返回True时,我继续使用wget下载MP3。
为什么?为什么不首先使用wget
呢?如果URL是可获取的,它将获取URL;否则,它会给你一个错误——就像机械化一样容易。这样就避免了对每个URL进行两次点击。
既然如此,为什么要尝试为wget
编写脚本,而不是使用stdlib中的内置支持或像requests
这样的第三方模块呢?
如果你只是在寻找一种并行化的方法,那么在Python中很容易做到:
def is_good_url(url):
req = urllib.request.Request(url, method='HEAD')
resp = urllib.request.urlopen(req)
return url, 200 <= resp.code < 300
with futures.ThreadPoolExecutor(max_workers=8) as executor:
fs = [executor.submit(is_good_url, url) for url in urls]
results = (f.result() for f in futures.as_completed(fs))
good_urls = [url for (url, good) in results if good]
要将其更改为实际下载有效的URL,而不仅仅是记下哪些URL是有效的,只需将任务函数更改为从GET
获取和保存数据,而不是执行HEAD
操作。文档中的ThreadPoolExecutor
示例几乎完全符合您的要求。