【发布时间】:2022-01-20 21:38:04
【问题描述】:
我有两个从父蜘蛛类继承的蜘蛛,如下所示:
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy.crawler import CrawlerProcess
class SpiderOpTest(CrawlSpider):
custom_settings = {
"USER_AGENT": "*",
"LOG_LEVEL": "WARNING",
"DOWNLOADER_MIDDLEWARES": {'scraper_scrapy.odds.middlewares.SeleniumMiddleware': 543},
}
httperror_allowed_codes = [301]
def parse_tournament(self, response):
print(f"Parsing tournament - {response.url}")
def parse_tournament_page(self, response):
print(f"Parsing tournament page - {response.url}")
class SpiderOpTest1(SpiderOpTest):
name = "test_1"
start_urls = ["https://www.oddsportal.com/tennis/argentina/atp-buenos-aires/results/"]
rules = (Rule(LinkExtractor(allow="/page/"), callback="parse_tournament_page"),)
class SpiderOpTest2(SpiderOpTest):
name = "test_2"
start_urls = ["https://www.oddsportal.com/tennis/results/"]
rules = (
Rule(LinkExtractor(allow="/atp-buenos-aires/results/"), callback="parse_tournament", follow=True),
Rule(LinkExtractor(allow="/page/"), callback="parse_tournament_page"),
)
process = CrawlerProcess()
process.crawl(<spider_class>)
process.start()
第一个蜘蛛中Rule 的parse_tournament_page 回调工作正常。
但是,第二个蜘蛛只运行第一个 Rule 的 parse_tournament 回调,尽管第二个 Rule 与第一个蜘蛛相同并且在同一页面上运行。
我显然错过了一些非常简单的东西,但对于我的生活,我无法弄清楚它是什么......
由于页面的关键部分通过 Javascript 加载,因此包含我正在使用的 Selenium 中间件可能对我有用:
from scrapy import signals
from scrapy.http import HtmlResponse
from selenium import webdriver
class SeleniumMiddleware:
@classmethod
def from_crawler(cls, crawler):
middleware = cls()
crawler.signals.connect(middleware.spider_opened, signals.spider_opened)
crawler.signals.connect(middleware.spider_closed, signals.spider_closed)
return middleware
def process_request(self, request, spider):
self.driver.get(request.url)
return HtmlResponse(
self.driver.current_url,
body=self.driver.page_source,
encoding='utf-8',
request=request,
)
def spider_opened(self, spider):
options = webdriver.FirefoxOptions()
options.add_argument("--headless")
self.driver = webdriver.Firefox(options=options)
def spider_closed(self, spider):
self.driver.close()
编辑:
所以我设法创建了第三个蜘蛛,它能够从 parse_tournament 内部执行 parse_tournament_page 回调:
class SpiderOpTest3(SpiderOpTest):
name = "test_3"
start_urls = ["https://www.oddsportal.com/tennis/results/"]
httperror_allowed_codes = [301]
rules = (
Rule(
LinkExtractor(allow="/atp-buenos-aires/results/"),
callback="parse_tournament",
follow=True,
),
)
def parse_tournament(self, response):
print(f"Parsing tournament - {response.url}")
xtr = LinkExtractor(allow="/page/")
links = xtr.extract_links(response)
for p in links:
yield response.follow(p.url, dont_filter=True, callback=self.parse_tournament_page)
def parse_tournament_page(self, response):
print(f"Parsing tournament PAGE - {response.url}")
这里的关键似乎是dont_filter=True - 如果将其保留为默认的False,则不会执行parse_tournament_page 回调。这表明 Scrapy 以某种方式将第二页解释为副本,据我所知并非如此。除此之外,如果我想解决这个问题,我需要将unique=False 添加到LinkExtractor。但是,这样做不会导致parse_tournament_page 回调执行:(
更新:
所以我想我已经找到了问题的根源。据我所知,RFPDupeFilter 的request_fingerprint 方法为https://www.oddsportal.com/tennis/argentina/atp-buenos-aires/results/ 创建与https://www.oddsportal.com/tennis/argentina/atp-buenos-aires/results/#/page/2/ 相同的哈希。
通过阅读,我需要继承 RFPDupeFilter 以重新配置 request_fingerprint 的工作方式。任何关于为什么生成相同哈希的建议和/或如何正确进行子类的提示将不胜感激!
【问题讨论】:
标签: python web-scraping scrapy