如何解决无法以正确的方式在Spider中使用dont_filter = true来避免一些不必要的活动
我创建了一个Spider来解析来自相同站点(由文本文件提供)登录页面的来自不同容器的链接,然后使用该链接从其内部页面获取标题。很少有链接具有蜘蛛网会相应处理的下一页按钮。
Spider会解析内容,但会陷入dont_filter=True
参数引起的无限循环。如果我不使用该参数,则Spider不会重用某些最初未能产生所需响应的链接。
我在三个地方使用了此参数dont_filter=True
。
- 在中间件的
_retry()
方法中 - 在
parse()
方法的最后一行 - 在
parse_content()
方法的最后一行
蜘蛛:
import os
import scrapy
import urllib
from bs4 import BeautifulSoup
from scrapy.crawler import CrawlerProcess
class YelpSpider(scrapy.Spider):
name = "yelpspidescript"
with open("all_urls.txt") as f:
start_urls = f.readlines()
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(url,callback=self.parse,meta={"lead_link":url})
def parse(self,response):
if response.meta.get("lead_link"):
lead_link = response.meta.get("lead_link")
elif response.meta.get("redirect_urls"):
lead_link = response.meta.get("redirect_urls")[0]
soup = BeautifulSoup(response.text,'lxml')
if soup.select("[class*='hoverable'] h4 a[href^='/biz/'][name]"):
for item in soup.select("[class*='hoverable'] h4 a[href^='/biz/'][name]"):
lead_link = response.urljoin(item.get("href"))
yield scrapy.Request(lead_link,meta={"lead_link":lead_link},callback=self.parse_content)
next_page = soup.select_one("a[class*='next-link'][href^='/search?']")
if next_page:
link = response.urljoin(next_page.get("href"))
yield scrapy.Request(link,meta={"lead_link":link},callback=self.parse)
else:
yield scrapy.Request(lead_link,dont_filter=True)
def parse_content(self,'lxml')
if soup.select_one("h1[class*='heading--inline__']"):
try:
name = soup.select_one("h1[class*='heading--inline__']").get_text(strip=True)
except AttributeError: name = ""
print(name)
else:
yield scrapy.Request(lead_link,callback=self.parse_content,dont_filter=True)
if __name__ == "__main__":
c = CrawlerProcess({
'USER_AGENT':'Mozilla/5.0','LOG_LEVEL':'ERROR',})
c.crawl(YelpSpider)
c.start()
中间件:
from fake_useragent import UserAgent
RETRY_HTTP_CODES = [500,502,503,504,408,403,401,400,404,408]
class yelp_custom_Middleware(object):
ua = UserAgent()
def process_request(self,request,spider):
request.headers['User-Agent'] = self.ua.random
def process_exception(self,exception,spider):
return self._retry(request,spider)
def _retry(self,reason,spider):
retryreq = request.copy()
retryreq.dont_filter = True
return retryreq
def process_response(self,response,spider):
if request.meta.get('dont_retry',False):
return response
if response.status in RETRY_HTTP_CODES:
reason = response_status_message(response.status)
return self._retry(request,spider) or response
return response
如何让蜘蛛不陷入无限循环?
编辑:
我认为要在文件all_urls.txt
中加入 few of the urls ,以帮助更好地识别问题。
解决方法
您可以计算每个URL的重试次数:
class yelp_custom_Middleware(object):
ua = UserAgent()
max_retries = 3
retry_urls = {}
def process_request(self,request,spider):
request.headers['User-Agent'] = self.ua.random
def process_exception(self,exception,spider):
return self._retry(request,spider)
def _retry(self,reason,spider):
retry_url = request.url
if retry_url not in self.retry_urls:
self.retry_urls[retry_url] = 1
else:
self.retry_urls[retry_url] += 1
if self.retry_urls[retry_url] > self.max_retries:
# Dont' retry
else:
# Retry
,
免责声明:此解决方案是解决上述问题的一种解决方法,当始终遇到请求中存在无限循环的问题时,建议始终找出原因而不是更改过滤器的工作方式。
-
在与您
customdupefilter.py
或middlewares.py
相同级别上创建settings.py
。import os from scrapy.dupefilters import RFPDupeFilter class CustomDupeFilter(RFPDupeFilter): default_max_retries = 5 def __init__(self,path=None,debug=False): super().__init__(path,debug) self.request_counter = {} def request_seen(self,request): max_retries = request.meta.get('max_retries') or self.default_max_retries fp = self.request_fingerprint(request) if fp in self.fingerprints: if self.request_counter.get(fp) >= max_retries: self.logger.info('Requests to %s exceeded maximum retries.',request) return True else: self.request_counter[fp] += 1 return None self.fingerprints.add(fp) self.request_counter.update({fp: 1}) if self.file: self.file.write(fp + os.linesep)
这假定您使用的是scrapy项目的标准文件夹结构,如果您更改了文件夹结构或文件名,请务必同时更改设置中的引用。
-
在您的
中添加以下行settings.py
DUPEFILTER_CLASS = 'YOUR_PROJECT_NAME.customdupefilter.CustomDupeFilter'
-
如果您在请求中使用
dont_filter=True
,它将像标准类一样绕过该过滤器。 -
您可以在请求元中发送
max_retries
参数,以更改该特定请求允许的最大重试次数,如果不这样做,它将使用default_max_retries
。以防万一某个特定的请求比其他大多数都需要更多的请求。示例:yield Request(url=request_url,meta={'max_retries': 3})
说明:
这将简单地覆盖标准过滤器类RFPDupeFilter
并实现一个计数器,以查看允许同一请求重复执行多少次,一旦超过最大次数,它将像以前一样进行过滤。
如前所述,dont_filter
将绕过此过滤方法。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。