使用scrapy爬行时如何跳过某些文件类型?
Posted
技术标签:
【中文标题】使用scrapy爬行时如何跳过某些文件类型?【英文标题】:how to skip some file type while crawling with scrapy? 【发布时间】:2012-08-21 20:25:54 【问题描述】:我想在使用scrapy爬行时跳过一些文件类型链接.exe .zip .pdf,但不想使用带有特定url常规的规则。怎么样?
更新:
因此,当正文尚未下载时,很难决定是否仅通过 Content-Type 响应此链接。我更改为在下载器中间件中删除 url。谢谢彼得和利奥。
【问题讨论】:
【参考方案1】:我构建了这个中间件来排除任何不在正则表达式白名单中的响应类型:
from scrapy.http.response.html import HtmlResponse
from scrapy.exceptions import IgnoreRequest
from scrapy import log
import re
class FilterResponses(object):
"""Limit the HTTP response types that Scrapy dowloads."""
@staticmethod
def is_valid_response(type_whitelist, content_type_header):
for type_regex in type_whitelist:
if re.search(type_regex, content_type_header):
return True
return False
def process_response(self, request, response, spider):
"""
Only allow HTTP response types that that match the given list of
filtering regexs
"""
# to specify on a per-spider basis
# type_whitelist = getattr(spider, "response_type_whitelist", None)
type_whitelist = (r'text', )
content_type_header = response.headers.get('content-type', None)
if not content_type_header or not type_whitelist:
return response
if self.is_valid_response(type_whitelist, content_type_header):
return response
else:
msg = "Ignoring request , content-type was not in whitelist".format(response.url)
log.msg(msg, level=log.INFO)
raise IgnoreRequest()
要使用它,请将其添加到 settings.py:
DOWNLOADER_MIDDLEWARES =
'[project_name].middlewares.FilterResponses': 999,
【讨论】:
【参考方案2】:如果你转到 Scrapy 根目录中的 linkextractor.py,你会看到以下内容:
"""
Common code and definitions used by Link extractors (located in
scrapy.contrib.linkextractor).
"""
# common file extensions that are not followed if they occur in links
IGNORED_EXTENSIONS = [
# images
'mng', 'pct', 'bmp', 'gif', 'jpg', 'jpeg', 'png', 'pst', 'psp', 'tif',
'tiff', 'ai', 'drw', 'dxf', 'eps', 'ps', 'svg',
# audio
'mp3', 'wma', 'ogg', 'wav', 'ra', 'aac', 'mid', 'au', 'aiff',
# video
'3gp', 'asf', 'asx', 'avi', 'mov', 'mp4', 'mpg', 'qt', 'rm', 'swf', 'wmv',
'm4a',
# other
'css', 'pdf', 'doc', 'exe', 'bin', 'rss', 'zip', 'rar',
]
但是,由于这适用于链接提取器(并且您不想使用规则),我不确定这是否会解决您的问题(我刚刚意识到您指定您不想使用规则。我以为你问过如何更改文件扩展名限制而不需要直接在规则中指定)。
好消息是,您还可以构建自己的下载器中间件,并将任何/所有请求丢弃到具有不良扩展名的 url。见Downloader Middlerware
可以通过访问request
对象的url属性获取请求的url,如下:request.url
基本上,在字符串末尾搜索“.exe”或您要删除的任何扩展名,如果它包含上述扩展名,则返回IgnoreRequest
异常,请求将立即被删除。
更新
为了在下载请求之前对其进行处理,您需要确保在自定义下载器中间件中定义“process_request”方法。
根据 Scrapy 文档
process_request
为每个通过下载的请求调用此方法 中间件。
process_request() 应该返回 None、Response 对象或 请求对象。
如果返回 None,Scrapy 会继续处理这个请求, 执行所有其他中间件,直到最后,适当的 下载器处理程序被称为执行的请求(及其响应 下载)。
如果它返回一个 Response 对象,Scrapy 不会打扰调用任何其他对象 请求或异常中间件,或适当的下载 功能;它会返回那个响应。响应中间件总是 调用每个响应。
如果它返回一个Request对象,返回的请求将是 重新安排(在调度程序中)以供将来下载。这 将始终调用原始请求的回调。如果新 请求有一个回调,它将与响应一起调用 下载,然后该回调的输出将传递给 原始回调。如果新请求没有回调,则 下载的响应将仅传递给原始请求 回调。
如果它返回一个 IgnoreRequest 异常,整个请求将被 完全删除并且它的回调从未调用过。
所以本质上,只需创建一个下载器类,添加一个方法类process_request,它以请求对象和蜘蛛对象为参数。如果 url 包含不需要的扩展名,则返回 IgnoreRequest 异常。
这一切都应该在页面被下载之前发生。但是,如果您想改为处理响应标头,则必须向网页发出请求。
您始终可以在下载器中同时实现 process_request 和 process_response 方法,其想法是立即删除明显的扩展名,并且如果由于某种原因 url 不包含文件扩展名,则请求将被处理并在 process_request 方法中捕获(因为您可以在标头中进行验证)?
【讨论】:
谢谢。我制作了一个下载器中间件,并查看响应中的 headers['Content-Type'] 是否以应用程序开头。但问题是在下载整个文件后调用此函数。它浪费了很多。是否可以在响应主体传输之前进行挂钩? 是的。我在上面添加了如何操作的描述:P 我正在使用规则 -Rule(LinkExtractor(unique=True), callback='parse', follow=True, process_request='process_request')
。在上述规则中,如何在不为deny=()
明确提及文件扩展名的情况下一次跳过所有具有linkextractor
中定义的文件扩展名的url?
@OmPrakash 您可以通过 from scrapy.linkextractors import IGNORED_EXTENSIONS
获取忽略的扩展名列表,然后将 if req.url.lower().endswith(tuple(IGNORED_EXTENSIONS)): raise scrapy.exceptions.IgnoreRequest
添加到您的 process_request
函数中。【参考方案3】:
.zip 和 .pdf 是ignored by scrapy by default。
作为一般规则,您可以将规则配置为仅包含与您的正则表达式匹配的网址(在本例中为 .htm*):
rules = (Rule(SgmlLinkExtractor(allow=('\.htm')), callback='parse_page', follow=True, ), )
或排除匹配正则表达式的那些:
rules = (Rule(SgmlLinkExtractor(allow=('.*'), deny=('\.pdf', '\.zip')), callback='parse_page', follow=True, ), )
阅读the documentation了解更多信息。
【讨论】:
以上是关于使用scrapy爬行时如何跳过某些文件类型?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用scrapy规则从Wiki演员和电影页面爬行到仅演员和fimlography链接中的链接
使用 WinInet 的方法迭代缓存文件时,如何跳过大文件?