在 scrapy 中使用自定义方法时无法找出正确的方法

Unable to figure out the right way while using a customized method within scrapy

我正在尝试结合使用 scrapy 和 selenium 从网站的内页中抓取不同帖子的标题,尽管该网站的内容是静态的。该脚本从着陆页抓取不同帖子的链接,并重新使用新解析的链接从其内页中获取标题。

我知道有一个 library 用于在 scrapy 中使用 selenium。但是,我对该基本用例使用该库不感兴趣。

下面的蜘蛛中有两种方法。我可以坚持使用一种方法来完成整个事情,但我在这里使用了两种方法来了解如何将链接从一种方法传递到另一种方法,以便在不发出任何请求的情况下在后一种方法中完成其余的事情,如 scrapy.Request().

脚本似乎运行正常。如果我从这里 yield self.parse_from_innerpage(response.urljoin(elem)) 踢出 yield 并像 self.parse_from_innerpage(response.urljoin(elem)).

这样使用,脚本也能正常工作

问题:我是否应该在 parse 方法中使用 yield 来继续当前的实现,因为它们可以互换工作?

我试过:

import scrapy
from selenium import webdriver
from scrapy.crawler import CrawlerProcess
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


class WhosebugSpider(scrapy.Spider):
    name = "Whosebug"

    start_urls = ['https://whosebug.com/questions/tagged/web-scraping']

    def __init__(self):
        self.driver = webdriver.Chrome()
        self.wait = WebDriverWait(self.driver,15)

    def parse(self, response):
        for elem in response.css(".summary .question-hyperlink::attr(href)").getall():
            yield self.parse_from_innerpage(response.urljoin(elem))

    def parse_from_innerpage(self,link):
        self.driver.get(link)
        elem = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "h1[itemprop='name'] > a")))
        print(elem.text)


if __name__ == "__main__":
    c = CrawlerProcess({
        'USER_AGENT':'Mozilla/5.0',
        'LOG_LEVEL':'ERROR',
    })
    c.crawl(WhosebugSpider)
    c.start()
python 中的

yield 用于在生成器内部“放弃” 运行 和 return 一个值。

所以这里的 parse 方法 return 是 parse_from_innerpage 的结果。但是,parse_from_innerpage 没有 return 语句,这意味着它 returns None.

从 scrapy 文档中阅读 this 关于 scrapy 期望蜘蛛做什么的内容。

简而言之,scrapy 使用 parse 方法作为结果的生成器,然后在它停止时向您显示它们 运行(用完了要抓取的链接)。 将 print 替换为 returns,一切都应该按预期工作

def parse_from_innerpage(self,link):
        self.driver.get(link)
        elem = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "h1[itemprop='name'] > a")))
        print(elem.text)

在尝试回答您的问题之前,我想对您在 parse_from_innerpage(self, link) 函数中的 print() 语句添加一点注释。虽然这个话题是controversially discussed it's a better practice to return the object in question - you can later decide to print it by (然后调用方法)或者直接Class.Method(**args)

不碍事 - 让我们来解决您的问题:

Should I use yield or not within parse method to go on with current implementation as they both work interchangeably?

要回答这个问题——或者更确切地说是理解回复——我强烈建议查看一些定性资源以掌握 yield 的概念。以下是一些用于进一步阅读目的的链接:

基本上,return 发回指定的值,另一方面,yield 生成一个 值序列 - 因此非常适合迭代值你得到了。


让我们看看你的代码:

parse(self, response) method:

def parse(self, response):
        for elem in response.css(".summary .question-hyperlink::attr(href)").getall():
            yield self.parse_from_innerpage(response.urljoin(elem))

简单地说,此方法需要来自 parse_from_innerpage(self, link) 的一些值,因为它“yields”或遍历接收列表。 但是,你的parse_from_innerpage(self,link)没有return任何东西-看看,有没有 return声明!:

def parse_from_innerpage(self,link):
        self.driver.get(link)
        elem = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "h1[itemprop='name'] > a")))
        
        #Use return instead of print()
        print(elem.text)

As parse_from_innerpage(**args) returns None, parse(**args) 也不会 return 任何东西,因为没有什么可以迭代 over/return .因此,您应该将 print() 替换为 return.


我建议查看 Scrapy Documentation (especially the Scrapy at a glance) to understand how Scrapy exactly works and what it expects a scrapy.Spider to do in order to achieve your goals. Basically the parse(**args) method is used as a generator (referring to the already mentioned Whosebug question) 以获得您想要获得的结果 - 一旦没有元素可以迭代(它停止)它 “向您展示它们”:

In the parse callback, we loop through the quote elements using a CSS Selector, yield a Python dict with the extracted quote text and author, look for a link to the next page and schedule another request using the same parse method as callback

...但如您所述,parse(**args) 不幸收到 None 由于 print() 而不是 return