Skip to main content
 首页 » 编程设计

python之如何避免爬虫中的重复

2025年05月04日11telwanggs

我使用 python 中的 scrapy 框架编写了一个爬虫来选择一些链接和元标记。然后爬取起始 url 并将数据以 JSON 编码格式写入文件。问题是当爬虫运行两个或者使用相同的起始 url 三次,文件中的数据会重复。为了避免这种情况,我在 scrapy 中使用了一个下载器中间件,它是这样的:http://snippets.scrapy.org/snippets/1/

我所做的是将上面的代码复制并粘贴到我的 scrapy 项目中的一个文件中,并通过添加以下行在 settings.py 文件中启用它:

SPIDER_MIDDLEWARES = {'a11ypi.removeDuplicates.IgnoreVisitedItems':560}  

其中“a11ypi.removeDuplicates.IgnoreVisitedItems”是类路径名称,最后我进入并修改了我的 items.py 文件并包含以下字段

visit_id = Field()   
visit_status = Field() 

但这行不通,爬虫在运行两次时仍然产生相同的结果并将其附加到文件

我在我的 pipelines.py 文件中写入文件如下:

import json  
 
class AYpiPipeline(object):   
    def __init__(self):   
    self.file = open("a11ypi_dict.json","ab+") 
 
 
   # this method is called to process an item after it has been scraped. 
 
 
    def process_item(self, item, spider): 
    d = {}   
 
    i = 0 
    # Here we are iterating over the scraped items and creating a dictionary of dictionaries. 
    try: 
        while i<len(item["foruri"]): 
        d.setdefault(item["foruri"][i],{}).setdefault(item["rec"][i],{})[item["foruri_id"][i]] = item['thisurl'] + ":" +item["thisid"][i] 
        i+=1 
    except IndexError: 
        print "Index out of range" 
 
    json.dump(d,self.file) 
        return item 

而我的爬虫代码如下:

from scrapy.contrib.spiders import CrawlSpider, Rule 
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor 
from scrapy.selector import HtmlXPathSelector 
from a11ypi.items import AYpiItem 
 
class AYpiSpider(CrawlSpider): 
    name = "a11y.in" 
    allowed_domains = ["a11y.in"] 
 
    # This is the list of seed URLs to begin crawling with. 
    start_urls = ["http://www.a11y.in/a11ypi/idea/fire-hi.html"] 
 
    # This is the callback method, which is used for scraping specific data 
    def parse(self,response): 
    temp = [] 
    hxs = HtmlXPathSelector(response) 
    item = AYpiItem() 
    wholeforuri = hxs.select("//@foruri").extract()            # XPath to extract the foruri, which contains both the URL and id in foruri 
    for i in wholeforuri: 
        temp.append(i.rpartition(":")) 
 
    item["foruri"] = [i[0] for i in temp]     # This contains the URL in foruri 
    item["foruri_id"] = [i.split(":")[-1] for i in wholeforuri]  # This contains the id in foruri 
    item['thisurl'] = response.url                                   
    item["thisid"] = hxs.select("//@foruri/../@id").extract() 
    item["rec"] = hxs.select("//@foruri/../@rec").extract() 
    return item   

请提出建议。

请您参考如下方法:

试着理解为什么要这样写代码段:

 if isinstance(x, Request): 
            if self.FILTER_VISITED in x.meta: 
                visit_id = self._visited_id(x) 
                if visit_id in visited_ids: 
                    log.msg("Ignoring already visited: %s" % x.url, 
                            level=log.INFO, spider=spider) 
                    visited = True 

请注意第 2 行,您实际上需要在 Request.meta 中输入一个名为 FILTER_VISITED 的键,以便中间件删除请求。原因是善意的,因为您访问过的每个 url 都将被跳过,如果您不这样做,您将根本没有要遍历的 url。因此,FILTER_VISITED 实际上允许您选择要跳过的 url 模式。如果您想跳过使用特定规则提取的链接,只需执行

Rule(SgmlLinkExtractor(allow=('url_regex1', 'url_regex2' )),  callback='my_callback', process_request = setVisitFilter) 
 
def setVisitFilter(request): 
   request.meta['filter_visited'] = True 
   return request 

P.S 我不知道它是否适用于 0.14 及更高版本,因为一些代码已更改以将蜘蛛上下文存储在 sqlite 数据库中。