【发布时间】:2016-08-06 05:41:17
【问题描述】:
我使用 Scrapy 抓取网页并下载一些文件。由于我得到的 file_url 将重定向到另一个 url(302 重定向)。所以我使用另一种方法 handle_redirect 来获取重定向的 url。我像这样自定义文件管道。
class MyFilesPipeline(FilesPipeline):
def handle_redirect(self, file_url):
response = requests.head(file_url)
if response.status_code == 302:
file_url = response.headers["Location"]
return file_url
def get_media_requests(self, item, info):
redirect_url = self.handle_redirect(item["file_urls"][0])
yield scrapy.Request(redirect_url)
def item_completed(self, results, item, info):
file_paths = [x['path'] for ok, x in results if ok]
if not file_paths:
raise DropItem("Item contains no images")
item['file_urls'] = file_paths
return item
使用上面的代码,我可以下载文件,但是下载时进程被阻塞,所以整个项目变得很慢。
我在蜘蛛中尝试了另一种解决方案,首先使用请求获取重定向 url 然后传递给另一个函数。并使用默认文件管道。
yield scrapy.Request(
download_url[0],
meta={
"name": name,
},
dont_filter=True,
callback=self.handle_redirect)
def handle_redirect(self, response):
logging.warning("respon %s" % response.meta)
download_url = response.headers["Location"].decode("utf-8")
return AppListItem(
name=response.meta["name"],
file_urls=[download_url],
)
仍然阻塞进程。
从这里的dos
当项目到达 FilesPipeline 时,file_urls 中的 URL 使用标准的 Scrapy 调度程序安排下载字段 和下载器(这意味着调度器和下载器中间件 被重用),但具有更高的优先级,在其他之前处理它们 页面被刮掉。该项目仍然“锁定”在那个特定的 管道阶段,直到文件完成下载(或失败 某种原因)
这是否意味着在文件下载之前我无法抓取下一个网址? (我的设置中没有设置download_delay)
编辑
我一开始已经添加了这个:
handle_httpstatus_list = [302]
所以我不会被重定向到重定向的 url,我的第一个解决方案使用 requests 是因为我认为 yield 会像这样工作:
- 我爬了一个页面,保持yield回调,然后调用return item
- 项目传递到管道,如果遇到一些 i/o,它将让蜘蛛像正常的异步 i/o 一样抓取下一页。
或者我必须等待下载的文件才能抓取下一页?这是 Scrapy 的缺点吗?我不关注的第二部分是如何计算爬取页面的速度。例如,3s 为一个完整的页面,默认并发为 16。我猜@neverlastn 使用 16/2/3 获取 2.5 个页面/ s.并发16不是意味着我可以同时处理16个请求吗?那么速度应该是16页/秒?如有错误请指正。
编辑2
谢谢你的回答,我现在明白怎么计算了,但是我还是不明白第二部分。在302上我第一次遇到这个问题。 Error 302 Downloading File in Scrapy 我有一个类似的网址
http://example.com/first
将使用 302 并重定向到
http://example.com/second
但 Scrapy 不会自动重定向到第二个,并且无法下载已连接的文件。从这里的代码Scrapy-redirect 和这里的dos RedirectMiddleware 指出scrapy 应该默认处理重定向。这就是为什么我做了一些技巧并尝试修复它。我的第三个解决方案将尝试像这样使用Celery
class MyFilesPipeline(FilesPipeline):
@app.task
def handle_redirect(self, file_url):
response = requests.head(file_url)
if response.status_code == 302:
file_url = response.headers["Location"]
return file_url
def get_media_requests(self, item, info):
redirect_url = self.handle_redirect.delay(item["file_urls"][0])
yield scrapy.Request(redirect_url)
def item_completed(self, results, item, info):
file_paths = [x['path'] for ok, x in results if ok]
if not file_paths:
raise DropItem("Item contains no images")
item['file_urls'] = file_paths
return item
由于我已经有很多蜘蛛,我不想使用第二种解决方案覆盖它们。所以我在管道中处理它们,这个解决方案会更好吗?
【问题讨论】: