常见问题¶
Scrapy 与 BeautifulSoup 或 lxml 相比如何?¶
BeautifulSoup 和 lxml 是用于解析 HTML 和 XML 的库。Scrapy 是一个用于编写网络爬虫的应用程序框架,这些爬虫可以爬取网站并从中提取数据。
Scrapy 提供了一种内置的数据提取机制(称为 选择器),但如果使用 BeautifulSoup(或 lxml)更方便,则可以轻松地使用它们。毕竟,它们只是解析库,可以从任何 Python 代码中导入和使用。
换句话说,将 BeautifulSoup(或 lxml)与 Scrapy 进行比较,就像将 jinja2 与 Django 进行比较。
我可以在 Scrapy 中使用 BeautifulSoup 吗?¶
是的,可以。如 上文所述,可以在 Scrapy 回调函数中使用 BeautifulSoup 解析 HTML 响应。您只需将响应体馈送到 BeautifulSoup
对象中,并从中提取所需的数据。
这是一个使用 BeautifulSoup API 和 lxml
作为 HTML 解析器的爬虫示例
from bs4 import BeautifulSoup
import scrapy
class ExampleSpider(scrapy.Spider):
name = "example"
allowed_domains = ["example.com"]
start_urls = ("http://www.example.com/",)
def parse(self, response):
# use lxml to get decent HTML parsing speed
soup = BeautifulSoup(response.text, "lxml")
yield {"url": response.url, "title": soup.h1.string}
注意
BeautifulSoup
支持多种 HTML/XML 解析器。请参阅 BeautifulSoup 的官方文档 以了解可用的解析器。
Scrapy 是否“窃取”了 Django 的 X?¶
可能吧,但我们不喜欢这个词。我们认为 Django 是一个很棒的开源项目,也是一个值得学习的典范,因此我们借鉴了 Django 的一些灵感来开发 Scrapy。
我们认为,如果某些事情已经做得很好,就没有必要重新发明轮子。这一理念不仅是开源和自由软件的基础之一,也适用于软件、文档、流程、政策等方面。因此,与其自己解决每个问题,我们选择从已经很好地解决了这些问题的项目中借鉴思路,并将精力集中在我们需要解决的真正问题上。
如果 Scrapy 能成为其他项目的灵感来源,我们将感到自豪。请随意从我们这里“窃取”!
Scrapy 是否支持 HTTP 代理?¶
是的。HTTP 代理支持(从 Scrapy 0.8 开始)通过 HTTP 代理下载器中间件提供。请参阅 HttpProxyMiddleware
。
如何抓取一个属性分布在不同页面的项目?¶
请参阅 将其他数据传递给回调函数。
如何在爬虫中模拟用户登录?¶
Scrapy 是以广度优先还是深度优先的方式进行爬取?¶
默认情况下,Scrapy 使用 后进先出 (LIFO) 队列来存储待处理的请求,这基本上意味着它以 深度优先 (DFO) 顺序进行爬取。在大多数情况下,这种顺序更为方便。
如果您确实希望以真正的 广度优先 (BFO) 顺序进行爬取,可以通过设置以下设置来实现
DEPTH_PRIORITY = 1
SCHEDULER_DISK_QUEUE = "scrapy.squeues.PickleFifoDiskQueue"
SCHEDULER_MEMORY_QUEUE = "scrapy.squeues.FifoMemoryQueue"
当待处理请求的数量低于 CONCURRENT_REQUESTS
、CONCURRENT_REQUESTS_PER_DOMAIN
或 CONCURRENT_REQUESTS_PER_IP
的配置值时,这些请求会并发发送。因此,爬取的最初几个请求很少遵循所需的顺序。将这些设置降低到 1
可以强制执行所需的顺序,但会大大降低爬取速度。
我的 Scrapy 爬虫存在内存泄漏。我该怎么办?¶
请参阅 调试内存泄漏。
此外,Python 本身存在一个内存泄漏问题,这在 无泄漏的泄漏 中有描述。
如何使 Scrapy 占用更少的内存?¶
请参阅上一个问题。
如何避免由于大量允许的域名导致的内存错误?¶
如果您的爬虫具有很长的 allowed_domains
列表(例如 50,000 个以上),请考虑使用需要更少内存的 自定义下载器中间件 来替换默认的 OffsiteMiddleware
下载器中间件。例如
如果您的域名足够相似,请使用您自己的正则表达式,而不是将
allowed_domains
中的字符串连接成一个复杂的正则表达式。如果您满足安装要求,请使用 pyre2 而不是 Python 的 re 来编译您的 URL 过滤正则表达式。请参阅 问题 1908。
另请参阅 StackOverflow 上的其他建议。
注意
启用自定义实现时,请记住禁用 scrapy.downloadermiddlewares.offsite.OffsiteMiddleware
DOWNLOADER_MIDDLEWARES = {
"scrapy.downloadermiddlewares.offsite.OffsiteMiddleware": None,
"myproject.middlewares.CustomOffsiteMiddleware": 50,
}
我可以在爬虫中使用基本 HTTP 认证吗?¶
是的,参见 HttpAuthMiddleware
。
为什么 Scrapy 下载的页面是英文而不是我的母语?¶
尝试通过覆盖 DEFAULT_REQUEST_HEADERS
设置来更改默认的 Accept-Language 请求头。
在哪里可以找到一些 Scrapy 项目示例?¶
参见 示例。
是否可以在不创建项目的情况下运行爬虫?¶
可以。您可以使用 runspider
命令。例如,如果您在 my_spider.py
文件中编写了一个爬虫,您可以使用以下命令运行它:
scrapy runspider my_spider.py
有关更多信息,请参见 runspider
命令。
我收到“已过滤的站外请求”消息。如何修复它们?¶
这些消息(以 DEBUG
级别记录)不一定表示存在问题,因此您可能不需要修复它们。
这些消息是由 OffsiteMiddleware
引发的,它是一个下载器中间件(默认启用),其目的是过滤掉对蜘蛛未覆盖的域的请求。
在生产环境中部署 Scrapy 爬虫的推荐方法是什么?¶
参见 部署爬虫。
我可以使用 JSON 进行大型导出吗?¶
这取决于您的输出有多大。请参阅 此警告 中 JsonItemExporter
文档。
我可以在信号处理程序中返回(Twisted)延迟吗?¶
某些信号支持从其处理程序返回延迟,而另一些则不支持。请参阅 内置信号参考 以了解哪些信号支持。
响应状态码 999 代表什么?¶
999 是 Yahoo 网站用来限制请求的自定义响应状态码。尝试通过在您的爬虫中使用 2
(或更高)的下载延迟来降低爬取速度。
from scrapy.spiders import CrawlSpider
class MySpider(CrawlSpider):
name = "myspider"
download_delay = 2
# [ ... rest of the spider code ... ]
或者通过使用 DOWNLOAD_DELAY
设置在您的项目中设置全局下载延迟。
我可以在我的爬虫中调用 pdb.set_trace()
来调试它们吗?¶
可以,但是您也可以使用 Scrapy shell,它允许您快速分析(甚至修改)爬虫正在处理的响应,这通常比普通的 pdb.set_trace()
更有用。
有关更多信息,请参见 从爬虫调用 shell 以检查响应。
将所有抓取的项目转储到 JSON/CSV/XML 文件中最简单的方法是什么?¶
要转储到 JSON 文件:
scrapy crawl myspider -O items.json
要转储到 CSV 文件:
scrapy crawl myspider -O items.csv
要转储到 XML 文件:
scrapy crawl myspider -O items.xml
有关更多信息,请参见 Feed 导出
某些表单中使用的这个巨大的神秘 __VIEWSTATE
参数是什么?¶
__VIEWSTATE
参数用于使用 ASP.NET/VB.NET 构建的网站。有关其工作原理的更多信息,请参见 此页面。此外,这里有一个 示例爬虫,它抓取了其中一个网站。
解析大型 XML/CSV 数据 Feed 的最佳方法是什么?¶
使用 XPath 选择器解析大型 Feed 可能存在问题,因为它们需要在内存中构建整个 Feed 的 DOM,这可能非常缓慢并消耗大量内存。
为了避免一次在内存中解析整个 Feed,您可以使用 xmliter_lxml()
和 csviter()
函数。实际上,这就是 XMLFeedSpider
使用的方法。
- scrapy.utils.iterators.xmliter_lxml(obj: Response | str | bytes, nodename: str, namespace: str | None = None, prefix: str = 'x') Iterator[Selector] [source]¶
- scrapy.utils.iterators.csviter(obj: Response | str | bytes, delimiter: str | None = None, headers: list[str] | None = None, encoding: str | None = None, quotechar: str | None = None) Iterator[dict[str, str]] [source]¶
返回给定 csv 对象的字典迭代器。
obj 可以是:- Response 对象 - unicode 字符串 - 以 utf-8 编码的字符串
delimiter 是用于分隔给定 obj 上字段的字符。
headers 是一个可迭代对象,当提供时,它提供返回字典的键,如果没有,则使用第一行。
quotechar 是用于在给定 obj 上封装字段的字符。
如何指示蜘蛛停止自身?¶
从回调中引发 CloseSpider
异常。更多信息请参见:CloseSpider
。
如何防止我的 Scrapy 机器人被封禁?¶
参见 避免被封禁。
我应该使用蜘蛛参数还是设置来配置我的蜘蛛?¶
两者 蜘蛛参数 和 设置 都可以用来配置你的蜘蛛。没有严格的规则要求使用其中一个或另一个,但是设置更适合于设置后不会发生太大变化的参数,而蜘蛛参数则更适合于更频繁更改的参数,甚至在每次蜘蛛运行时都会更改,有时甚至需要蜘蛛才能运行(例如,设置蜘蛛的起始 URL)。
举例说明,假设你有一个蜘蛛需要登录网站才能抓取数据,并且你只想抓取网站某个特定部分的数据(每次都不一样)。在这种情况下,登录凭据将是设置,而要抓取的部分的 URL 将是蜘蛛参数。
我正在抓取 XML 文档,我的 XPath 选择器没有返回任何项目¶
您可能需要删除命名空间。参见 删除命名空间。
如何在项目管道中将一个项目拆分为多个项目?¶
项目管道 无法为每个输入项目产生多个项目。 创建蜘蛛中间件,并使用其 process_spider_output()
方法来实现此目的。例如
from copy import deepcopy
from itemadapter import is_item, ItemAdapter
class MultiplyItemsMiddleware:
def process_spider_output(self, response, result, spider):
for item in result:
if is_item(item):
adapter = ItemAdapter(item)
for _ in range(adapter["multiply_by"]):
yield deepcopy(item)
Scrapy 是否支持 IPv6 地址?¶
是的,通过将 DNS_RESOLVER
设置为 scrapy.resolver.CachingHostnameResolver
。请注意,这样做会使您无法为 DNS 请求设置特定的超时时间(DNS_TIMEOUT
设置的值将被忽略)。
如何处理 <class 'ValueError'>: filedescriptor out of range in select()
异常?¶
这个问题 已报告 在 macOS 上运行广泛爬取时出现,其中默认的 Twisted reactor 是 twisted.internet.selectreactor.SelectReactor
。可以通过使用 TWISTED_REACTOR
设置切换到不同的 reactor。
如何取消给定响应的下载?¶
在某些情况下,停止特定响应的下载可能很有用。例如,有时您可以通过检查其标头或主体的前几个字节来确定是否需要响应的完整内容。在这种情况下,您可以通过将处理程序附加到 bytes_received
或 headers_received
信号并引发 StopDownload
异常来节省资源。有关其他信息和示例,请参阅 停止响应下载 主题。
如何发出空请求?¶
from scrapy import Request
blank_request = Request("data:,")
在这种情况下,URL 设置为数据 URI 方案。数据 URL 允许您在网页中内联包含数据,类似于外部资源。“data:” 方案加上空内容(“,”)本质上是向没有特定内容的数据 URL 发出请求。
运行 runspider
时,我收到 error: No spider found in file: <filename>
¶
如果您的 Scrapy 项目的蜘蛛模块名称与 Python 标准库模块(如 csv.py
或 os.py
)或您已安装的任何 Python 包 的名称冲突,则可能会发生这种情况。参见 问题 2680。