Ci3*_*Ci3 4 twisted scrapy scrapy-spider
我有一个很大的相对 url 文件,我想用 Scrapy 抓取它,我已经编写了一些代码来逐行读取这个文件,并为我的蜘蛛构建请求进行解析。下面是一些示例代码。
蜘蛛:
def start_requests(self):
with open(self._file) as infile:
for line in infile:
inlist = line.replace("\n","").split(",")
item = MyItem(data = inlist[0])
request = scrapy.Request(
url = "http://foo.org/{0}".format(item["data"]),
callback = self.parse_some_page
)
request.meta["item"]
yield request
def parse_some_page(self,response):
...
request = scrapy.Request(
url = "http://foo.org/bar",
callback = self.parse_some_page2
)
yield request
Run Code Online (Sandbox Code Playgroud)
这工作正常,但对于一个大的输入文件,我看到parse_some_page2
在start_requests
完成所有初始请求之前不会调用它。有什么方法可以让 Scrapy 更早地开始调用回调吗?最终,我不想在开始看到项目流过管道之前等待一百万个请求。
我想出了2个解决方案。1)如果大型站点太多,请在单独的进程中运行蜘蛛。2)通过Twisted使用deferreds和callbacks (请不要跑掉,不会太吓人)。我将讨论如何使用第二种方法,因为第一种方法可以简单地用谷歌搜索。
执行的每个函数都yield request
将“阻塞”,直到结果可用。因此,您的parse_some_page()
函数会生成一个简单的响应对象,并且在返回响应之前不会继续访问下一个 URL。我确实设法找到了一些需要一段时间才能获取的网站(主要是外国政府网站),希望它能模拟您遇到的类似情况。这是一个快速简便的示例:
# spider/stackoverflow_spider.py
from twisted.internet import defer
import scrapy
class StackOverflow(scrapy.Spider):
name = 'stackoverflow'
def start_requests(self):
urls = [
'http://www.gob.cl/en/',
'http://www.thaigov.go.th/en.html',
'https://www.yahoo.com',
'https://www.stackoverflow.com',
'https://swapi.co/',
]
for index, url in enumerate(urls):
# create callback chain after a response is returned
deferred = defer.Deferred()
deferred.addCallback(self.parse_some_page)
deferred.addCallback(self.write_to_disk, url=url, filenumber=index+1)
# add callbacks and errorbacks as needed
yield scrapy.Request(
url=url,
callback=deferred.callback) # this func will start the callback chain AFTER a response is returned
def parse_some_page(self, response):
print('[1] Parsing %s' % (response.url))
return response.body # this will be passed to the next callback
def write_to_disk(self, content, url, filenumber):
print('[2] Writing %s content to disk' % (url))
filename = '%d.html' % filenumber
with open(filename, 'wb') as f:
f.write(content)
# return what you want to pass to the next callback function
# or raise an error and start Errbacks chain
Run Code Online (Sandbox Code Playgroud)
我稍微改变了一些东西,使其更易于阅读和运行。需要注意的第一件事start_requests()
是在循环Deferred
中创建对象和链接(通过addCallback()
)回调函数urls
。现在看一下callback
参数scrapy.Request
:
yield scrapy.Request(
url=url,
callback=deferred.callback)
Run Code Online (Sandbox Code Playgroud)
此代码段的作用是scrapy.Response
在请求可用后立即启动回调链。在 Twisted 中,Deferreds
只有在Deferred.callback(result)
使用值执行后才开始运行回调链。
提供响应后,该parse_some_page()
函数将Response
作为参数运行。您要做的是提取您需要的内容并将其传递给下一个回调(即write_to_disk()
我的示例)。Deferred
如有必要,您可以在循环中添加更多回调。
所以这个答案和你最初所做的不同之处在于,你以前是yield
先等待所有响应,然后再执行回调。我的方法Deferred.callback()
用作每个请求的回调,以便立即处理每个响应。
希望这会有所帮助(和/或有效)。
我不知道这是否真的适合你,因为我找不到一个太大而无法解析的网站。另外,我是 Scrapy 的新人 :D 但我有多年的 Twisted 经历。