Scrapy spider не включая все запрошенные страницы
У меня есть скриптовый сценарий для Yelp, который по большей части работает. По существу, я могу снабдить его списком страниц Yelp, и он должен вернуть все отзывы со всех страниц. Сценарий до сих пор находится ниже:
from scrapy.spider import Spider
from scrapy.selector import Selector
from scrapy.http import Request
import re
from yelp2.items import YelpReviewItem
RESTAURANTS = ['sixteen-chicago']
def createRestaurantPageLinks(self, response):
reviewsPerPage = 40
sel = Selector(response)
totalReviews = int(sel.xpath('//div[@class="rating-info clearfix"]//span[@itemprop="reviewCount"]/text()').extract()[0].strip().split(' ')[0])
pages = [Request(url=response.url + '?start=' + str(reviewsPerPage*(n+1)), callback=self.parse) for n in range(totalReviews/reviewsPerPage)]
return pages
class Yelp2aSpider(Spider):
name = "yelp2a"
allowed_domains = ["yelp.com"]
start_urls = ['http://www.yelp.com/biz/%s' % s for s in RESTAURANTS]
def parse(self, response):
requests = []
sel = Selector(response)
reviews = sel.xpath('//div[@class="review review-with-no-actions"]')
items = []
for review in reviews:
item = YelpReviewItem()
item['venueName'] = sel.xpath('//meta[@property="og:title"]/@content').extract()
item['reviewer'] = review.xpath('.//li[@class="user-name"]/a/text()').extract()
item['reviewerLoc'] = review.xpath('.//li[@class="user-location"]/b/text()').extract()
item['rating'] = review.xpath('.//meta[@itemprop="ratingValue"]/@content').extract()
item['reviewDate'] = review.xpath('.//meta[@itemprop="datePublished"]/@content').extract()
item['reviewText'] = review.xpath('.//p[@itemprop="description"]/text()').extract()
item['url'] = response.url
items.append(item)
return items
if response.url.find('?start=') == -1:
requests += createRestaurantPageLinks(self, response)
return requests
Однако проблема, с которой я сталкиваюсь, заключается в том, что этот конкретный скрипт очищает каждую страницу каждого запрошенного обзора, за исключением первой страницы. Если я комментирую последнее утверждение "если", оно только царапает первую страницу. Я подозреваю, что все, что мне нужно, - это простая команда "еще", но я ковылявший... помощь очень ценится!
EDIT: это код, как он в настоящее время стоит на основе полученной помощи...
from scrapy.spider import Spider
from scrapy.selector import Selector
from scrapy.http import Request
import re
from yelp2.items import YelpReviewItem
RESTAURANTS = ['sixteen-chicago']
def createRestaurantPageLinks(self, response):
reviewsPerPage = 40
sel = Selector(response)
totalReviews = int(sel.xpath('//div[@class="rating-info clearfix"]//span[@itemprop="reviewCount"]/text()').extract()[0].strip().split(' ')[0])
pages = [Request(url=response.url + '?start=' + str(reviewsPerPage*(n+1)), callback=self.parse) for n in range(totalReviews/reviewsPerPage)]
return pages
class Yelp2aSpider(Spider):
name = "yelp2a"
allowed_domains = ["yelp.com"]
start_urls = ['http://www.yelp.com/biz/%s' % s for s in RESTAURANTS]
def parse(self, response):
requests = []
sel = Selector(response)
reviews = sel.xpath('//div[@class="review review-with-no-actions"]')
items = []
for review in reviews:
item = YelpReviewItem()
item['venueName'] = sel.xpath('//meta[@property="og:title"]/@content').extract()
item['reviewer'] = review.xpath('.//li[@class="user-name"]/a/text()').extract()
item['reviewerLoc'] = review.xpath('.//li[@class="user-location"]/b/text()').extract()
item['rating'] = review.xpath('.//meta[@itemprop="ratingValue"]/@content').extract()
item['reviewDate'] = review.xpath('.//meta[@itemprop="datePublished"]/@content').extract()
item['reviewText'] = review.xpath('.//p[@itemprop="description"]/text()').extract()
item['url'] = response.url
yield item
if response.url.find('?start=') == -1:
requests += createRestaurantPageLinks(self, response)
for request in requests:
yield request
Как указано в комментарии ниже, запуск этого кода как есть обходит каждую нужную страницу, но возвращает только один обзор на страницу, а не все из них.
Я попытался изменить yield item
на yield items
, но сообщение об ошибке ERROR: Spider must return Request, BaseItem or None, got 'list' in <GET http://www.yelp.com/biz/[...]>
возвращается для каждого URL-адреса, по которому был выполнен обход.
3 ответа:
Вам нужно немного реорганизовать методы. Сначала разберем страницу ресторана в методе
parse()
. Затем возвращайте запросы на просмотр и обрабатывайте ответы другим способом, напримерparse_review()
:import re from scrapy.item import Item, Field from scrapy.spider import Spider from scrapy.selector import Selector from scrapy.http import Request from yelp2.items import YelpReviewItem RESTAURANTS = ['sixteen-chicago'] class Yelp2aSpider(Spider): name = "yelp2a" allowed_domains = ["yelp.com"] start_urls = ['http://www.yelp.com/biz/%s' % s for s in RESTAURANTS] def parse(self, response): reviewsPerPage = 40 sel = Selector(response) totalReviews = int(sel.xpath('//div[@class="rating-info clearfix"]//span[@itemprop="reviewCount"]/text()').extract()[0].strip().split(' ')[0]) pages = [Request(url=response.url + '?start=' + str(reviewsPerPage*(n+1)), callback=self.parse_review) for n in range(totalReviews/reviewsPerPage)] return pages def parse_review(self, response): sel = Selector(response) reviews = sel.xpath('//div[@class="review review-with-no-actions"]') for review in reviews: item = YelpReviewItem() item['venueName'] = sel.xpath('//meta[@property="og:title"]/@content').extract() item['reviewer'] = review.xpath('.//li[@class="user-name"]/a/text()').extract() item['reviewerLoc'] = review.xpath('.//li[@class="user-location"]/b/text()').extract() item['rating'] = review.xpath('.//meta[@itemprop="ratingValue"]/@content').extract() item['reviewDate'] = review.xpath('.//meta[@itemprop="datePublished"]/@content').extract() item['reviewText'] = review.xpath('.//p[@itemprop="description"]/text()').extract() item['url'] = response.url yield item
Если вы возвращаете элементы / запросы в нескольких местах, вы должны заменить свои операторы
return
На операторыyield
, которые превращают вашу функцию в генератор, который возвращает новый элемент каждый раз, когда он генерируется (выдает его), не выходя из функции, пока они все не будут возвращены. В противном случае, как и сейчас, ваша функция завершит работу после первогоreturn
и не сможет отправлять запросы на следующие страницы.Edit: коррекция-вы должны дать один пункт / Запрос одновременно, так что:
Заменить
for review in reviews: item = ... return items
С
for review in reviews: item = ... yield item
И заменить
return requests
С
for request in requests: yield request
Окончательный ответ действительно заключался в отступе одной единственной строки
yield
. Это код, который в конечном итоге сделал то, что мне было нужно.from scrapy.spider import Spider from scrapy.selector import Selector from scrapy.http import Request import re from yelp2.items import YelpReviewItem RESTAURANTS = ['sixteen-chicago'] def createRestaurantPageLinks(self, response): reviewsPerPage = 40 sel = Selector(response) totalReviews = int(sel.xpath('//div[@class="rating-info clearfix"]//span[@itemprop="reviewCount"]/text()').extract()[0].strip().split(' ')[0]) pages = [Request(url=response.url + '?start=' + str(reviewsPerPage*(n+1)), callback=self.parse) for n in range(totalReviews/reviewsPerPage)] return pages class YelpXSpider(Spider): name = "yelpx" allowed_domains = ["yelp.com"] start_urls = ['http://www.yelp.com/biz/%s' % s for s in RESTAURANTS] def parse(self, response): requests = [] sel = Selector(response) reviews = sel.xpath('//div[@class="review review-with-no-actions"]') items = [] for review in reviews: item = YelpReviewItem() item['venueName'] = sel.xpath('//meta[@property="og:title"]/@content').extract() item['reviewer'] = review.xpath('.//li[@class="user-name"]/a/text()').extract() item['reviewerLoc'] = review.xpath('.//li[@class="user-location"]/b/text()').extract() item['rating'] = review.xpath('.//meta[@itemprop="ratingValue"]/@content').extract() item['reviewDate'] = review.xpath('.//meta[@itemprop="datePublished"]/@content').extract() item['reviewText'] = review.xpath('.//p[@itemprop="description"]/text()').extract() item['url'] = response.url yield item if response.url.find('?start=') == -1: requests += createRestaurantPageLinks(self, response) for request in requests: yield request
Спасибо всем за помощь нубу!