在Scrapy中递归折叠二级链接

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Scrapy中递归折叠二级链接相关的知识,希望对你有一定的参考价值。

使用Scrapy,我试图从所有语言中删除维基百科的链接网络。每个维基百科页面都应包含指向维基数据项目的链接,该项目可以唯一标识所有语言的页面主题。我试图实现的过程如下所示:

  1. 首先,从每个页面(“源”链接)中提取维基数据链接。
  2. 迭代页面上的其余链接。
  3. 对于每个链接,使用新的回调函数向相应的页面(“目标”链接)发送请求。
  4. 从相应的目标页面中提取Wikidata链接。
  5. 迭代目标页面上的所有链接并回调原始的parse函数。

基本上,我想跳过给定源页面上的中间链接,而是抓住相应的维基数据链接。

这是我到目前为止的(半工作)代码:

from urllib.parse import urljoin, urlparse
from scrapy import Spider    
from wiki_network.items import WikiNetworkItem

WD = 
    "//a/@href[contains(., 'wikidata.org/wiki/Special:EntityPage') 
    and not(contains(., '#'))][1]"

TARGETS = 
    "//a/@href[contains(., '/wiki/') 
    and not(contains(., 'wikidata')) 
    and not(contains(., 'wikimedia'))]"

class WikiNetworkSpider(Spider):
    name = "wiki_network"
    allowed_domains = ["wikipedia.org"]    
    start_urls = ["https://gl.wikipedia.org/wiki/Jacques_Derrida"]
    filter = re.compile(r"^.*(?!.*:[^_]).*wiki.*")               

    def parse(self, response):        

        # Extract the Wikidata link from the "source" page
        source = response.xpath(WD).extract_first()       

        # Extract the set of links from the "source" page                         
        targets = response.xpath(TARGETS).extract()                
        if source:                
            source_title = response.xpath("//h1/text()").extract_first()                                      
            for target in targets:   
                if self.filter.match(str(target)) is not None:
                    item = WikiNetworkItem()        
                    item["source"] = source
                    item["source_domain"] = urlparse(response.url).netloc
                    item["refer"] = response.url
                    item["source_title"] = source_title  

                    # Yield a request to the target page
                    yield Request(url=urljoin(response.url, str(target)), 
                                  callback=self.parse_wikidata, 
                                  meta={"item": item})

    def parse_wikidata(self, response):
        item = WikiNetworkItem(response.meta["item"])
        wikidata_target = response.xpath(WD).extract_first()
        if wikidata_target:      

            # Return current item
            yield self.item_helper(item, wikidata_target, response)

            # Harvest next set of links            
            for s in response.xpath(TARGETS).extract():
                if self.filter.match(str(s)) is not None:
                    yield Request(url=urljoin(response.url, str(s)), 
                                  callback=self.parse, meta={"item": item})

    def item_helper(self, item, wikidata, response):
        print()
        print("Target: ", wikidata)        
        print()
        if item["source"] != wikidata:                
            target_title = response.xpath("//h1/text()").extract_first()                            
            item["target"] = wikidata
            item["target_title"] = target_title
            item["target_domain"] = urlparse(response.url).netloc
            item["target_wiki"] = response.url                                    
            print()
            print("Target: ", target_title)            
            print()
            return item 

蜘蛛运行并刮擦链接一段时间(刮取的项目数通常达到620左右),但最终它会建立一个庞大的队列,完全停止刮擦,然后继续爬行。我是否应该期待它在某个时刻再次开始刮擦?

似乎应该有一种简单的方法在Scrapy中进行这种二级抓取,但到目前为止我读过的其他问题似乎主要是关于如何在Scrapy中处理分页,而不是如何“折叠” “以这种方式的链接。

答案

只要蜘蛛没有问题,你真正想要的就是当你跑步时

yield Request(url=urljoin(response.url, str(target)), 
                                  callback=self.parse_wikidata, 
                                  meta={"item": item})

它应该yield早于下面类型的排队请求

yield Request(url=urljoin(response.url, str(s)), 
                                  callback=self.parse, meta={"item": item})

如果你看一下文档

https://doc.scrapy.org/en/latest/topics/request-response.html

priority(int) - 此请求的优先级(默认为0)。调度程序使用优先级来定义用于处理请求的顺序。具有更高优先级值的请求将更早执行。允许使用负值以指示相对较低的优先级。

所以你会用

yield Request(url=urljoin(response.url, str(target)), 
                                  callback=self.parse_wikidata, 
                                  meta={"item": item}, priority=1)

yield Request(url=urljoin(response.url, str(s)), 
                  callback=self.parse, meta={"item": item}, priority=-1)

这将确保刮刀优​​先考虑将导致数据首先被刮除的链接

以上是关于在Scrapy中递归折叠二级链接的主要内容,如果未能解决你的问题,请参考以下文章

片段项目不会折叠

scrapy按顺序启动多个爬虫代码片段(python3)

Scrapy学习第四课

scrapy初试水 day03(递归调用)

可折叠工具栏 - 使片段页脚在 Android 中始终可见

vue 二级列表折叠面板