Я пытаюсь очистить заголовки разных сообщений с их внутренних страниц с веб-сайта, используя scrapy в сочетании с селеном, хотя содержимое этого сайта статично. Сценарий захватывает ссылки различных сообщений с целевой страницы и повторно использует недавно проанализированные ссылки для извлечения заголовков с их внутренних страниц.
Я знаю, что есть библиотека, которая предназначена для использования селена в скрэпи. Однако я не заинтересован в использовании этой библиотеки для этого базового варианта использования.
В следующем пауке есть два метода. Я мог бы придерживаться одного метода, чтобы сделать все это, но здесь я использовал два метода, чтобы понять, как я могу передавать ссылки из одного метода в другой, чтобы делать остальные вещи в последнем методе без каких-либо запросов, как в scrapy.Request()
.
Скрипт вроде работает корректно. Скрипт тоже работает корректно, если выкинуть yield
отсюда yield self.parse_from_innerpage(response.urljoin(elem))
и использовать лайк self.parse_from_innerpage(response.urljoin(elem))
.
Вопрос: следует ли использовать yield или not в методе parse для продолжения текущей реализации, поскольку они оба работают взаимозаменяемо?
Я пробовал с:
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 StackoverflowSpider(scrapy.Spider):
name = "stackoverflow"
start_urls = ['https://stackoverflow.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(StackoverflowSpider)
c.start()
yield
в python используется внутри генератора, чтобы «отказаться» от запуска и вернуть значение.
поэтому здесь метод parse
возвращает результат parse_from_innerpage
. Однако у parse_from_innerpage
нет оператора return, а это значит, что он возвращает None
.
Прочтите это из документации по scrapy о том, что scrapy ожидает от паука.
Короче говоря, scrapy использует метод parse
в качестве генератора результатов, а затем показывает их вам, когда он перестает работать (кончаются ссылки для очистки).
Замените print
на возврат, и все должно работать как положено.
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)
Прежде чем попытаться ответить на ваш вопрос, я хочу добавить небольшое примечание о вашем утверждении print()
внутри функции parse_from_innerpage(self, link)
. Хотя эта тема вызывает споры , лучше возвращать рассматриваемый объект - позже вы можете решить напечатать его, назначив экземпляр класса (и затем вызвав метод) или просто напрямую Class.Method(**args)
Это из пути - давайте решим ваш вопрос:
Должен ли я использовать yield или нет в методе parse, чтобы продолжить текущую реализацию, поскольку они оба работают взаимозаменяемо?
Чтобы ответить на этот вопрос или, вернее, понять ответ, я настоятельно рекомендую обратиться к некоторым качественным ресурсам, чтобы понять концепцию 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)
, поскольку он «выдает» или выполняет итерацию по списку получения. Однако ваш parse_from_innerpage(self,link)
ничего не возвращает — посмотрите, нет 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)
Поскольку parse_from_innerpage(**args)
возвращает None
, parse(**args)
также ничего не вернет, так как нечего перебирать/возвращать. Следовательно, вы должны заменить print()
на return
.
Я предлагаю просмотреть Документацию по Scrapy (особенно Scrapy с первого взгляда ), чтобы понять, как именно работает Scrapy и что он ожидает от scrapy.Spider
для достижения ваших целей. В основном метод parse(**args)
используется в качестве генератора (ссылаясь на уже упомянутый вопрос StackOverflow) для результатов, которые вы хотите получить - когда нет элементов для итерации (он останавливается), он «показывает их вам»:
В обратном вызове синтаксического анализа мы перебираем элементы цитаты с помощью CSS. Selector, создайте словарь Python с извлеченным текстом цитаты и автор, найдите ссылку на следующую страницу и запланируйте другой запрос используя тот же метод синтаксического анализа, что и обратный вызов
... но, как уже упоминалось в вашем случае, parse(**args)
, к сожалению, получает None
из-за print()
вместо return
.
Ваш метод
parse_from_innerpage
ничего не возвращает (то есть возвращает None). Вероятно, это приводит к тому, что генератор возвращает много объектов None. Вместо использования печати верните elem.text