我已经设法用Scrapy编写一个非常简单的爬虫程序,并给出了这些给定的约束条件:
它运行良好,但如果我向第一个请求添加回调,它就不会实现规则!
这是我的代码:(工作但不正确,带有实例)
from scrapy.contrib.spiders import CrawlSpider,Rule
from scrapy.selector import HtmlXPathSelector
from scrapy.http import Request
from scrapySpider.items import SPage
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
class TestSpider4(CrawlSpider):
name = "spiderSO"
allowed_domains = ["cumulodata.com"]
start_urls = ["http://www.cumulodata.com"]
extractor = SgmlLinkExtractor()
def parse_start_url(self, response):
#3
print('----------manual call of',response)
self.parse_links(response)
print('----------manual call done')
# 1 return Request(self.start_urls[0]) # does not call parse_links(example.com)
# 2 return Request(self.start_urls[0],callback = self.parse_links) # does not call parse_links(example.com)
rules = (
Rule(extractor,callback='parse_links',follow=True),
)
def parse_links(self, response):
hxs = HtmlXPathSelector(response)
print('----------- manual parsing links of',response.url)
links = hxs.select('//a')
for link in links:
title = link.select('@title')
url = link.select('@href').extract()[0]
meta={'title':title,}
yield Request(url, callback = self.parse_page,meta=meta)
def parse_page(self, response):
print('----------- parsing page: ',response.url)
hxs = HtmlXPathSelector(response)
item=SPage()
item['url'] = str(response.request.url)
item['title']=response.meta['title']
item['h1']=hxs.select('//h1/text()').extract()
yield item
我尝试过以下三种方式解决这个问题:
parse_links
- 同样的问题parse_links
,通过实施parse_start_url
,函数不会被调用以下是日志:
----------manual call of <200 http://www.cumulodata.com>)
----------manual call done
#No '----------- manual parsing links', so `parse_links` is never called!
版本
答案 0 :(得分:18)
这是一个完美运作的刮刀:
from scrapy.contrib.spiders import CrawlSpider,Rule
from scrapy.selector import HtmlXPathSelector
from scrapy.http import Request
from scrapySpider.items import SPage
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
class TestSpider4(CrawlSpider):
name = "spiderSO"
allowed_domains = ["cumulodata.com"]
start_urls = ["http://www.cumulodata.com/"]
extractor = SgmlLinkExtractor()
rules = (
Rule(extractor,callback='parse_links',follow=True),
)
def parse_start_url(self, response):
list(self.parse_links(response))
def parse_links(self, response):
hxs = HtmlXPathSelector(response)
links = hxs.select('//a')
for link in links:
title = ''.join(link.select('./@title').extract())
url = ''.join(link.select('./@href').extract())
meta={'title':title,}
cleaned_url = "%s/?1" % url if not '/' in url.partition('//')[2] else "%s?1" % url
yield Request(cleaned_url, callback = self.parse_page, meta=meta,)
def parse_page(self, response):
hxs = HtmlXPathSelector(response)
item=SPage()
item['url'] = response.url
item['title']=response.meta['title']
item['h1']=hxs.select('//h1/text()').extract()
return item
<强>更改强>:
已实施parse_start_url
- 不幸的是,当您为第一个请求指定回调时,不会执行规则。这是内置于Scrapy中的,我们只能通过解决方法来管理它。所以我们在这个函数中做list(self.parse_links(response))
。为什么list()
?因为parse_links
是一个生成器,而生成器是懒惰的。所以我们需要明确地完全调用它。
cleaned_url = "%s/?1" % url if not '/' in url.partition('//')[2] else "%s?1" % url
- 这里有几件事:
一个。我们在网址末尾添加了“/?1” - 由于parse_links
会返回重复的网址,因此Scrapy会将其过滤掉。避免这种情况的更简单方法是将dont_filter=True
传递给Request()。但是,您的所有网页都是相互关联的(返回到来自pageAA的索引等),而dont_filter
会产生过多的重复请求。项目
湾if not '/' in url.partition('//')[2]
- 再次,这是因为您网站中的链接。其中一个内部链接是“www.cumulodata.com”,另一个是“www.cumulodata.com/”。由于我们明确添加了允许重复的机制,因此产生了一个额外的项目。由于我们需要完美,我实施了这个黑客。
title = ''.join(link.select('./@title').extract())
- 您不想返回节点,而是返回数据。另外:''。join(list)在列表为空的情况下优于list [0]。
恭喜您创建一个测试网站,该网站提出了一个奇怪的问题 - 重复项既是必要的,也是不需要的!